Docker Compose Production Patterns
Layer a base compose file with an override for production, set resource limits, restart policies and healthcheck-aware dependencies.
Compose is not just for local dev. With a few patterns it runs small production stacks cleanly. The core idea is one base file plus environment-specific overrides.
Base + override files
Keep shared config in compose.yml and put prod-only settings (resource limits, no bind-mounted source, real restart policies) in compose.prod.yml. Compose merges them in order.
Production must-haves
- Restart policy —
restart: unless-stoppedso services survive reboots. - Resource limits — cap memory and CPU so one service can't starve the host.
- Health-gated startup —
depends_onwithcondition: service_healthy. - Pinned images — real tags or digests, never
latest. - No source bind mounts — ship the built image instead.
Roll it out
Deploy with both files and Compose does the merge. docker compose pull then up -d gives you a rolling-ish update on a single host.
Example
# compose.prod.yml — merged on top of compose.yml
services
api
imageghcr.io/acme/api1.4.2
restartunless-stopped
env_file.env.production
deploy
resources
limits
cpus"1.0"
memory512M
healthcheck
test"CMD" "wget" "-qO-" "http://localhost:3000/healthz"
interval30s
timeout3s
start_period20s
retries3
depends_on
db
conditionservice_healthy
db
imagepostgres16-alpine
restartunless-stopped
volumes
pgdata:/var/lib/postgresql/data
healthcheck
test"CMD-SHELL" "pg_isready -U postgres"
interval10s
timeout3s
retries5
volumes
pgdataWhen to use it
- A team maintains a base compose.yml for shared config and a compose.override.prod.yml that adds resource limits, restart policies, and production env vars.
- A CD pipeline runs docker compose -f compose.yml -f compose.prod.yml up -d to merge the base and production configs at deploy time.
- An ops engineer sets memory limits on all compose services to prevent a single runaway service from starving the host.
More examples
Base and production override files
Separates base config from production-only concerns (restarts, limits, logging) into two files merged at deploy time.
# compose.yml (base)
services:
api:
build: .
environment:
NODE_ENV: production
# compose.prod.yml (override)
services:
api:
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
logging:
driver: json-file
options:
max-size: '10m'
max-file: '3'Deploy with merged compose files
Merges base and production compose files, rebuilds images, starts services, and removes orphaned containers.
docker compose \
-f compose.yml \
-f compose.prod.yml \
up -d --build --remove-orphansRolling update for a service
Pulls the latest api image and recreates only that service without restarting its dependencies.
# Pull the new image version
docker compose pull api
# Recreate only the api service with zero downtime (requires multiple replicas)
docker compose up -d --no-deps api
Discussion