Database Migration Strategy
Codex provides two commands for managing database migrations in multi-container environments:
migrate- Runs database migrations and exitswait-for-migrations- Waits for migrations to complete before proceeding
Problem
When multiple containers start simultaneously, they may all attempt to run migrations concurrently, causing conflicts and errors. This is especially problematic in Kubernetes deployments where multiple pods can start at the same time.
Solution
Use a dedicated migration job/container that runs migrations before application containers start. Application containers can then skip migrations and wait for them to complete.
Commands
migrate
Runs database migrations and exits with an appropriate exit code.
codex migrate --config /path/to/config.yaml
Usage:
- Kubernetes Job: Run migrations as a separate job before deploying application pods
- Docker Compose: Use as a one-time service that runs before the main application
Exit Codes:
0- Migrations completed successfully- Non-zero - Migration failed
wait-for-migrations
Waits for migrations to complete before proceeding. Useful as an init container in Kubernetes.
codex wait-for-migrations --config /path/to/config.yaml [--timeout 300] [--interval 2]
Options:
--timeout- Maximum time to wait in seconds (default: 300)--interval- Check interval in seconds (default: 2)
Usage:
- Kubernetes Init Container: Wait for migrations before starting the main container
- Docker Compose: Use as a dependency service
Environment Variables
CODEX_SKIP_MIGRATIONS
Set to "true" or "1" to skip automatic migrations on startup. When set, the application will:
- Skip running migrations automatically
- Verify that migrations are complete
- Fail to start if migrations are not complete
Example:
CODEX_SKIP_MIGRATIONS=true codex serve --config /path/to/config.yaml
Docker Compose Strategy
The docker-compose.yml includes a migration service that runs migrations before the application starts:
services:
# Migration job (runs once)
codex-migrate:
build:
context: .
dockerfile: Dockerfile
depends_on:
postgres:
condition: service_healthy
command: ["codex", "migrate", "--config", "/app/config/config.docker.yaml"]
restart: "no" # Run once and exit
# Application (skips migrations)
codex:
depends_on:
postgres:
condition: service_healthy
environment:
CODEX_SKIP_MIGRATIONS: "true"
command: ["codex", "serve", "--config", "/app/config/config.docker.yaml"]
Usage:
# Start migration job
docker-compose up codex-migrate
# Start application (migrations already done)
docker-compose up codex
Kubernetes Strategy
Option 1: Init Container (Recommended)
Use wait-for-migrations as an init container:
apiVersion: apps/v1
kind: Deployment
metadata:
name: codex
spec:
template:
spec:
initContainers:
- name: wait-for-migrations
image: codex:latest
command: ["codex", "wait-for-migrations", "--config", "/app/config/config.yaml"]
env:
- name: CODEX_DATABASE_POSTGRES_HOST
value: "postgres"
# ... other database config
containers:
- name: codex
image: codex:latest
env:
- name: CODEX_SKIP_MIGRATIONS
value: "true"
# ... other config
Option 2: Migration Job
Run migrations as a separate Kubernetes Job:
apiVersion: batch/v1
kind: Job
metadata:
name: codex-migrate
spec:
template:
spec:
containers:
- name: migrate
image: codex:latest
command: ["codex", "migrate", "--config", "/app/config/config.yaml"]
env:
- name: CODEX_DATABASE_POSTGRES_HOST
value: "postgres"
# ... other database config
restartPolicy: Never
backoffLimit: 3
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: codex
spec:
template:
spec:
containers:
- name: codex
image: codex:latest
env:
- name: CODEX_SKIP_MIGRATIONS
value: "true"
# ... other config
Deployment Order:
- Deploy migration job
- Wait for job completion
- Deploy application deployment
Best Practices
- Always run migrations before application starts - Use a job or init container
- Set
CODEX_SKIP_MIGRATIONS=truein application containers to prevent conflicts - Use health checks - Ensure database is ready before running migrations
- Monitor migration jobs - Set appropriate timeouts and retry limits
- Test migrations - Test migration strategy in staging before production
Troubleshooting
Migrations fail with "already applied" errors
This is normal when multiple containers try to run migrations. Use the migration job/init container strategy.
Application fails to start with "migrations not complete"
- Ensure migration job/init container completed successfully
- Check database connectivity
- Verify
CODEX_SKIP_MIGRATIONSis set correctly
Migration job hangs
- Check database connectivity
- Verify database credentials
- Check migration logs for errors
- Increase timeout if migrations are slow