Production Deployment Guidelines
This guide covers the critical configuration changes you must make before deploying ThunderID in a production environment. The default configuration is designed for local development and evaluation only. Do not use it in production without applying the changes below.
Prerequisites
Before you begin, ensure you have:
- A running ThunderID instance configured for your deployment target (see Choose Your Deployment).
- Access to a supported production database (PostgreSQL 13 or later recommended).
- Valid TLS certificates issued by a trusted Certificate Authority (CA).
- The
opensslcommand-line tool installed on your system.
Configure a Production Database
The default configuration uses SQLite, which stores data in local files. SQLite does not support concurrent writes from multiple processes and is not suitable for production use. Use PostgreSQL for production deployments.
ThunderID uses three separate databases:
| Database | Purpose |
|---|---|
config | Application configuration, flows, and identity providers |
runtime | Active sessions, tokens, and OAuth state |
user | User accounts and credentials |
Configure each database in deployment.yaml:
database:
config:
type: "postgres"
postgres:
hostname: "your-db-host"
port: "5432"
name: "configdb"
username: "dbuser"
password: "dbpassword"
sslmode: "require"
max_open_conns: 500
max_idle_conns: 100
conn_max_lifetime: 3600
runtime:
type: "postgres"
postgres:
hostname: "your-db-host"
port: "5432"
name: "runtimedb"
username: "dbuser"
password: "dbpassword"
sslmode: "require"
max_open_conns: 500
max_idle_conns: 100
conn_max_lifetime: 3600
user:
type: "postgres"
postgres:
hostname: "your-db-host"
port: "5432"
name: "userdb"
username: "dbuser"
password: "dbpassword"
sslmode: "require"
max_open_conns: 500
max_idle_conns: 100
conn_max_lifetime: 3600
Key settings:
- Set
sslmodeto"require"to enforce encrypted connections to the database. - Adjust
max_open_conns,max_idle_conns, andconn_max_lifetimebased on your expected traffic and database capacity. - Never store database credentials in
deployment.yamldirectly. Use environment variables, Kubernetes secrets, or a secrets manager.
Replace TLS Certificates
ThunderID ships with a self-signed TLS certificate for local development. Self-signed certificates trigger browser warnings and are not trusted by clients in production. Replace them with certificates issued by a trusted CA.
Step 1: Get a TLS Certificate
Get a certificate and private key for your production domain from a trusted CA. You can use:
- Let's Encrypt for free, automated certificates.
- Your organization's internal CA.
- A commercial CA.
Step 2: Replace the Default Certificates
Copy your certificate and private key to the ThunderID security directory:
cp your-domain.cert config/certs/server.cert
cp your-domain.key config/certs/server.key
Step 3: Update the Configuration
Verify the paths in deployment.yaml point to the correct files:
tls:
min_version: "1.3"
cert_file: "config/certs/server.cert"
key_file: "config/certs/server.key"
If you store certificates in a different location, update cert_file and key_file accordingly.
When deploying with Docker, mount the certificates as volumes:
docker run --rm \
-p 8090:8090 \
-v $(pwd)/deployment.yaml:/opt/thunder/deployment.yaml \
-v $(pwd)/certs/server.cert:/opt/thunder/config/certs/server.cert \
-v $(pwd)/certs/server.key:/opt/thunder/config/certs/server.key \
ghcr.io/thunder-id/thunderid:latest
Generate a Unique Encryption Key
ThunderID uses a symmetric encryption key to protect sensitive data at rest, including credentials and tokens. The default key in the repository is a well-known value that anyone can find. You must replace it before going to production.
Step 1: Generate a New Key
Generate a cryptographically secure 32-byte key:
openssl rand -hex 32
This command outputs a 64-character hexadecimal string. Copy the output.
Step 2: Store the Key
Write the generated value to the key file:
printf '%s' "<generated-key>" > config/certs/crypto.key
Replace <generated-key> with the output from the previous command. Do not include whitespace or newline characters.
Step 3: Verify the Configuration
Confirm deployment.yaml references the key file:
crypto:
encryption:
key: "file://config/certs/crypto.key"
You can also supply the key directly as an inline value, but this is not recommended for production. Prefer the file:// path form, an environment variable, or a secrets manager to avoid storing the key in deployment.yaml:
crypto:
encryption:
key: "<generated-key>"
Protect the encryption key with the same level of care as a database password. If the key is lost, encrypted data becomes unrecoverable. If the key is compromised, all protected data is exposed.
Configure a CORS Allowlist
Cross-Origin Resource Sharing (CORS) controls which browser origins can call ThunderID. Origins are stored in the server-config cors section. No origins are allowed by default, so list each production app explicitly.
Create config/resources/server_configs/cors.yaml:
name: cors
value:
allowedOrigins:
- "https://app.example.com"
- "https://admin.example.com"
To change origins without a restart, update the same section with PUT /server-config/cors. Runtime values are merged with the declarative file.
Guidelines:
-
List only origins that your applications actively use. Do not use wildcards (
*). -
Use the full origin format: scheme, hostname, and port if non-standard (for example,
https://app.example.com:8443). -
If your domain follows a consistent pattern, you can use a regular expression:
name: cors
value:
allowedOrigins:
- "https://app.example.com"
- regex: '^https://[a-z0-9-]+\.example\.com$'Anchor regex patterns with
^and$. ThunderID logs a startup warning for unanchored patterns because they can match unintended origins.
Configure Caching
ThunderID supports two cache backends: in-memory and Redis.
| Cache Type | Suitable For |
|---|---|
| In-memory | Single-pod deployments only |
| Redis | Multi-pod (replicated) deployments |
Do not use in-memory caching when running multiple replicas. Each pod maintains a separate cache, which causes cache inconsistency across the deployment. Cached entries written in one pod are invisible to other pods, leading to incorrect authentication or authorization decisions.
Single-Pod Deployments
For a single-pod deployment, in-memory caching is acceptable:
cache:
disabled: false
type: "inmemory"
size: 1000
ttl: 3600
evictionPolicy: "LRU"
cleanupInterval: 300
Multi-Pod Deployments
Use Redis when running more than one replica:
cache:
disabled: false
type: "redis"
ttl: 3600
redis:
address: "your-redis-host:6379"
username: ""
password: "your-redis-password"
db: 0
keyPrefix: "thunderid:"
Key settings:
- Set a
keyPrefixwhen multiple services share the same Redis instance. This avoids key collisions. - Secure Redis with authentication and network-level access controls. Do not expose Redis publicly.
- Use a managed Redis service (such as Amazon ElastiCache or Google Memorystore) for high availability.
Next Steps
After applying the production configuration:
- Restrict access to
deployment.yamland theconfig/certs/directory. These files contain sensitive credentials and keys. - Enable database backups on a regular schedule.
- Set up monitoring and alerting for ThunderID and its dependent services.
- Review the Kubernetes deployment guide for Helm-based configuration of the settings covered here.