Kubernetes
Deploy Team Server with its OCI Helm chart. Use the chart-managed PostgreSQL option only for evaluation; use a managed or otherwise production-operated PostgreSQL service for production.
Prerequisites
- Kubernetes 1.25 or later
- Helm 3.12 or later
kubectlaccess that can create namespaces, Deployments, Services, ConfigMaps, Secrets, and RBAC resources- A default StorageClass for the evaluation database
- A Team Server license
- An OIDC application and public HTTPS hostname for production
The examples use Team Server image 1.0.3 and Helm chart 0.1.0. Replace both only after reviewing the release you intend to deploy.
Prepare the Namespace and Configuration
kubectl create namespace endura-team-serverSave the production configuration example as production.yaml. Add the discovery jobs and OIDC providers you use.
For an evaluation deployment with built-in authentication, remove settings.oidc so the file does not reference unset OIDC variables.
Create the ConfigMap:
kubectl create configmap endura-team-server-config \
--namespace endura-team-server \
--from-file=production.yamlIf a private integration endpoint uses an internal CA, include the CA in the same ConfigMap and reference it from settings.http.ca_certificates:
kubectl create configmap endura-team-server-config \
--namespace endura-team-server \
--from-file=production.yaml \
--from-file=corporate-ca.pem=/path/to/corporate-ca.pemMethod 1: Evaluation Deployment
Generate two different JWT secrets:
openssl rand -base64 48
openssl rand -base64 48Create the application Secret. Use a test-only built-in username and password:
kubectl create secret generic endura-team-server-secrets \
--namespace endura-team-server \
--from-literal=ENDURA_LICENSE_KEY='<license_key>' \
--from-literal=JWT_SENSOR_SECRET='<first_base64_secret>' \
--from-literal=JWT_USER_SECRET='<second_base64_secret>' \
--from-literal=SERVER_URL='http://localhost:5150' \
--from-literal=TENANT_NAME='Evaluation' \
--from-literal=ENDURA_AUTH_USERNAME='developer' \
--from-literal=ENDURA_AUTH_PASSWORD='<test_password>'Create values-evaluation.yaml:
image:
tag: "1.0.3"
service:
type: ClusterIP
postgresql:
enabled: true
auth:
postgresPassword: "<database_admin_password>"
username: endura
password: "<database_application_password>"
database: endura
primary:
persistence:
enabled: true
size: 8GiProtect this file because it contains database credentials, then install:
chmod 600 values-evaluation.yaml
helm install endura-team-server \
oci://ghcr.io/endurasecurity/helm/endura-team-server \
--version 0.1.0 \
--namespace endura-team-server \
--values values-evaluation.yamlAccess the service through a local port forward:
kubectl port-forward \
--namespace endura-team-server \
service/endura-team-server 5150:5150Open http://localhost:5150 and sign in with the evaluation account.
Method 2: Production Deployment
Prepare a PostgreSQL database and a role that can run migrations and read and write Team Server data. Put its URI and the OIDC credentials in the application Secret:
kubectl create secret generic endura-team-server-secrets \
--namespace endura-team-server \
--from-literal=DATABASE_URL='postgresql://endura:<password>@postgres.example.com:5432/endura' \
--from-literal=ENDURA_LICENSE_KEY='<license_key>' \
--from-literal=JWT_SENSOR_SECRET='<first_base64_secret>' \
--from-literal=JWT_USER_SECRET='<second_base64_secret>' \
--from-literal=SERVER_URL='https://team-server.example.com' \
--from-literal=TENANT_NAME='Example Organization' \
--from-literal=GOOGLE_OIDC_CLIENT_ID='<client_id>' \
--from-literal=GOOGLE_OIDC_CLIENT_SECRET='<client_secret>'Create a TLS Secret using your issued certificate:
kubectl create secret tls endura-team-server-tls \
--namespace endura-team-server \
--cert=/path/to/tls.crt \
--key=/path/to/tls.keyCreate values-production.yaml:
replicaCount: 2
image:
tag: "1.0.3"
service:
type: ClusterIP
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_cookie_flags ~ secure httponly samesite=lax;
hosts:
- host: team-server.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: endura-team-server-tls
hosts:
- team-server.example.com
resources:
limits:
memory: 4Gi
requests:
cpu: 500m
memory: 2Gi
postgresql:
enabled: falseThe ingress terminates TLS, so production.yaml should omit settings.tls. Keep SERVER_URL on HTTPS. The ingress annotation shown above sets secure session-cookie attributes; use the equivalent for a different ingress controller.
Install the chart:
helm install endura-team-server \
oci://ghcr.io/endurasecurity/helm/endura-team-server \
--version 0.1.0 \
--namespace endura-team-server \
--values values-production.yamlVerify the Deployment
helm status endura-team-server --namespace endura-team-server
kubectl rollout status deployment/endura-team-server \
--namespace endura-team-server
kubectl get pods,service,ingress --namespace endura-team-server
kubectl logs deployment/endura-team-server \
--namespace endura-team-serverVerify readiness through the public endpoint:
curl -f https://team-server.example.com/_readinessThe response is HTTP 200 with {"ok":true}.
Set Up the First Administrator
The OIDC user must sign in once before its database record exists. Find its ID:
kubectl exec deployment/endura-team-server \
--namespace endura-team-server -- \
endura task user_get_id email:admin@example.comAssign the role:
kubectl exec deployment/endura-team-server \
--namespace endura-team-server -- \
endura task user_set_role id:42 role:administratorRefresh the browser or sign in again.
Update Team Server
Review Runtime Sensor compatibility before a major update. Pin the new chart and image versions explicitly:
CHART_VERSION=0.1.0
IMAGE_VERSION=1.0.3
helm upgrade endura-team-server \
oci://ghcr.io/endurasecurity/helm/endura-team-server \
--version "$CHART_VERSION" \
--namespace endura-team-server \
--values values-production.yaml \
--set image.tag="$IMAGE_VERSION"
kubectl rollout status deployment/endura-team-server \
--namespace endura-team-serverThe chart uses maxSurge: 0 and maxUnavailable: 1. With one replica, an update creates a short service gap. With two or more healthy replicas, pods are replaced one at a time.
Update Configuration
kubectl create configmap endura-team-server-config \
--namespace endura-team-server \
--from-file=production.yaml \
--dry-run=client -o yaml | kubectl apply -f -
kubectl rollout restart deployment/endura-team-server \
--namespace endura-team-serverUpdate Secrets
Recreate or patch endura-team-server-secrets, then restart the Deployment. Rotating JWT secrets invalidates tokens signed with the old values.
Backup and Restore
For production, use the external database provider’s backup, recovery, and retention controls. Also back up production.yaml, values-production.yaml, and the names and sources of Kubernetes Secrets.
For the evaluation database:
kubectl exec deployment/endura-team-server-postgresql \
--namespace endura-team-server -- \
sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > team_server.sqlRestore only into an empty or intentionally replaced evaluation database:
kubectl exec -i deployment/endura-team-server-postgresql \
--namespace endura-team-server -- \
sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"' < team_server.sqlMonitoring
# Current logs
kubectl logs deployment/endura-team-server \
--namespace endura-team-server --follow
# Previous container instance
kubectl logs deployment/endura-team-server \
--namespace endura-team-server --previous
# Pod events and readiness
kubectl describe pods \
--namespace endura-team-server \
--selector app.kubernetes.io/instance=endura-team-serverThe chart annotates pods for Prometheus scraping on port 5150. Confirm the annotations match your Prometheus discovery configuration.
Uninstall
Back up Team Server before uninstalling, especially when using the chart-managed database.
helm uninstall endura-team-server --namespace endura-team-serverDelete configuration and application Secrets only after confirming that you no longer need the deployment:
kubectl delete configmap endura-team-server-config \
--namespace endura-team-server
kubectl delete secret endura-team-server-secrets \
--namespace endura-team-serverNamespace Deletion Removes All Namespaced Data
Deleting the namespace removes every remaining Team Server resource in it, including database claims. Confirm the backup and namespace name before running kubectl delete namespace endura-team-server.
Troubleshooting
Pod Is Not Ready
kubectl describe pods \
--namespace endura-team-server \
--selector app.kubernetes.io/instance=endura-team-server
kubectl logs deployment/endura-team-server \
--namespace endura-team-serverCheck configuration parsing, required Secrets, JWT validation, license validation, and database connectivity.
Database Is Unreachable
Run a temporary PostgreSQL client using the same network path and credentials as Team Server, or use the database provider’s connectivity test. Confirm NetworkPolicies, DNS, TLS, and port 5432.
Ingress Opens but Login Fails
Confirm that SERVER_URL, server.host, CORS, settings.tenant.base_url, the ingress hostname, and the OIDC callback all use the same public HTTPS origin. The callback must include /api/authentication/{provider}/callback.
Internal CI/CD Integration Is Blocked
Add only the required target CIDRs to settings.http.allowed_networks. Mount internal CA bundles in the configuration ConfigMap and list them under settings.http.ca_certificates.
Rollout Stalls
kubectl rollout status deployment/endura-team-server \
--namespace endura-team-server
kubectl get events --namespace endura-team-server \
--sort-by=.lastTimestampCheck image availability, scheduling resources, configuration, Secrets, and readiness errors.