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