Skip to main content

Docker Production Recommendations

This guide covers Docker-specific hardening steps for running ThunderID in production. For configuration-level settings such as TLS certificates, encryption keys, and CORS, see the Production Deployment Guidelines.

Pin the Image Version

Always pin to a specific release tag rather than latest:

# Avoid: resolves to a different image after each release
ghcr.io/thunder-id/thunderid:latest

# Prefer: explicit and reproducible
ghcr.io/thunder-id/thunderid:<version>

Pinning ensures upgrades are intentional and your deployment is reproducible across environments.

Use PostgreSQL

SQLite stores data in a single local file and does not support concurrent writes. For production:

  • Use an external PostgreSQL instance (13 or later).
  • Configure the connection in deployment.yaml. See Configure a Production Database for the full schema.
  • Mount deployment.yaml into the container rather than baking configuration into a custom image.

Set a Restart Policy

Always run ThunderID with --restart unless-stopped so it recovers automatically from host reboots and unexpected exits:

docker run -d \
--name thunderid \
--restart unless-stopped \
...

In a Compose file:

services:
thunderid:
restart: unless-stopped

Mount Configuration and Security Material

Provision production configuration, certificates, private keys, and secrets outside the container. Mount them read-only into every ThunderID replica:

docker run -d \
--name thunderid \
--restart unless-stopped \
-p 8090:8090 \
--mount type=bind,src="$(pwd)/deployment.yaml",dst=/opt/thunderid/deployment.yaml,readonly \
--mount type=bind,src="$(pwd)/config/certs",dst=/opt/thunderid/config/certs,readonly \
--mount type=bind,src="$(pwd)/config/secrets",dst=/opt/thunderid/config/secrets,readonly \
--mount type=bind,src="$(pwd)/config/resources/server_configs/cors.yaml",dst=/opt/thunderid/config/resources/server_configs/cors.yaml,readonly \
--mount type=bind,src="$(pwd)/config/resources/server_configs/csp.yaml",dst=/opt/thunderid/config/resources/server_configs/csp.yaml,readonly \
ghcr.io/thunder-id/thunderid:<version>

The certs directory must contain the TLS pair, RSA and ECDSA signing pairs, and encryption key referenced by deployment.yaml. The secrets directory must contain the Direct Auth Secret when it uses the default file reference. See Mount Production Configuration for the complete file inventory and permission requirements.

Terminate TLS at a Reverse Proxy

Running a reverse proxy in front of ThunderID separates TLS termination from the application and gives you centralized certificate management. Use Nginx or Traefik.

NGINX configuration example:

server {
listen 443 ssl;
server_name thunderid.example.com;

ssl_certificate /etc/ssl/certs/thunderid.crt;
ssl_certificate_key /etc/ssl/private/thunderid.key;
ssl_protocols TLSv1.2 TLSv1.3;

location / {
proxy_pass https://thunderid:8090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Compose file with NGINX:

services:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/thunderid.conf
- ./certs:/etc/ssl
depends_on:
- thunderid

thunderid:
image: ghcr.io/thunder-id/thunderid:<version>
restart: unless-stopped
expose:
- "8090"
volumes:
- ./deployment.yaml:/opt/thunderid/deployment.yaml:ro
- ./config/certs:/opt/thunderid/config/certs:ro
- ./config/secrets:/opt/thunderid/config/secrets:ro
- ./config/resources/server_configs/cors.yaml:/opt/thunderid/config/resources/server_configs/cors.yaml:ro
- ./config/resources/server_configs/csp.yaml:/opt/thunderid/config/resources/server_configs/csp.yaml:ro

Set Resource Constraints

Prevent a runaway container from exhausting host resources by setting CPU and memory limits:

services:
thunderid:
image: ghcr.io/thunder-id/thunderid:<version>
deploy:
resources:
limits:
cpus: "2"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M

Start conservative and adjust based on observed usage.

Configure a Health Check

Add a health check so Docker can detect and restart an unhealthy container automatically:

services:
thunderid:
image: ghcr.io/thunder-id/thunderid:<version>
healthcheck:
test: ["CMD", "curl", "-fsk", "https://localhost:8090/health/liveness"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

Manage Secrets Securely

Never pass secrets as inline environment variables or in docker run commands: they appear in docker inspect output and shell history.

Use an environment file:

# .env (add to .gitignore)
DB_HOST=postgres.example.com
DB_USER=thunderid_user
DB_PASS=<secure-password>

Reference it with --env-file:

docker run -d \
--name thunderid \
--env-file .env \
...

Or in a Compose file:

services:
thunderid:
env_file: .env

For secrets that must not appear on disk even temporarily, use Docker Secrets (Swarm mode) or mount them from a secrets manager such as HashiCorp Vault or AWS Secrets Manager.

Configure Log Drivers

By default, Docker writes logs to JSON files with no size limit or rotation. Set limits to avoid filling the disk:

docker run -d \
--name thunderid \
--log-driver json-file \
--log-opt max-size=50m \
--log-opt max-file=5 \
...

To forward directly to a log aggregator, use the appropriate driver:

docker run -d \
--name thunderid \
--log-driver syslog \
--log-opt syslog-address=tcp://logs.example.com:514 \
...

Next Steps

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.