Running Docker Compose in production: what nobody tells you
Docker Compose is great for development. Here's what changes when you use it in production — and how to handle it.
Docker Compose is the default way developers define multi-container apps. It's great locally.
But "just run docker compose up -d on the server" skips a lot of details that
matter in production. Here's what you need to handle.
1. Auto-restart on reboot
By default, Compose containers don't restart when the server reboots. Fix this by adding a restart policy to every service that should always be running:
services:
app:
image: my-app:latest
restart: unless-stopped # always restart except when manually stopped
db:
image: postgres:15
restart: unless-stopped 2. Persistent volumes
Containers are ephemeral. Any data written inside a container is lost when it's recreated. All persistent state must be in named volumes or bind mounts:
services:
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data: # Docker manages this volume
On Simplewala, the daily server backup includes /var/lib/docker/volumes,
so named volumes are backed up automatically.
3. Environment secrets
Don't put secrets in your Compose file or Docker images. Use an .env file
on the server (not committed to git) or Docker secrets:
# .env (on the server, not in git)
POSTGRES_PASSWORD=very_long_random_string
SECRET_KEY=another_long_random_string # docker-compose.yml
services:
app:
env_file: .env 4. Health checks
Docker only knows if a container is running — not if it's actually healthy. Add health checks so Compose knows when a service is ready:
services:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
app:
depends_on:
db:
condition: service_healthy 5. Zero-downtime updates
docker compose up -d recreates containers in an order that can briefly drop
connections. For a simple approach with a brief gap, this is fine. For zero downtime, run
a rolling update behind a reverse proxy:
# Scale up, let the proxy route to new containers, then scale down
docker compose up -d --no-deps --scale app=2 app
# wait a few seconds
docker compose up -d --no-deps --scale app=1 app 6. Log management
Container logs grow indefinitely by default. Limit log size:
services:
app:
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5" 7. Reverse proxy and SSL
Don't expose containers directly on port 80/443. Use a reverse proxy. On Simplewala, the Front Door proxy handles this — point it at your container's exposed port and SSL is handled automatically without touching Nginx config.
services:
app:
ports:
- "3000:3000" # expose on localhost:3000
# Front Door in Simplewala proxies yourdomain.com → localhost:3000 with SSL Summary checklist
- ✅
restart: unless-stoppedon all services - ✅ Named volumes for all persistent data
- ✅ Secrets in
.env, not in Compose file - ✅ Health checks on databases before app starts
- ✅ Log size limits
- ✅ Reverse proxy for SSL — don't expose containers directly
Ready to simplify your hosting?
Deploy your first server in 90 seconds. All features included.
Start free