S Simplewala
Docker DevOps

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.

Simplewala Team

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-stopped on 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