Docker
Deploy Team Server with Docker Compose and a local PostgreSQL container, or run the Team Server container against an existing PostgreSQL service.
Prerequisites
- Docker Engine 24 or later
- Docker Compose v2 for the Compose method
- PostgreSQL 18 or a compatible managed PostgreSQL service
- At least 2 GB of memory and 10 GB of disk for Team Server
- A Team Server license and an OIDC application for production authentication
- A public HTTPS URL reachable by users and Runtime Sensors
Prepare Configuration
mkdir -p team-server/config
cd team-serverSave the production configuration example as config/production.yaml. Before deployment:
- Add the discovery jobs for the CI/CD providers you use.
- Configure at least one OIDC provider.
- Add outbound HTTP policy for private identity providers, integration endpoints, or internal certificate authorities.
- Confirm that
server.host, CORS, andsettings.tenant.base_urluseSERVER_URL.
Create a credential-encryption key, two distinct JWT secrets, and a database password:
openssl rand -base64 32
openssl rand -base64 48
openssl rand -base64 48
openssl rand -hex 24Create .env with the generated values:
POSTGRES_DB=team_server
POSTGRES_USER=team_server
POSTGRES_PASSWORD=<generated_database_password>
DATABASE_URL=postgresql://team_server:<generated_database_password>@postgres:5432/team_server
ENDURA_CREDENTIAL_ENCRYPTION_KEY=<credential_encryption_key>
ENDURA_LICENSE_KEY=<license_key>
JWT_SENSOR_SECRET=<first_base64_secret>
JWT_USER_SECRET=<second_base64_secret>
SERVER_URL=https://team-server.example.com
TENANT_NAME=Example Organization
GOOGLE_OIDC_CLIENT_ID=<client_id>
GOOGLE_OIDC_CLIENT_SECRET=<client_secret>Use the environment-variable names referenced by your selected OIDC provider. Protect the file:
chmod 600 .env
printf '%s\n' '.env' >> .gitignoreStartup requires a 32-byte encryption key and two different JWT secrets of at least 32 bytes each.
Method 1: Docker Compose
Create docker-compose.yaml:
services:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
endura-team-server:
image: ghcr.io/endurasecurity/container/endura-team-server:testing
env_file: .env
ports:
- "5150:5150"
volumes:
- ./config:/app/config:ro
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:5150/_readiness"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
volumes:
postgres_data:Start the services:
docker compose up -d
docker compose logs -f endura-team-serverVerify them:
docker compose ps
curl -f http://localhost:5150/_readinessReadiness returns HTTP 200 with {"ok":true} after Team Server connects to PostgreSQL and completes startup.
Method 2: Existing PostgreSQL
Use this method when PostgreSQL is managed separately. Update DATABASE_URL in .env with the external host, then run:
docker run -d \
--name endura-team-server \
--env-file .env \
-p 5150:5150 \
-v "$PWD/config:/app/config:ro" \
--restart unless-stopped \
ghcr.io/endurasecurity/container/endura-team-server:testingVerify startup:
docker ps --filter name=endura-team-server
docker logs endura-team-server
curl -f http://localhost:5150/_readinessConfigure HTTPS
By default, forward public HTTPS through a trusted proxy to HTTP port 5150. Set SERVER_URL to the public HTTPS origin; keep the internal HTTP readiness check.
Direct TLS Termination
To terminate TLS in Team Server instead, add this to settings in production.yaml:
settings:
tls:
certificate: "/run/secrets/server_cert"
private_key: "/run/secrets/server_key"For Docker Compose, add the certificate mounts to the Team Server service:
services:
endura-team-server:
secrets:
- server_cert
- server_key
secrets:
server_cert:
file: ./certs/server.pem
server_key:
file: ./certs/server-key.pemFor direct Docker, add read-only mounts:
-v "$PWD/certs/server.pem:/run/secrets/server_cert:ro" \
-v "$PWD/certs/server-key.pem:/run/secrets/server_key:ro"Direct TLS replaces HTTP on port 5150. Set SERVER_URL=https://team-server.example.com:5150, with matching OIDC callbacks, CORS, and tenant URL. If a front end maps public 443 to this listener, use its origin instead.
Remove the HTTP-only Compose healthcheck. Monitor public HTTPS /_readiness with certificate validation and use HTTPS for subsequent checks.
Recreate the container after changing .env, apply certificate mounts and configuration, then verify the public endpoint:
curl -f https://team-server.example.com:5150/_readinessUse the port-443 URL if applicable. For a private CA, pass --cacert /path/to/ca.pem; reserve -k for temporary self-signed testing.
Set Up the First Administrator
Sign in to create the unscoped Viewer record, then find it by OIDC email or built-in <ENDURA_AUTH_USERNAME>@localhost (for example, developer@localhost):
docker compose exec endura-team-server \
endura task user_get email:admin@example.comFor direct Docker, replace docker compose exec endura-team-server with docker exec endura-team-server.
Read the numeric ID from the response’s data, then assign the role:
docker compose exec endura-team-server \
endura task user_update id:42 role:administratorRefresh the browser or sign in again. The Administration menu confirms the new role.
Operate the Deployment
Logs and Status
docker compose ps
docker compose logs -f endura-team-server
docker compose logs -f postgresFor direct Docker, use docker ps and docker logs -f endura-team-server.
Update
Review Runtime Sensor compatibility before a major update.
Before upgrading, set ENDURA_CREDENTIAL_ENCRYPTION_KEY and check the plaintext credential blocker; adding a key does not convert old credentials.
Docker Compose:
docker compose pull endura-team-server
docker compose up -d endura-team-server
docker compose logs endura-team-serverDirect Docker:
docker pull ghcr.io/endurasecurity/container/endura-team-server:testing
docker stop --time 60 endura-team-server
docker rm endura-team-server
# Run the same docker run command used for deployment.To rotate the credential-encryption key for either deployment method:
- Generate a new value with
openssl rand -base64 32. - In
.env, move the old value toENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYSand setENDURA_CREDENTIAL_ENCRYPTION_KEYto the new value. - Recreate the Team Server container so it reads the changed environment file. Use
docker compose up -d endura-team-serverfor Compose, or repeat the direct Docker removal and run commands. - Confirm that the logs report
credentials validatedand that/_readinesssucceeds. - Remove
ENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS, recreate the container, and repeat the validation checks.
Keep the previous key until restart succeeds. JWT rotation separately revokes tokens signed with the old secret.
Back Up and Restore
Back up the Compose-managed database and configuration:
docker compose exec -T postgres \
sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > team_server.sql
tar -czf team_server_config.tar.gz config/Back up source secrets (or .env) separately in protected storage. Restoring encrypted credentials requires the backup’s current encryption key and any previous keys then active.
Restore into an empty or intentionally replaced database:
docker compose exec -T postgres \
sh -c 'psql -U "$POSTGRES_USER" "$POSTGRES_DB"' < team_server.sqlFor external PostgreSQL, use the database provider’s backup and restore procedure.
Follow Maintenance and Recovery for archive verification, restore sequencing, and post-restore checks.
Uninstall
Stop and remove the Compose services while preserving database data:
docker compose downFor direct Docker:
docker stop --time 60 endura-team-server
docker rm endura-team-serverDeleting the Compose volume is permanent
docker compose down --volumes deletes the PostgreSQL volume and all Team Server data. Back up the database and confirm the project directory before running it.
Troubleshooting
Container Does Not Start
docker compose config
docker compose logs endura-team-serverCheck that config/production.yaml is mounted, every referenced environment variable is present, the credential-encryption key is valid, and both JWT secrets are valid and different. An unavailable previous key or a credential-authentication failure also prevents startup.
Database Connection Fails
docker compose logs postgres
docker compose exec postgres \
sh -c 'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"'Confirm that DATABASE_URL uses the Compose service name postgres, not localhost. For an external database, confirm DNS, port 5432, TLS requirements, and credentials from the Team Server container.
Readiness Fails
curl -v http://localhost:5150/_readiness
docker compose logs --tail 100 endura-team-serverReadiness stays unavailable while the database is unreachable or startup validation fails.
Team Server Cannot Reach an Internal OIDC or Integration Endpoint
Add the target network to settings.http.allowed_networks. If the identity provider or integration endpoint uses an internal CA, mount the CA and add it to settings.http.ca_certificates. See Outbound HTTP Policy.
For additional help, use the Endura contact page.