Skip to Content
Cap CPU and Memory So One Container Can't Starve the Host
Advanced
0 likes
Tutorials

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
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.