Skip to content

Set Docker resource limits

Severity: lowApplies to: Docker Engine 27.x+Applies to: Docker Engine 29.x
The fix
Terminal window
docker run \
--memory=512m \
--cpus=1.0 \
--pids-limit=200 \
--read-only --tmpfs /tmp \
myimage
services:
app:
mem_limit: 512m
cpus: 1.0
pids_limit: 200
read_only: true
tmpfs: [/tmp]

Containers have no resource limits by default. A container can allocate all the host’s memory, saturate every core, and create processes until the kernel’s PID table is full. Namespaces isolate what a process can see; they do not ration what it can consume.

This closes no exposure and stops no attacker who has code execution — which is why it’s low and last. What it prevents is one container taking down every other container on the box, which is a different problem and a real one.

Of the three, this is the one worth setting even if you skip the others.

Memory and CPU exhaustion are noisy and gradual; the OOM killer eventually intervenes, and a pegged CPU is slow rather than fatal. PID exhaustion is neither. A fork bomb in one container fills the host’s global PID table, and once it’s full the host cannot fork any new process at all — including the shell you’d use to fix it, and including docker itself. Recovery is often a hard reboot.

Terminal window
--pids-limit=200

That’s the whole fix. It costs nothing, breaks essentially nothing (200 processes is generous for a typical service), and turns an unrecoverable host into one container failing.

It doesn’t need to be an attack, either — a buggy loop spawning subprocesses does it just as well.

Terminal window
--read-only --tmpfs /tmp

A read-only root filesystem means a compromise can’t drop a payload into the container, can’t rewrite your application code, and can’t persist anything across a restart. --tmpfs /tmp gives back the scratch space most applications need, and it lives in memory rather than on disk.

Most stateless services run read-only with no changes. It’s one of the highest value-to-effort items here, which is why it’s on this page rather than left out — it doesn’t really belong under “resource limits”, but it belongs somewhere and this is the closest fit.

Terminal window
--memory=512m

Without --memory-swap, Docker sets swap to twice the memory limit — so --memory=512m actually permits 512 MB RAM plus 512 MB swap. If you meant “512 MB total, no swap”:

Terminal window
--memory=512m --memory-swap=512m

Setting them equal disables swap for the container. That’s usually what you want for a service where swapping is worse than failing, and it’s not what you get by default.