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
- Use specific image tags, never
latest - Store secrets in .env files (never commit to Git)
- Use internal networks for backend services
- Set resource limits to prevent container sprawl
- Enable health checks for all services
- Run containers as non-root users
- Keep images updated for security patches
- 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.