🎁 New User? Get 20% off your first purchase with code NEWUSER20 Register Now β†’
Menu

Categories

Docker Compose Production Deployment: Multi-Container Applications at Scale

Docker Compose Production Deployment: Multi-Container Applications at Scale

Docker Compose simplifies multi-container application deployment by defining your entire stack in a single YAML file. While Kubernetes dominates large-scale orchestration, Docker Compose remains the ideal choice for small to medium deployments, development environments, and single-server production setups.

Production-Ready docker-compose.yml

version: "3.8"

services:
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - certbot-data:/etc/letsencrypt:ro
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - frontend

  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/production
      - REDIS_URL=redis://cache:6379
      - NODE_ENV=production
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    restart: unless-stopped
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: production
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d production"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  cache:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: unless-stopped
    networks:
      - backend

volumes:
  postgres-data:
  redis-data:
  certbot-data:

networks:
  frontend:
  backend:
    internal: true

Environment Variables Management

# .env file (never commit to Git)
DB_PASSWORD=secure-random-password-here
REDIS_PASSWORD=another-secure-password
API_SECRET_KEY=your-api-secret

# .env.example (commit this as template)
DB_PASSWORD=change-me
REDIS_PASSWORD=change-me
API_SECRET_KEY=change-me

Health Checks

Health checks ensure containers are actually working, not just running:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
  interval: 30s     # Check every 30 seconds
  timeout: 10s      # Wait up to 10 seconds for response
  retries: 3        # Mark unhealthy after 3 failures
  start_period: 40s # Grace period during startup

Logging Configuration

services:
  api:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
        tag: "{{.Name}}"

Deployment Commands

# Deploy in production (detached)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Build and deploy
docker compose up -d --build

# Scale a service
docker compose up -d --scale api=3

# Rolling update (zero downtime)
docker compose up -d --no-deps --build api

# View logs
docker compose logs -f api

# Check status
docker compose ps

Backup Strategy

# Database backup
docker compose exec db pg_dump -U app production | gzip > backup-$(date +%Y%m%d).sql.gz

# Volume backup
docker run --rm -v postgres-data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-volume.tar.gz /data

Security Best Practices

  1. Use specific image tags, never latest
  2. Store secrets in .env files (never commit to Git)
  3. Use internal networks for backend services
  4. Set resource limits to prevent container sprawl
  5. Enable health checks for all services
  6. Run containers as non-root users
  7. Keep images updated for security patches
  8. Use read-only volumes where possible

Monitoring with Docker Stats

# Real-time resource usage
docker stats

# Format output
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"

Docker Compose is a proven tool for production deployments that balances simplicity with power. Use it for single-server deployments, and when your needs grow, the transition to Kubernetes becomes much smoother because you already think in containers and services.

Share this article:
Nico Brandt
About the Author

Nico Brandt

JavaScript Development, TypeScript Engineering, Web Application Architecture, Technical Documentation

Nico Brandt is a JavaScript and TypeScript developer focused on building well-structured, maintainable, and scalable web applications.

He works extensively with modern JavaScript and TypeScript across frontend and backend environments, emphasizing type safety, code readability, and predictable application behavior.

...
JavaScript TypeScript Frontend Development Backend APIs Asynchronous Programming

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.