A practical guide to planning Docker host and container capacity, estimating resources, setting limits, recognizing scaling signals, and applying safety margins with concrete examples.
Intro
Docker capacity planning is the process of estimating and allocating compute, memory, storage, and network resources for containerized workloads. Without deliberate planning, teams often hit unexpected outages, wasted cloud spend, or missed performance targets. This guide provides a practical, step-by-step approach to capacity planning for Docker hosts and containers. You will learn how to inventory your environment, set resource limits, verify configurations, diagnose problems, and establish an operational checklist. The examples use Docker CLI commands and Compose files, and all numbers are illustrative, to be adapted to your workload.
Version and Environment Inventory
Before changing any limits, record the current Docker version, host specifications, and running workloads. This baseline helps you understand what you have, identify containers without limits, and track changes over time.
Verify Docker and OS versions
Run:
docker version --format '{{.Server.Version}}'
Example output:
20.10.12
Record the host kernel and OS:
uname -r && cat /etc/os-release | head -n 2
Example output:
5.4.0-109-generic
NAME="Ubuntu"
VERSION="20.04.4 LTS (Focal Fossa)"
Inspect host resources
Use free -h for memory, nproc for CPUs, and df -h for storage.
Example host: 4 vCPU, 16 GB RAM, 100 GB disk.
free -h
nproc
df -h /
Typical output:
total used free shared buff/cache available
Mem: 15Gi 3.2Gi 10Gi 256Mi 1.8Gi 11Gi
Swap: 2.0Gi 0B 2.0Gi
4
/dev/sda1 98G 32G 61G 35% /
List running containers and their resource usage
docker stats --no-stream
Example output (truncated):
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
abc123 web 0.50% 256MiB / 15.6GiB 1.60% 1.2MB / 300kB 0B / 0B 10
def456 db 2.10% 1.1GiB / 15.6GiB 7.03% 500kB / 200kB 10MB / 2MB 25
Note which containers have no memory limit (shown as host total) and which have explicit limits. In the example above, both containers show the host total (15.6GiB) as the limit, meaning no limit is set. This baseline helps identify where to focus planning.
Prerequisites for capacity planning
- Docker Engine 20.10 or later (for cgroup v2 support).
- Access to Docker daemon with appropriate permissions.
- A test or staging environment to validate changes before production.
- Monitoring tooling (e.g., Prometheus, cAdvisor, or Docker stats) to collect metrics.
Estimating Resource Needs
Before setting limits, estimate what each container actually needs. Use a combination of observation, load testing, and application knowledge.
Observe existing usage
If the container is already running, let it run under normal load for at least 24-48 hours and capture peak usage. Use docker stats periodically or a monitoring system.
Example: For a web container, you might see average CPU 0.3 cores, peak 1.2 cores; memory average 300 MB, peak 800 MB. Use peak as a starting point for limits.
Load testing
If the container is new or you anticipate a traffic spike, simulate load with tools like ab, wrk, or hey. Run the container with generous limits initially, then measure.
docker run -d --name myapp-test --cpus=4 --memory=4g myimage:latest
# Generate load from host or another container
ab -n 10000 -c 100 http://localhost:8080/
While the test runs, monitor docker stats myapp-test and note peak CPU and memory.
Application knowledge
Consult developers or documentation for expected resource profiles. A Java application with a 2 GB heap will need more memory than the heap size due to overhead. A CPU-bound video transcoder may need multiple cores.
Add a safety margin
Limits should not be set at the exact peak. Add a margin of 20-30% for headroom and unexpected spikes. For the web example above, you might set a CPU limit of 1.5 cores and memory limit of 1 GB.
Safe Configuration Path
Set resource limits on containers to prevent a single workload from starving others. The main controls are CPU and memory limits. Storage and network limits are less commonly managed per container, but you should plan for them at the host level.
CPU limits
Docker uses CPU shares for relative weight and CPU quota for hard limits. For simplicity, start with CPU quota using --cpus.
Example: limit a container to 1.5 CPUs
docker run -d --name myapp --cpus="1.5" myimage:latest
To verify:
docker inspect myapp --format '{{.HostConfig.NanoCpus}}'
Expected output: 1500000000 (nano CPUs).
If the container attempts to use more than 1.5 cores, it will be throttled.
Memory limits
Memory limits are critical because exceeding them can cause the OOM killer to terminate processes. Use --memory and optionally --memory-swap.
Example: limit memory to 512 MB and disallow swap
docker run -d --name myapp --memory="512m" --memory-swap="512m" myimage:latest
By setting --memory-swap equal to --memory, the container cannot use swap.
To verify from inside the container, check cgroup limits:
docker exec myapp cat /sys/fs/cgroup/memory.max
Expected output (in bytes): 536870912.
Docker Compose configuration
For multi-container applications, define limits in docker-compose.yml.
services:
web:
image: nginx:1.23
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
Note: deploy.resources applies to Swarm mode. For non-Swarm Compose, use the mem_limit and cpus keys (version 2 format) or the resources block in version 3 with docker-compose v1.28+.
For the latest Compose specification, use the resources block under services (not deploy) if you are not using Swarm:
services:
web:
image: nginx:1.23
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
Storage and network planning
- Storage: Monitor host disk usage and container writable layers. Use
docker system dfto see space used by images, containers, and volumes.
docker system df
Example output:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 3 4.2GB 2.1GB (50%)
Containers 8 5 1.5GB 100MB (6%)
Local Volumes 4 2 3.8GB 0B (0%)
Build Cache 0 0 0B 0B
- Network: If using user-defined bridge networks, ensure adequate IP range and avoid port exhaustion.
Avoid setting storage quotas per container unless using specific storage drivers (e.g., overlay2 with project quotas). For most teams, host-level monitoring suffices.
Verification and Diagnostics
After applying limits, verify they are effective and monitor for signs of resource pressure.
Check effective limits
Use docker inspect to confirm limits are set:
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' myapp
Example output: 536870912 1500000000.
Monitor runtime usage
docker stats myapp --no-stream
Observe MEM USAGE / LIMIT column; if usage is close to limit, the container may be under stress.
Simulate load to validate limits
For CPU, run a stress tool inside the container (if available) and observe throttling:
docker exec myapp sh -c "apt-get update && apt-get install -y stress && stress --cpu 4"
In another terminal, docker stats will show CPU % capped at the limit (e.g., 150% for 1.5 CPUs). For memory, you can allocate memory in a test container and confirm it gets killed at the limit.
Host-level diagnostics
free -hto see host memory pressure.vmstat 1to observe CPU run queue and swapping.iostat -x 1for disk I/O saturation.docker eventsto catch OOM kills or restarts.
Expected results and thresholds
Define acceptable thresholds. For example:
- CPU throttling: if
nr_throttledin cgroup CPU stats increases significantly, the container is being throttled. - Memory: if container memory usage consistently exceeds 80% of limit, consider increasing limit or optimizing application.
- Host: maintain at least 20% free memory and CPU headroom for the Docker daemon and system processes.
Failure Modes and Recovery
Common capacity-related failures and how to recover.
Out of Memory (OOM) kills
When a container exceeds its memory limit, the kernel OOM killer terminates a process inside the container. The container may restart if a restart policy is set.
Detection: docker inspect myapp --format '{{.State.OOMKilled}}' returns true. Also look for kernel logs (dmesg | grep -i oom).
Recovery: Increase memory limit if the workload legitimately needs more, or optimize the application. Rollback by restoring the previous limit and restarting the container.
CPU starvation
If a container is CPU throttled, it may experience high latency. Check docker stats for high CPU % equal to limit, and inspect cgroup cpu.stat for nr_throttled:
docker exec myapp cat /sys/fs/cgroup/cpu.stat
If throttling is severe, increase CPU quota or move workload to a larger host.
Disk full
Containers writing logs or data can fill the host disk. This can cause Docker daemon issues.
Detection: df -h shows high usage; docker system df shows large volumes or container logs.
Recovery: Clean up unused images/containers (docker system prune), rotate logs, or add storage. To avoid recurrence, configure log rotation in daemon.json or per container.
Rollback plan
Always document the previous resource settings. Use configuration files (Compose, scripts) under version control. To rollback, revert the configuration and restart the container or service. Example:
docker compose down && git checkout previous-compose.yml && docker compose up -d
Operations Checklist
Use this checklist for ongoing capacity management.
| Item | Frequency | Command / Action |
|---|---|---|
| Review container resource usage | Weekly | docker stats --no-stream |
| Check host memory and CPU headroom | Weekly | free -h, uptime |
| Check disk usage (host and Docker) | Weekly | df -h, docker system df |
| Review for OOM kills | After incidents | docker inspect <container> --format '{{.State.OOMKilled}}' |
| Review log sizes and rotation | Monthly | du -sh /var/lib/docker/containers//.log |
| Update resource limits based on trends | As needed | Edit Compose file or run commands, then apply |
| Test disaster recovery for limits | Quarterly | Simulate memory exhaustion in staging |
Additional operational practices
- Set alerts for host resource usage above 80%.
- Use cAdvisor or Prometheus to collect container metrics.
- Perform capacity reviews after each major release.
- Document each service's expected baseline and peak resource usage.
Scaling Signals and Capacity Planning
Knowing when to scale is part of capacity planning. Watch for these signals:
- Consistent high CPU usage (>80% of limit) over several days.
- Memory usage approaching limit with frequent OOM kills.
- Increasing response times or error rates.
- Host running out of resources (high load average, low free memory).
When scaling, consider both vertical (increase limits) and horizontal (add replicas) approaches. For stateful services like databases, vertical scaling may be easier; for stateless web apps, horizontal scaling with a load balancer is common.
Conclusion
Docker capacity planning is an ongoing discipline, not a one-time task. Start with a clear inventory of your environment, set conservative limits, verify them under simulated load, and monitor for deviations. Use the checklist to review capacity regularly, and always have a rollback plan. By following the practical examples in this guide, you can avoid common resource pitfalls and maintain reliable containerized services.
Next steps:
- Run the version and environment inventory on one host.
- Set CPU and memory limits on your most critical container.
- Verify the limits with
docker inspectanddocker stats. - Simulate a memory or CPU spike to observe behavior.
- Document your baseline and establish a monitoring routine.