Kubernetes
Deploy Team Server with its OCI Helm chart. The chart creates one Team Server Deployment and one Service, which defaults to ClusterIP. Operate PostgreSQL, application configuration, and credentials separately from the Helm release.
Prerequisites
- Kubernetes 1.25 or later
- Helm 3.12 or later and configured
kubectl curlandjqfor resolving the selected release channel- Cluster permissions to create a namespace, Deployment, Service, optional Ingress, ConfigMap, and Secret
- A reachable PostgreSQL database and a role that can run migrations and read and write Team Server data
- A Team Server license
- An OIDC application and public HTTPS hostname for production
The examples select the testing channel and resolve its current Team Server chart version from the channel manifest. See Release Channels before selecting another channel.
Prepare the Namespace and Configuration
Resolve the chart version and create the namespace:
CHANNEL=testing
CHART_VERSION=$(curl -sSf "https://repo.endurasecurity.com/${CHANNEL}/release.json" \
| jq -er '.products.teamServer.chart.version') || exit $?
kubectl create namespace endura-team-serverSave the production configuration example as production.yaml. Keep the required maintenance jobs and add the discovery jobs and OIDC providers you use.
Create the ConfigMap:
kubectl create configmap endura-team-server-config \
--namespace endura-team-server \
--from-file=production.yaml \
--dry-run=client -o yaml | kubectl apply -f - || exit $?If a private identity provider or integration endpoint uses an internal CA, use this complete ConfigMap command instead. Include its PEM bundle in the same ConfigMap and reference config/<file-name> 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=/secure/path/corporate-ca.pem \
--dry-run=client -o yaml | kubectl apply -f - || exit $?Create the Application Secret
Create a protected environment file containing:
DATABASE_URLfor the independently operated PostgreSQL serviceENDURA_CREDENTIAL_ENCRYPTION_KEYENDURA_LICENSE_KEY- Distinct
JWT_SENSOR_SECRETandJWT_USER_SECRETvalues SERVER_URLandTENANT_NAME- Every OIDC or built-in authentication credential referenced by
production.yaml
install -m 0600 /dev/null team-server.env
${EDITOR:-vi} team-server.env
kubectl create secret generic endura-team-server-secrets \
--namespace endura-team-server \
--from-env-file=team-server.envGenerate ENDURA_CREDENTIAL_ENCRYPTION_KEY with openssl rand -base64 32; it must decode to exactly 32 bytes. Generate each JWT secret with openssl rand -base64 48; each decoded JWT value must contain at least 32 bytes, and the JWT values must differ. Keep credential values out of Helm values and shell history. The chart reads this Secret but does not create, update, or delete it.
For a non-production evaluation, the Secret may contain ENDURA_AUTH_USERNAME and ENDURA_AUTH_PASSWORD instead of OIDC credentials. The database still remains outside the Helm release.
Configure Helm Values
The default Service is ClusterIP. For production HTTPS ingress, install an Ingress controller and find its class with kubectl get ingressclass. Create values-production.yaml, replacing your-ingress-class with that installed class:
ingress:
enabled: true
className: your-ingress-class
hosts:
- host: team-server.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: endura-team-server-tls
hosts:
- team-server.example.comCreate the referenced TLS Secret before installation. The ingress terminates TLS, so production.yaml should omit settings.tls. Keep SERVER_URL on HTTPS. Team Server derives cookie transport security from the HTTPS settings.tenant.base_url; the ingress does not need to rewrite cookie attributes.
The schema rejects unknown values. One replica runs web, workers, and scheduler without leader election; no-surge upgrades stop it before replacement, causing a short interruption.
An empty image.tag uses the chart’s immutable appVersion; image.digest selects image.repository@image.digest and takes precedence over the tag. Set priorityClassName to an existing PriorityClass when Team Server must receive higher scheduling priority.
Install
helm install endura-team-server \
"oci://ghcr.io/endurasecurity/helm/${CHANNEL}/endura-team-server" \
--version "$CHART_VERSION" \
--namespace endura-team-server \
--values values-production.yaml || exit $?The chart creates no ServiceAccount or RBAC resources and disables ServiceAccount token mounting because Team Server does not call the Kubernetes API.
Verify the Deployment
kubectl rollout status deployment/endura-team-server \
--namespace endura-team-server --timeout=300s || exit $?For a local check, start port-forwarding in one terminal and leave it running:
kubectl port-forward \
--namespace endura-team-server \
service/endura-team-server 5150:5150In a second terminal:
curl -f http://127.0.0.1:5150/_readinessExpect HTTP 200 and {"ok":true}; for ingress, check public HTTPS. /_readiness removes database-unready Pods from traffic. Startup/liveness use process-only /_health, avoiding restarts solely for database outages.
Inspect logs and events when rollout or readiness fails:
kubectl logs deployment/endura-team-server \
--namespace endura-team-server
kubectl get events \
--namespace endura-team-server \
--sort-by=.lastTimestampSet Up the First Administrator
Sign in to create the unscoped Viewer record. Find it by OIDC email or built-in <ENDURA_AUTH_USERNAME>@localhost (for example, developer@localhost), then promote its ID:
kubectl exec deployment/endura-team-server \
--namespace endura-team-server -- \
endura task user_get email:admin@example.com
kubectl exec deployment/endura-team-server \
--namespace endura-team-server -- \
endura task user_update id:42 role:administratorRefresh or sign in again to load the new role.
Update Team Server
Before upgrading, add ENDURA_CREDENTIAL_ENCRYPTION_KEY to the Secret and check the plaintext credential blocker; a key alone does not convert old credentials.
Review Runtime Sensor compatibility, then resolve and pin the new chart version:
CHART_VERSION=$(curl -sSf "https://repo.endurasecurity.com/${CHANNEL}/release.json" \
| jq -er '.products.teamServer.chart.version') || exit $?
helm upgrade endura-team-server \
"oci://ghcr.io/endurasecurity/helm/${CHANNEL}/endura-team-server" \
--version "$CHART_VERSION" \
--namespace endura-team-server \
--values values-production.yaml || exit $?
kubectl rollout status deployment/endura-team-server \
--namespace endura-team-server --timeout=300s || exit $?After editing local production.yaml, reapply the ConfigMap before restarting:
kubectl create configmap endura-team-server-config \
--namespace endura-team-server \
--from-file=production.yaml \
--dry-run=client -o yaml | kubectl apply -f - || exit $?
kubectl rollout restart deployment/endura-team-server \
--namespace endura-team-server
kubectl rollout status deployment/endura-team-server \
--namespace endura-team-server --timeout=300s || exit $?Include every CA file when reapplying the ConfigMap. Apply credential changes to the Secret before restarting.
To rotate the credential-encryption key:
Generate a new value with
openssl rand -base64 32.In the protected
team-server.envsource file, move the old value toENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYSand setENDURA_CREDENTIAL_ENCRYPTION_KEYto the new value.Apply the Secret and restart the single-replica Deployment:
kubectl create secret generic endura-team-server-secrets \ --namespace endura-team-server \ --from-env-file=team-server.env \ --dry-run=client -o yaml | kubectl apply -f - || exit $? kubectl rollout restart deployment/endura-team-server \ --namespace endura-team-server kubectl rollout status deployment/endura-team-server \ --namespace endura-team-serverConfirm that the logs report
credentials validatedand that/_readinesssucceeds.Remove
ENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYSfromteam-server.env, then repeat the Secret update, restart, and validation in steps 3 and 4.
Keep the previous key until restart succeeds. JWT rotation separately revokes tokens signed with the old secret.
Backup and Uninstall
Use the database provider’s backup, recovery, and retention controls. Also back up production.yaml, the Helm values file, and the sources of all Kubernetes Secrets. A database backup cannot restore encrypted integration credentials without the credential-encryption keyring that was active when the backup was created.
helm uninstall endura-team-server --namespace endura-team-serverThe external database, ConfigMap, application Secret, and TLS Secret remain after uninstall. Delete them only after confirming they are no longer needed.
Troubleshooting
Pod Reports CreateContainerConfigError
Confirm that the ConfigMap selected by config.name and the Secret selected by secrets.name exist in the release namespace. Confirm that the ConfigMap contains a production.yaml key.
Pod Reports CrashLoopBackOff
Inspect the Team Server logs for configuration and startup errors. Confirm that the application Secret contains DATABASE_URL, a valid ENDURA_CREDENTIAL_ENCRYPTION_KEY, and every environment value referenced by production.yaml. An unavailable previous key or a credential-authentication failure also prevents startup.
Database Is Unreachable
Confirm DNS, network policy, TLS requirements, credentials, and the PostgreSQL port from the Team Server Pod’s network path. /_readiness reports database dependency failure; inspect Pod restart counts and events for process failures.
Ingress Opens but Login Fails
Confirm that SERVER_URL, server.host, CORS, settings.tenant.base_url, the ingress hostname, and the OIDC callback use the same public HTTPS origin. The callback includes /api/authentication/{provider}/callback. Team Server adds HttpOnly and SameSite=Lax itself and uses the HTTPS tenant URL to add Secure, even when ingress terminates TLS.
Internal Endpoint Is Blocked
Add only the required identity-provider or integration CIDRs to settings.http.allowed_networks. Mount internal CA bundles in the configuration ConfigMap and list their config/<file-name> paths under settings.http.ca_certificates.