Skip to content

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 Sensors

Prepare Configuration

Both deployment methods use the same project directory, configuration, and environment file.

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 integration endpoints or internal certificate authorities.
  4. Confirm that server.host, CORS, and settings.tenant.base_url use SERVER_URL.

Create two distinct JWT secrets and a database password:

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

Each JWT secret must decode to at least 32 bytes, and the values must differ. Team Server validates both at startup.

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", "curl", "-f", "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

Production deployments must expose the public SERVER_URL over HTTPS. Terminate TLS at a trusted reverse proxy or load balancer, or configure Team Server to terminate it directly.

For direct termination, 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"

Restart the container, then verify the public endpoint:

curl -f https://team-server.example.com/_readiness

Use -k only while testing a self-signed certificate.

Set Up the First Administrator

An OIDC user must sign in once before the user record exists. After the first login, find the user ID:

docker compose exec endura-team-server \
  endura task user_get_id email:admin@example.com

For direct Docker, replace docker compose exec endura-team-server with docker exec endura-team-server.

Assign the role with the returned numeric ID:

docker compose exec endura-team-server \
  endura task user_set_role 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.

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 rm --force endura-team-server
# Run the same docker run command used for deployment.

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/

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.

Uninstall

Stop and remove the Compose services while preserving database data:

docker compose down

For direct Docker:

docker rm --force 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, and both JWT secrets are valid and different.

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.

An Integration Cannot Reach an Internal URL

Add the target network to settings.http.allowed_networks. If the endpoint uses an internal CA, mount the CA and add it to settings.http.ca_certificates. See Outbound HTTP Policy.

For additional help, contact support@endurasecurity.com.