Without limits, one leaky container can starve every other service on the host.
By default a container may use all of the host's CPU and memory. Limits are enforced by the kernel's control groups, and you set them on docker run (or in Compose):
--memory 512m: hard memory limit. If the container exceeds it, the kernel's OOM killer kills a process in it, usually the main one, and the container exits with code 137.docker inspect -f '{{.State.OOMKilled}}'confirms it.--memory-swap: memory plus swap. Setting it equal to--memorydisables swap for the container.--memory-reservation: a soft limit applied when the host is under memory pressure.--cpus 1.5: at most 1.5 CPUs' worth of time. Exceeding it means throttling (slower), not a kill.--cpu-shares: a relative weight that only matters when CPUs are contended.--cpuset-cpus 0,1: pin to specific cores.--pids-limit 200: cap the number of processes, which stops fork bombs.
docker stats shows live usage against limits. docker update --memory 1g api changes limits on a running container.
Make the runtime aware of its limits. Modern JVMs (container support is on by default) and Go read cgroup limits, though Go's GOMAXPROCS only became container-aware in recent versions. Node sizes its heap from container memory in recent versions too, but --max-old-space-size is still common. Many apps size worker pools from the host's CPU count, so set worker counts explicitly.
In Compose, use deploy.resources.limits (cpus, memory) and optionally reservations; docker compose up applies them.
Set limits from measurements: watch real usage under load, then add headroom. A limit that's too tight produces mysterious exit 137 restarts.
Example
docker run -d --name api --memory 512m --memory-swap 512m \
--cpus 1.5 --pids-limit 200 acme/api:1.4.2
docker stats --no-stream api
# NAME CPU % MEM USAGE / LIMIT MEM % PIDS
# api 37.2% 311MiB / 512MiB 60.7% 23
docker inspect api --format '{{.State.ExitCode}} OOM={{.State.OOMKilled}}'
# 137 OOM=true (after a memory spike)
docker update --memory 1g --memory-swap 1g api
# compose.yaml equivalent:
# deploy:
# resources:
# limits: { cpus: "1.5", memory: 512M }Remember
- Default is unlimited; set --memory, --cpus and --pids-limit for production
- Memory over the limit = OOM kill (exit 137); CPU over the limit = throttling
- Make runtimes and worker pools aware of the limits
Watch out for
- Setting a memory limit without telling the JVM, Node or worker pool, so the app is repeatedly OOM-killed.