Docker Container Resource Sizing Guide
Proper container sizing is critical for application performance, stability, and cost optimization. Under-provisioned containers lead to performance issues and crashes, while over-provisioned containers waste resources and money. This comprehensive guide covers best practices for sizing Docker containers across different workloads and platforms.
Understanding Container Resources
CPU Resources
Container CPU is typically measured in cores or millicores (1 core = 1000 millicores):
- CPU Requests: Guaranteed minimum CPU allocation
- CPU Limits: Maximum CPU the container can use
- CPU Shares: Relative priority when competing for CPU
- Throttling: Container is throttled when exceeding limits
Memory Resources
Memory allocation directly impacts container behavior:
- Memory Requests: Minimum memory guaranteed to the container
- Memory Limits: Maximum memory before OOM (Out of Memory) kill
- Swap: Generally disabled in container environments
- OOM Score: Priority for killing under memory pressure
Sizing by Workload Type
API/Web Servers
| Traffic Level | CPU Request | Memory Request | Notes |
|---|---|---|---|
| Low (<100 RPS) | 100-250m | 128-256 MB | Single core sufficient |
| Medium (100-1000 RPS) | 250-500m | 256-512 MB | Scale horizontally |
| High (1000+ RPS) | 500m-1000m | 512 MB-1 GB | Multiple replicas needed |
Background Workers
| Job Type | CPU | Memory | Considerations |
|---|---|---|---|
| I/O Bound | 100-250m | 256-512 MB | More workers, less CPU each |
| CPU Bound | 500m-2000m | 256-512 MB | Match to job parallelism |
| Memory Intensive | 250-500m | 1-4 GB | Watch for memory leaks |
Database Containers
Database containers require careful sizing for performance:
- PostgreSQL: 1-2 CPU cores, 2-8 GB RAM base
- MySQL: 1-2 CPU cores, 1-4 GB RAM base
- MongoDB: 2-4 CPU cores, 4-16 GB RAM
- Redis: 1 CPU core, memory sized to dataset
Language/Runtime Considerations
JVM-Based (Java, Kotlin, Scala)
- JVM requires significant base memory (512MB-1GB minimum)
- Set -Xmx to 75-80% of container memory limit
- Consider container-aware JVM flags (-XX:+UseContainerSupport)
- Startup can be slow; consider longer health check delays
Node.js
- Single-threaded by default; typically need <1 CPU per process
- Set --max-old-space-size for heap limit
- Use cluster module or multiple containers for concurrency
- Base memory: 128-256 MB for simple apps
Python
- GIL limits CPU parallelism per process
- Use multiple workers (gunicorn, uvicorn) for concurrency
- Memory usage varies significantly by libraries
- NumPy/Pandas can use significant memory
Go
- Efficient runtime with low base memory (10-50 MB)
- Excellent concurrency with goroutines
- Set GOMAXPROCS if limiting CPU
- Often can use smaller containers than other languages
.NET Core
- Modern .NET is container-optimized
- Base memory 100-200 MB
- Configure GC for container environments
- Consider ReadyToRun for faster startup
Requests vs Limits Best Practices
Setting Requests
Base requests on actual observed usage:
- Use P50-P75 of historical resource usage
- Include headroom for normal variation (10-20%)
- Consider startup requirements
- Monitor and adjust based on actual usage
Setting Limits
Limits should prevent runaway resource consumption:
- CPU: 2-3x the request (allows bursting)
- Memory: 1.25-1.5x the request (prevent OOM)
- Consider P99 usage plus safety margin
- Never set memory limits lower than peak usage
QoS Classes in Kubernetes
| QoS Class | Configuration | Eviction Priority |
|---|---|---|
| Guaranteed | Requests = Limits | Last to be evicted |
| Burstable | Requests < Limits | Middle priority |
| BestEffort | No requests/limits | First to be evicted |
Monitoring and Optimization
Key Metrics to Track
- CPU utilization: % of allocated CPU used
- Memory usage: Working set vs limit
- Throttle events: CPU throttling frequency
- OOM events: Out of memory kills
- Container restarts: Stability indicator
Tools for Profiling
- docker stats: Real-time container metrics
- cAdvisor: Container resource analysis
- Prometheus + Grafana: Historical metrics
- VPA (Kubernetes): Automatic right-sizing recommendations
- Goldilocks: VPA recommendations visualization
Cost Optimization Strategies
1. Right-Size Based on Data
Don't guess - use actual metrics:
- Collect at least 2 weeks of usage data
- Account for peak periods and batch jobs
- Use P95 CPU and max memory as baseline
- Review and adjust quarterly
2. Use Spot/Preemptible Instances
- 60-90% cost savings for fault-tolerant workloads
- Combine with on-demand for critical services
- Implement graceful shutdown handling
3. Implement Autoscaling
- Horizontal Pod Autoscaler (HPA) for varying load
- Scale to zero for development environments
- Set appropriate scale-down delays
4. Optimize Container Images
- Smaller images = faster scaling
- Use multi-stage builds
- Consider distroless or alpine base images
Platform-Specific Considerations
Kubernetes
- Use ResourceQuotas to prevent over-allocation
- LimitRanges for default container sizing
- Pod Disruption Budgets for availability
- Consider pod priority and preemption
AWS ECS
- Task sizing rounds up to supported combinations
- Fargate has specific CPU/memory combinations
- EC2 launch type offers more flexibility
- Consider Savings Plans for consistent workloads
Docker Swarm
- Resource reservations guarantee resources
- Limits cap maximum usage
- Simpler than Kubernetes but less granular
Common Sizing Mistakes
- Setting limits too low: Causes OOM kills and restarts
- No limits at all: One container can starve others
- Equal requests and limits: Prevents efficient bin packing
- Ignoring startup requirements: Containers may fail to start
- Not accounting for sidecars: Service mesh, logging add overhead
- One-size-fits-all: Different services have different needs
Conclusion
Container sizing is both an art and a science. Start with reasonable defaults based on your workload type and runtime, then iterate based on actual metrics. The goal is to find the sweet spot that ensures reliability and performance while minimizing costs. Regular monitoring and adjustment are key to maintaining optimal sizing as your application evolves.
