Articles

Cgroups and Namespaces: How Containers Actually Work

Linux namespaces isolate what a process can see; cgroups limit what it can use. Together they're the kernel primitives that make a container a container.

Chisato Chisato · · 4 min read
Server racks with network cabling

Cgroups and namespaces are the two Linux kernel features that make containers possible: namespaces control what a process can see — its own process list, filesystem, network interfaces — and cgroups control what it can use — how much CPU, memory, and I/O it’s allowed to consume. Neither one is a container by itself. A container runtime like Docker or containerd is really just a program that wires these two primitives together around an ordinary process and calls the result “isolated.”

Namespaces: what a process can see

A namespace wraps a global kernel resource so that processes inside the namespace see their own private view of it, while processes outside see the normal, unwrapped version. Linux provides several kinds:

  • PID namespace — processes inside see their own process ID tree, starting at PID 1, unaware of any processes running outside the namespace.
  • Network namespace — its own network interfaces, routing table, and port space, so a container can bind to port 80 without conflicting with the host’s own port 80.
  • Mount namespace — its own view of the filesystem hierarchy, which is what lets a container have a root filesystem completely different from the host’s.
  • UTS namespace — its own hostname, independent of the host machine’s.
  • IPC namespace — its own inter-process communication resources (shared memory, message queues), isolated from the host’s.
  • User namespace — lets a process appear to run as root inside the namespace while actually mapping to an unprivileged user on the host, narrowing what damage a compromised “root” process inside a container can actually do.

A process can be placed in a mix of these namespaces independently — a container typically gets all of them, but the kernel doesn’t require that.

Cgroups: what a process can use

Where namespaces are about visibility, control groups (cgroups) are about resource accounting and limits. A cgroup can cap how much CPU time a group of processes gets, how much memory they can allocate before being throttled or killed, how much disk and network I/O bandwidth they can consume, and how many total processes they’re allowed to spawn.

This is what stops one noisy container from starving every other workload on the same host. Without cgroup limits, a memory leak or a runaway loop in one container could consume all available RAM or CPU on the machine, taking down unrelated workloads that happen to share the host. Kubernetes resource requests and limits are, under the hood, translated directly into cgroup configuration on each node.

Putting them together: a container is just a process

Run docker run and the runtime does roughly this: it creates a new set of namespaces, sets up a cgroup with the requested resource limits, mounts the container image’s filesystem into the new mount namespace, and then executes the requested command inside that combination. From the kernel’s point of view, there’s no special “container” object — just a regular process (or process tree) that happens to be namespaced and cgrouped. Tools like docker ps are essentially inspecting that same process tree with extra bookkeeping layered on top.

Namespaces vs cgroups

NamespacesCgroups
ControlsVisibility / isolationResource limits
Answers”What can this process see?""How much can this process use?”
ExamplesPID, network, mount, userCPU, memory, I/O, process count
Failure mode if missingProcesses can see and interfere with each otherOne process can starve the whole host

Why this matters for security

Because every container on a host shares the same kernel, namespaces and cgroups are the only thing standing between one container and another — there’s no hypervisor boundary underneath, unlike a virtual machine. A kernel bug that lets a process escape its namespace, or a misconfiguration that leaves a container running with excessive capabilities, can turn into a container escape that reaches the host or a sibling container directly.

This is also why user namespaces matter more than they might seem to: without one, a process running as root inside a container really is root as far as the kernel is concerned if it manages to break out, whereas a properly configured user namespace maps that “root” to an unprivileged host user, containing the blast radius of an escape. Understanding this layered model is also why NUMA and other hardware-adjacent concepts matter for containerized workloads — cgroup CPU limits interact with how the scheduler places processes across physical cores and memory nodes.

The takeaway

A container isn’t a kernel-level object — it’s an ordinary process wrapped in namespaces for isolation and a cgroup for resource limits. Namespaces answer what the process can see; cgroups answer what it can use. Every container runtime, from Docker to the one Kubernetes uses on each node, is ultimately just orchestrating these two primitives, which is also why the shared kernel underneath them is the actual security boundary worth taking seriously.

Chisato Chisato · · 4 min read

Virtual Machines vs Containers: Key Differences

VMs virtualize hardware with a full guest OS per instance; containers share the host kernel and isolate processes. What that trade-off costs and buys you.

#Cloud #DevOps #Containers
Chisato Chisato · · 4 min read

Docker vs Podman: Container Runtimes Compared

Docker runs containers through a persistent root daemon; Podman runs them daemonless and rootless by default. What that architectural split changes.

#Docker #DevOps #Containers