The bug-reference lesson later in this domain shows OOMKilled as a symptom — exit code 137, silent logs. This lesson is the mechanism underneath it: what a `--memory` or `--cpus` flag actually does to a container, using the exact same Linux kernel feature Operating Systems Fundamentals' process-isolation content is built on.
4 min read
A container isn't a lightweight virtual machine — it's an ordinary Linux
process, isolated from others on the same machine using kernel features,
not separate hardware. Two of those features do almost all the work:
namespaces (what a process can see — its own filesystem view,
network stack, process list) and cgroups (what a process can
consume — CPU, memory, I/O). Docker's --memory and --cpus flags are
a friendly interface directly on top of cgroups; there's no separate
Docker-specific enforcement mechanism underneath them.
docker run --memory=512m my-app:latestThis creates a cgroup capping the container's memory at 512MB. If the
container's total memory usage tries to exceed that limit, the kernel's
OOM (out-of-memory) killer steps in and sends SIGKILL to the
offending process — not a graceful shutdown, not a chance to log an error,
just immediate termination. This is precisely the mechanism behind the
OOMKilled / exit code 137 bug covered later in this domain: the
container didn't crash on its own, the kernel enforced a limit that had
already been exceeded.
A memory limit set below what the application, its runtime (the Node.js
heap, a JVM's allocated heap, a Python interpreter), and any child
processes genuinely need together will trigger OOM kills even though
nothing is actually leaking memory — it's just undersized relative to
real requirements. Setting a limit by copying a number from a blog post,
rather than measuring actual usage under real load (docker stats, or a
proper load test), is one of the most common causes of mysterious,
seemingly random OOMKilled events in production.
CPU is handled differently from memory, because CPU is a rate, not a fixed pool — a container can't simply be capped at "500 CPU-things" the way it's capped at 512MB.
docker run --cpus="1.5" my-app:latest--cpus="1.5" limits the container to at most 1.5 CPU cores' worth of
processing time, enforced via the cgroup CPU quota mechanism — the
kernel tracks how much CPU time the container has used within a fixed
period and throttles it once the quota for that period is exhausted, even
if other cores sit idle. This is distinct from CPU shares
(--cpu-shares), which don't cap usage at all — they only set relative
priority when multiple containers compete for the same CPU under
contention, doing nothing when the machine isn't actually busy.
A container hitting its CPU quota doesn't crash or log an error the way an
OOM kill does — it just runs slower, with requests taking longer to
complete, for no reason visible in the application's own logs.
docker stats or, on a real host, checking
/sys/fs/cgroup/cpu.stat's nr_throttled count directly confirms whether
a container has actually been throttled — a genuinely different diagnosis
than "the code got slower" or "the database is the bottleneck," and one
easy to miss without knowing to look for it specifically.
Older Linux distributions use cgroups v1; newer ones (and current
Docker defaults on recent kernels) use cgroups v2, which
consolidates several separate v1 hierarchies into one unified structure
and changes some underlying accounting behavior. This is rarely visible
day-to-day, but it explains occasional platform-specific reports of
identical --memory/--cpus flags behaving slightly differently between
a developer's machine and a production host running a different kernel
version — worth knowing exists as a real variable, without needing to
memorize the full implementation differences.
Kubernetes' resources.requests and resources.limits (covered in the
Kubernetes domain) are the exact same cgroup mechanism, one layer up —
Kubernetes translates a Pod's resource spec into the same --memory and
CPU quota settings this lesson describes, applied to the container(s)
inside that Pod. Understanding what's actually happening at the Docker
level here is what makes a Kubernetes OOMKilled event or CPU-throttling
mystery diagnosable instead of opaque.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What Linux kernel feature do Docker's --memory and --cpus flags actually configure?
2. What happens when a container's memory usage exceeds its configured cgroup limit?
3. What's the difference between CPU shares and a CPU quota (--cpus)?
4. Why might CPU throttling be mistaken for a code performance bug?
Docker & Containers