Docker Best Practices
Dockerfile Best Practices
Use Official Base Images
Why:
- Regular security updates
- Well-documented
- Optimized builds
- Community-vetted
Use Specific Tags
Why:
- Predictable builds
- Easier rollbacks
- Security audit trail
- Build reproducibility
Minimize Layers
Why:
- Smaller image size
- Faster builds
- Better caching
Order Instructions for Cache Efficiency
Principle: Put least-changing instructions first, most-changing last
Use Multi-Stage Builds
Benefits:
- Smaller final image (50-90% reduction)
- No build tools in production
- Faster deployments
- More secure
Real-world example:
Use .dockerignore
Why:
- Faster builds (less context to send)
- Smaller images
- Avoid leaking secrets
- Clean build context
Don't Run as Root
Why:
- Security: Limit damage from container breakout
- Principle of least privilege
- Required for many Kubernetes environments
Handle Signals Properly
Graceful shutdown in Python:
Minimize Image Size
Image size comparison:
Alpine gotchas:
- Uses musl instead of glibc (some packages won't work)
- Smaller package ecosystem
- May need to compile dependencies
Set Metadata
Query labels:
Security Best Practices
Scan Images for Vulnerabilities
Example output:
Remediation:
- Update base image
- Update dependencies
- Apply patches
- If no fix: Accept risk or find alternative
Don't Include Secrets in Images
For sensitive files:
Secrets not in image!
Use Trusted Registries
Verify image signatures (Docker Content Trust):
Limit Container Capabilities
Resource Constraints
In Docker Compose:
Production Best Practices
Health Checks
In Docker Compose:
Check health status:
Logging
Application should log to stdout/stderr:
Configure logging driver:
Centralized logging:
Restart Policies
Best practice: Use unless-stopped for production services
Environment Variables
In Dockerfile (defaults only):
Container Naming and Tagging
Tagging strategy:
latest: Most recent stablev1.2.3: Specific releasev1.2: Latest patch in minor versionv1: Latest minor in major versiondev,staging,prod: Environment-specific builds- Git SHA:
myapp:abc123for traceability
Docker Compose Best Practices
Use Version 3.8+
Environment-Specific Overrides
Base configuration (docker-compose.yml):
Development override (docker-compose.override.yml):
Production override (docker-compose.prod.yml):
Use:
Dependency Management
Note: depends_on only waits for container start, not readiness. Use health checks!
Secrets Management
Access in app:
Named Volumes
Optimization Best Practices
Build Performance
Enable BuildKit:
Runtime Performance
Use specific base images:
Optimize layer caching:
Network Performance
Monitoring & Observability
Container Metrics
Export to monitoring system:
Distributed Tracing
Anti-Patterns to Avoid
❌ One Container Per Host
Problem: Wastes resources, defeats purpose of containers
Solution: Run multiple containers on same host
❌ Storing Data in Containers
Problem: Data lost when container removed
Solution: Use volumes for persistent data
❌ Using SSH in Containers
Problem: Containers should be immutable, accessed via docker exec
Solution: docker exec -it <container> bash
❌ Running Multiple Processes
Problem: Hard to manage, logs mixed, exit handling complex
Solution: One process per container (or use proper init system like tini)
❌ Using Latest Tag in Production
Problem: Unpredictable, can break deployments
Solution: Use specific version tags
❌ Large Images
Problem: Slow pulls, slow starts, security risks
Solution: Multi-stage builds, alpine images, minimal dependencies
Testing Checklist
Before pushing to production:
Ready to see these practices in action? Check out Use Cases for real-world Docker scenarios!