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:

bash
# 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:

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

In a Compose file:

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

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

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

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

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

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

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

Reference it with --env-file:

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

Or in a Compose file:

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

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

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