Skip to content

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-server

Save the production configuration example as config/production.yaml. Before deployment:

  1. Add the discovery jobs for the CI/CD providers you use.
  2. Configure at least one OIDC provider.
  3. Add outbound HTTP policy for private identity providers, integration endpoints, or internal certificate authorities.
  4. Confirm that server.host, CORS, and settings.tenant.base_url use SERVER_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 24

Create .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' >> .gitignore

Startup 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-server

Verify them:

docker compose ps
curl -f http://localhost:5150/_readiness

Readiness 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:testing

Verify startup:

docker ps --filter name=endura-team-server
docker logs endura-team-server
curl -f http://localhost:5150/_readiness

Configure 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.pem

For 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/_readiness

Use 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.com

For 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:administrator

Refresh 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 postgres

For 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-server

Direct 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:

  1. Generate a new value with openssl rand -base64 32.
  2. In .env, move the old value to ENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS and set ENDURA_CREDENTIAL_ENCRYPTION_KEY to the new value.
  3. Recreate the Team Server container so it reads the changed environment file. Use docker compose up -d endura-team-server for Compose, or repeat the direct Docker removal and run commands.
  4. Confirm that the logs report credentials validated and that /_readiness succeeds.
  5. 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.sql

For 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 down

For direct Docker:

docker stop --time 60 endura-team-server
docker rm endura-team-server

Deleting 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-server

Check 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-server

Readiness 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.