Docker Compose Core Knowledge
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: docker-compose for comprehensive documentation.
Full Stack Example
docker-compose.yml
services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - DATABASE_URL=postgresql://user:pass@db:5432/mydb - REDIS_URL=redis://redis:6379 depends_on: db: condition: service_healthy redis: condition: service_started volumes: - ./src:/app/src # Dev hot reload networks: - app-network
db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: mydb volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d mydb"] interval: 5s timeout: 5s retries: 5 networks: - app-network
redis: image: redis:7-alpine volumes: - redis_data:/data networks: - app-network
volumes: postgres_data: redis_data:
networks: app-network: driver: bridge
Common Commands
Start services
docker-compose up -d docker-compose up --build # Rebuild images
Stop
docker-compose down docker-compose down -v # Remove volumes too
Logs
docker-compose logs -f app docker-compose logs --tail=100 app
Execute
docker-compose exec app sh docker-compose exec db psql -U user mydb
Scale
docker-compose up -d --scale app=3
Override for Dev
docker-compose.override.yml (auto-loaded)
services: app: build: target: development volumes: - .:/app - /app/node_modules command: npm run dev
Environment Files
services: app: env_file: - .env - .env.local
When NOT to Use This Skill
Skip this skill when:
-
Creating single-container Dockerfiles - use docker skill
-
Deploying to production Kubernetes clusters - use kubernetes skill
-
Setting up CI/CD workflows - use github-actions skill
-
Working with Docker Swarm (deprecated) - use kubernetes skill
-
Building Docker images only (no orchestration needed) - use docker skill
Anti-Patterns
Anti-Pattern Problem Solution
Hardcoding ports in compose Port conflicts, inflexible Use ${PORT:-3000}:3000 with env vars
No health checks Services start before ready Add healthcheck and depends_on.condition
Exposing all ports publicly Security risk Bind to 127.0.0.1:port for local only
Using latest tags Unpredictable behavior Pin specific versions postgres:16-alpine
Storing secrets in compose file Secret exposure Use Docker secrets or env files (gitignored)
No resource limits Resource exhaustion Set deploy.resources.limits
Mixing dev and prod config Configuration drift Use docker-compose.override.yml and .prod.yml
Not using named volumes Data loss on recreation Use named volumes for persistence
Services on default network No isolation Create custom networks per tier
Running as root Security vulnerability Set user: "1000:1000" or use Dockerfile USER
Quick Troubleshooting
Issue Diagnosis Fix
Service won't start Dependency not ready Add depends_on with condition: service_healthy
Port already in use Another service using port Change port mapping or stop conflicting service
Database connection refused Service name wrong, network issue Use service name as host: db:5432 not localhost
Changes not reflected Old containers running Run docker-compose down && docker-compose up --build
Volume data not persisting Using anonymous volumes Use named volumes in top-level volumes:
"network not found" Network removed or wrong name Run docker-compose up to recreate networks
Can't connect between services Different networks Put services on same network
Environment variables not working Wrong syntax, not loaded Use ${VAR:-default} and check .env file
"service unhealthy" Health check failing Check docker-compose logs and fix health endpoint
Slow startup on Mac/Windows Volume mounting overhead Use named volumes instead of bind mounts for dependencies
Production Readiness
Security Configuration
docker-compose.prod.yml
services: app: image: myapp:${VERSION:-latest} security_opt: - no-new-privileges:true read_only: true tmpfs: - /tmp cap_drop: - ALL cap_add: - NET_BIND_SERVICE user: "1000:1000" deploy: resources: limits: cpus: '2' memory: 2G reservations: cpus: '0.5' memory: 512M
db: image: postgres:16-alpine environment: POSTGRES_PASSWORD_FILE: /run/secrets/db_password secrets: - db_password volumes: - postgres_data:/var/lib/postgresql/data:Z
secrets: db_password: file: ./secrets/db_password.txt
Health Checks
services: app: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s
db: healthcheck: test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"] interval: 10s timeout: 5s retries: 5
redis: healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3
Networking
services: app: networks: - frontend - backend ports: - "127.0.0.1:3000:3000" # Only localhost
db: networks: - backend # No ports exposed externally
nginx: networks: - frontend ports: - "80:80" - "443:443"
networks: frontend: driver: bridge backend: driver: bridge internal: true # No internet access
Logging
services: app: logging: driver: json-file options: max-size: "10m" max-file: "3" labels: "service,environment" labels: - "service=app" - "environment=production"
Or use external logging
app: logging: driver: fluentd options: fluentd-address: localhost:24224 tag: "docker.{{.Name}}"
Restart Policies
services: app: restart: unless-stopped deploy: restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s
db: restart: always
Backup Strategy
services: backup: image: postgres:16-alpine entrypoint: /bin/sh command: -c "pg_dump -h db -U $$POSTGRES_USER $$POSTGRES_DB | gzip > /backups/backup_$$(date +%Y%m%d_%H%M%S).sql.gz" environment: PGPASSWORD_FILE: /run/secrets/db_password secrets: - db_password volumes: - ./backups:/backups depends_on: db: condition: service_healthy profiles: - backup
Testing
docker-compose.test.yml
services: test: build: context: . target: test environment: - DATABASE_URL=postgresql://test:test@db-test:5432/testdb depends_on: db-test: condition: service_healthy command: npm test
db-test: image: postgres:16-alpine environment: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: testdb tmpfs: - /var/lib/postgresql/data # In-memory for speed
Run tests
docker-compose -f docker-compose.test.yml up --abort-on-container-exit --exit-code-from test
Monitoring Metrics
Metric Target
Container uptime
99.9%
Health check pass rate 100%
Restart count < 1/day
Memory usage < 80% limit
Checklist
-
Security opts (no-new-privileges, read_only)
-
Resource limits (CPU, memory)
-
Health checks on all services
-
Internal networks for databases
-
Secrets management (not env vars)
-
Logging with rotation
-
Restart policies configured
-
Backup strategy defined
-
Test compose file
-
Production override file
Reference Documentation
-
Networking
-
Production Setup