How do Linux namespaces and cgroups provide container isolation?
Quick Answer
Namespaces control what a process can see — each namespace type (PID, network, mount, UTS, IPC, user) gives a container its own isolated view of processes, network interfaces, filesystem mounts, hostname, and more, so it appears to have the machine to itself even though it's really sharing the host's single kernel with other containers. cgroups (control groups) control what a process can use — limiting and accounting for CPU, memory, disk I/O, and other resources, so one container can't starve others sharing the same host. Together, namespaces provide isolation and cgroups provide resource control — the two complementary kernel mechanisms that make a container a container.
Detailed Answer
Namespaces — controlling what a process can see
A Linux namespace wraps a global system resource so that processes inside the namespace see their own isolated instance of it, while processes outside see the normal, unwrapped resource (or a different namespace's instance entirely).
| Namespace | What it isolates |
|---|---|
| PID | Process IDs — a container's process sees itself as PID 1, unaware of any other processes running on the host or in other containers |
| NET | Network interfaces, IP addresses, routing tables, ports — a container gets its own virtual network stack, distinct from the host's |
| MNT | Filesystem mount points — a container sees only its own filesystem view (its image's layers plus any mounted volumes), not the host's real filesystem |
| UTS | Hostname and domain name — a container can have its own hostname, independent of the host machine's |
| IPC | Inter-process communication resources (shared memory, semaphores) — prevents one container's IPC objects from being visible to or colliding with another's |
| USER | User and group IDs — lets a process be root inside the container's namespace while mapping to an unprivileged, non-root user on the actual host, reducing the impact of a container escape |
# Inside a container, the container's own main process appears as PID 1
docker exec my-container ps aux
# PID USER COMMAND
# 1 root node server.js <- this process's REAL host PID might be, say, 48213
This is why a container "sees" only its own processes, its own network configuration, and its own filesystem. This gives the strong illusion of running on a dedicated machine, even though it's really just an ordinarily-scheduled process on the shared host, viewed through a namespace-restricted lens.
cgroups — controlling what a process can use
Namespaces control visibility. cgroups control resource consumption: how much CPU, memory, disk I/O, and network bandwidth a process (or group of processes) is allowed to use. cgroups also provide accounting and metrics for actual usage.
docker run --memory="512m" --cpus="1.5" myapp:1.0
This translates directly into cgroup configuration. The kernel's cgroup subsystem enforces that this container's processes can never allocate more than 512MB of memory (triggering an OOM kill if exceeded — the same underlying mechanism covered in the Kubernetes stack's OOMKilled question). It also caps the container at 1.5 CPU cores' worth of scheduling time, regardless of how much the host machine actually has available.
Why both are needed together
Namespaces alone would let a container see only itself. But without cgroups, nothing would stop that container from consuming all of the host's CPU or memory, starving every other container sharing the machine. Isolation of view without control of consumption isn't enough for a genuinely multi-tenant host. Conversely, cgroups alone (limiting resource usage) without namespaces would still let one container's processes see and potentially interfere with every other process on the host. Together, namespaces provide the illusion of a dedicated machine, and cgroups provide the guarantee that one tenant can't monopolize the shared machine's real resources.
Why this matters beyond trivia
Understanding that "a container" is really just an ordinary Linux process — made to look isolated via namespaces and made resource-bounded via cgroups, not some fundamentally different kind of virtualized entity — explains a lot of otherwise-surprising container behavior. It explains why docker top can show container processes' real host PIDs. It explains why a container "escape" vulnerability is fundamentally about breaking out of namespace/cgroup confinement rather than "hacking a virtual machine." And it explains why Kubernetes's resource requests/limits (see that stack) map directly onto these exact same underlying cgroup mechanisms.