Set Docker resource limits
docker run \ --memory=512m \ --cpus=1.0 \ --pids-limit=200 \ --read-only --tmpfs /tmp \ myimageservices: app: mem_limit: 512m cpus: 1.0 pids_limit: 200 read_only: true tmpfs: [/tmp]Why it matters
Section titled “Why it matters”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.
pids-limit is the one that matters
Section titled “pids-limit is the one that matters”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.
--pids-limit=200That’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.
read-only is cheap and underused
Section titled “read-only is cheap and underused”--read-only --tmpfs /tmpA 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.
Memory limits have a subtlety
Section titled “Memory limits have a subtlety”--memory=512mWithout --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”:
--memory=512m --memory-swap=512mSetting 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.
Related
Section titled “Related”- Drop capabilities — the other per-container narrowing.
- Rootless mode — note cgroup v2 is required there for limits to work at all.