Without resource constraints, a Docker container can consume most of the CPU and memory available on its host. That gets risky on a shared server. One memory leak or CPU-heavy process may slow every other container and, in a bad enough case, destabilize the host itself.
Quick answer
Set --memory and --cpus when you start the container:
docker run -d \
--name web-app \
--memory="512m" \
--cpus="1.5" \
nginx:alpineThat caps the container at 512 MB of memory and the equivalent capacity of one and a half CPU cores. With Docker Compose, put mem_limit and cpus under the service:
services:
web:
image: nginx:alpine
mem_limit: 512m
cpus: 1.5One catch: Resource limits take effect when Docker creates the container. If the container already exists, recreate it after editing the Compose file. For supported limits, you can use docker update instead.
How Docker resource limits work
Docker relies on operating-system control groups, usually called cgroups, to track and restrict resource use. Memory limits determine how much memory a container can consume. CPU limits work a little differently: they control how much processor time the container can receive when the host is busy.
A CPU limit doesn’t pin the container to a specific core. With --cpus="2", for instance, the container can use up to the equivalent capacity of two logical CPUs, possibly spread across several cores.
| Requirement | Option | Example |
|---|---|---|
| Maximum memory | --memory | --memory="512m" |
| Reserved memory | --memory-reservation | --memory-reservation="256m" |
| Maximum CPU capacity | --cpus | --cpus="1.5" |
| Relative CPU priority | --cpu-shares | --cpu-shares="512" |
| Maximum processes | --pids-limit | --pids-limit="200" |
Setting limits with docker run
Limit container memory
This command gives the container a hard memory ceiling of 1 GB:
docker run -d --name api --memory="1g" my-api-imageIf the container crosses that limit and can’t reclaim enough memory, the kernel may kill one of its processes. Docker will commonly report the container as OOM-killed.
You can pair the hard limit with a lower, soft reservation:
docker run -d \
--name api \
--memory="1g" \
--memory-reservation="512m" \
my-api-imageThe reservation affects memory allocation while the host is under contention. It isn’t the cap, though. --memory remains the hard ceiling.
Control memory and swap
To control the combined amount of memory and swap available to a container, use --memory-swap:
docker run -d \
--name worker \
--memory="512m" \
--memory-swap="1g" \
my-worker-imageHere, the container gets 512 MB of memory and can use up to another 512 MB of swap. Exactly how swap behaves depends on the host configuration. For latency-sensitive applications, leaning heavily on it is usually a poor trade.
Limit CPU usage
For a direct CPU ceiling, --cpus is the simpler option:
docker run -d --name processor --cpus="0.75" my-imageThe container can consume up to 75% of one logical CPU’s capacity. If it needs to run only on selected logical CPUs, use --cpuset-cpus:
docker run -d --name processor --cpuset-cpus="0,2" my-imageCPU pinning has its place with specialized workloads. Most of the time, though, a general CPU quota is easier to operate.
Setting limits in Docker Compose
For an ordinary local Docker Compose deployment, define the limits directly beneath the service:
services:
app:
image: example/app:latest
mem_limit: 768m
mem_reservation: 384m
cpus: 1.25
pids_limit: 300
restart: unless-stoppedThen recreate the service so Docker applies the changed configuration:
docker compose up -d --force-recreateDocker Swarm resource settings
For a stack deployed to Docker Swarm, the settings belong under deploy.resources instead:
services:
app:
image: example/app:latest
deploy:
resources:
limits:
cpus: "1.50"
memory: 768M
reservations:
cpus: "0.25"
memory: 256MDeploy that stack with:
docker stack deploy -c compose.yaml productionDon’t assume the Swarm deployment settings will behave exactly like local Compose settings. Use the format that matches the command and environment you’re actually running.
Checking Docker CPU and memory limits
For a live view of resource use across running containers, start with:
docker statsTo see the limits configured for a particular container, run:
docker inspect web-app --format 'Memory={{.HostConfig.Memory}} NanoCPUs={{.HostConfig.NanoCpus}}'Docker returns memory in bytes. CPU capacity is stored in nano-CPUs, which means 1500000000 represents 1.5 CPUs.
You can also check whether the container was terminated after running out of available memory:
docker inspect web-app --format 'OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}}'Mistakes that cause trouble
- Setting limits too low: An application may fail during startup, garbage collection, migrations, or a traffic spike.
- Mixing up reservations and hard limits: A reservation doesn’t stop a container from consuming more resources.
- Editing Compose without recreating containers: Existing containers may keep their old settings.
- Treating CPU shares as a strict ceiling: CPU shares set relative priority during contention. They don’t impose a fixed maximum.
- Forgetting application-level limits: Worker counts, JVM heap size, database buffers, and runtime caches all need to fit inside the container limit.
Practical habits
- Measure normal and peak consumption with
docker statsbefore settling on limits. - Keep enough headroom for startup work and short-lived workload spikes.
- Set application memory options below Docker’s ceiling. A Java heap, for example, shouldn’t consume the container’s entire memory allowance.
- Use process limits to reduce the damage from fork bombs or runaway worker creation.
- After deployment, watch OOM events, throttling, response time, and restart counts.
- Leave capacity for Docker itself, the kernel, monitoring agents, and the host’s other services.
A sensible starting point: Watch the workload under realistic traffic, set the limit above the measured peak, then adjust it gradually. Limits chosen arbitrarily low tend to cause more downtime than they prevent.