Cap CPU and Memory So One Container Can't Starve the Host
Set CPU and memory limits, watch them work, change them on a running container and set them in Compose. After this, one busy container cannot take the whole machine.
Without limits, one busy or leaking container can take all the CPU and memory, and every other container on the machine slows down or dies with it. This page sets CPU and memory limits, checks that they work, and changes them on a running container. Run on Docker Desktop 4.85.0 (Engine 29.6.2) with alpine:3.24 and python:3.14-alpine.
Cap the CPU
Start a container that keeps one CPU core busy forever, limited to half a core:
docker run -d --name cpu-hog --cpus 0.5 alpine:3.24 sh -c "while :; do :; done" | Out-Null
Start-Sleep -Seconds 5
docker stats cpu-hog --no-stream --format "{{.Name}} CPU {{.CPUPerc}}"
cpu-hog CPU 50.11%
About 50 percent of one core, even though the loop would happily use 100. The exact number moves around a little between samples. A CPU limit never kills anything. The container just gets slower when it reaches the limit.
Change the limit while it runs
docker update changes limits without restarting the container:
docker update --cpus 0.25 cpu-hog | Out-Null
Start-Sleep -Seconds 5
docker stats cpu-hog --no-stream --format "{{.Name}} CPU {{.CPUPerc}}"
docker inspect cpu-hog --format "NanoCpus={{.HostConfig.NanoCpus}}"
docker rm -f cpu-hog | Out-Null
cpu-hog CPU 24.76%
NanoCpus=250000000
Cap the memory
Memory is different. If a container goes over its memory limit, the kernel kills it:
docker run --name mem-capped --memory 64m --memory-swap 64m python:3.14-alpine python -c "x = bytearray(200 * 1024 * 1024)"
docker inspect mem-capped --format "ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}"
ExitCode=137 OOMKilled=true
Exit code 137 and OOMKilled=true. That is the limit doing its job: this container died, and nothing else on the machine did. Set --memory-swap to the same value as --memory, otherwise the container can also use swap and gets very slow before it is killed.
Pick the numbers from a measurement
Don't guess. Run the service with a normal load and watch it:
docker run -d --name mem-measure python:3.14-alpine python -m http.server 8000 | Out-Null
Start-Sleep -Seconds 3
docker stats mem-measure --no-stream --format "{{.Name}} memory {{.MemUsage}}"
mem-measure memory 15.5MiB / 15.58GiB
With no limit set, the number after the slash is the memory available to the Docker VM. Measure usage under representative load, including expected peaks, then choose a limit with enough headroom for that workload. Test the service with the limit applied and adjust it using further measurements. If it exits with code 137, inspect OOMKilled and the service logs before deciding whether the memory limit needs to change.
In Compose
services:
api:
image: python:3.14-alpine
command: ["python", "-m", "http.server", "8000"]
cpus: 0.5
mem_limit: 256m
memswap_limit: 256m
docker compose -f limits-demo/compose.yaml up -d 2>$null
docker inspect limits-demo-api-1 --format "Memory={{.HostConfig.Memory}} NanoCpus={{.HostConfig.NanoCpus}}"
docker compose -f limits-demo/compose.yaml down 2>$null
Memory=268435456 NanoCpus=500000000
256 MB in bytes and half a CPU. Compose also accepts the same limits under deploy.resources.limits. Use one style in a project, not both.
Docker Desktop has a limit above yours
On Docker Desktop with WSL 2, all containers live inside one Linux VM, and Windows decides how much memory and CPU that VM can use. You change that in the .wslconfig file in your Windows user folder, not in Docker. Container limits only share out what the VM has. I did not change .wslconfig for this page.
⚠ Warning: A memory limit lower than what the app really needs does not make it use less memory. It makes it restart in a loop. Measure first.
Related course: Docker Advanced.
About
Set CPU and memory limits, watch them work, change them on a running container and set them in Compose. After this, one busy container cannot take the whole machine.
- Responsible
- Christopher R
- Last Update
- 08/01/2026
- Reading Time
- 4 min
Reviews
Sign in to write a reviewComments (0)
Sign in to commentNo comments yet.