14.2 Namespaces, cgroups, and Container Isolation
In the Earth-Centered Stack House, there are two kinds of "duplicate selves." Virtual machines make residents believe they own their own hardware and kernel; containers don't copy the foundation, they still have the host kernel scheduling regular processes, only changing what those processes can see and how much resources they can use.
Namespaces provide isolation, cgroups organize, measure, and limit resources, and features like images, root filesystem, capabilities, seccomp, networking, and lifecycle management complete the "container" product abstraction. Just keep one clear, non-misleading distinction:
VM provides workloads with virtual hardware and a separate Guest kernel; Linux containers provide processes with isolated views and resource boundaries.
1. Namespace Changes What You See
Current common namespace types in Linux include:
| Type | clone/unshare Flag | Primary Isolated View |
|---|---|---|
| Mount | CLONE_NEWNS | Mount Point |
| PID | CLONE_NEWPID | Process ID |
| Network | CLONE_NEWNET | Network devices, protocol stack, ports, and routing |
| UTS | CLONE_NEWUTS | hostname and NIS domain name |
| IPC | CLONE_NEWIPC | System V IPC and POSIX Message Queues |
| User | CLONE_NEWUSER | UID, GID, and capabilities |
| Cgroup | CLONE_NEWCGROUP | cgroup path view |
| Time | CLONE_NEWTIME | Partial System Clock |
Namespaces are composable. Creating only a PID namespace does not automatically isolate network, mount points, or user identity. A container runtime must explicitly create which namespaces and which resources are intentionally shared with the host or other containers.
First, observe the current process
/proc/PID/ns Handle exposing the namespace of the process:
ls -l /proc/self/ns
readlink /proc/self/ns/pid
readlink /proc/1/ns/pidIf two processes' type-specific links show the same namespace identifier, they belong to the same namespace instance. File descriptors obtained by opening these special files can keep the namespace alive and be available for setns to join.
clone can create a namespace alongside a child process, unshare allows the calling process to enter a new namespace, and setns enables joining an existing namespace. Permission checks are closely tied to user namespaces, and it's not assumed that any ordinary process can create all types.
2. A PID namespace Goes Beyond changing numbers
In a nested PID namespace, the same process can have different PIDs. The PID 1 seen in the inner namespace might be 18342 in the parent namespace:
Host PID namespace: container init = 18342
Container PID namespace: container init = 1PID 1 has special responsibilities. It should reclaim orphaned child processes and properly forward or handle termination signals. If a business application directly becomes PID 1 in a container but never wait child processes, zombies will accumulate within the container; ignoring signal semantics will also make stops and rolling updates sluggish.
A PID namespace only changes process numbering and visibility; it does not prevent kernel vulnerabilities on the same host from crossing boundaries, nor does it automatically limit the number of processes that can be created; process count restrictions should be enforced using mechanisms like the pids controller.
3. Mount namespace and the root filesystem
A mount namespace provides a group of processes with an isolated view of mount points. Container runtime typically also:
- Prepare the root filesystem after image expansion;
- Set mount propagation properties;
- Mount
/proc, essential devices, and temporary file systems; - Use
pivot_rootor an equivalent mechanism to switch the root; - Add a read-only layer, volume, and bind mount as configured;
- Avoid accidentally exposing Host-sensitive paths.
chroot Only changes the root of path resolution, not the complete security boundary. Without mount/user namespace, capability dropping, and other protections, privileged processes can still affect a broader system.
Container image layered filesystems typically allow multiple containers to share read-only layers, adding a writable layer for each container. Copy-on-write saves space, but means that write-intensive workloads like databases should store persistent data in clearly defined volumes rather than relying on short-lived writable layers.
4. Network namespace forms an isolated protocol stack view
A new network namespace can start with just loopback. The runtime typically uses a veth pair to connect it to the host:
container eth0
│
veth pair
│
Host bridge / routing / policy
│
physical networkNetwork namespace isolates interfaces, addresses, routes, and ports, but traffic still ultimately passes through the host network path. Firewalls, eBPF, service proxies, NAT, and CNI plugins may continue to process packets.
"Containers having independent IPs" does not mean they have physical network interfaces, nor does it imply that network policies are automatically secure. Containers sharing the Host's network namespace actually lose their own port space entirely.
5. Changing User Namespace to Alter Identity Mapping
The user namespace allows a process to see UID 0 within the namespace, while still mapping to a regular non-privileged UID in the parent namespace:
inside user namespace: uid 0
maps to
outside on Host: uid 100000The effectiveness of capability relative to user namespace determination means that "root inside a container" does not necessarily equal "root on the host," but misconfigured mappings, exposed devices, or kernel vulnerabilities could still lead to privilege escalation.
Rootless containers reduce the host privileges of the runtime and container processes using user namespaces, providing an important layer of defense in depth. They are also subject to limitations on ports, devices, file ownership, and certain mount operations.
6. cgroup prevents one tenant from filling up the entire engine
Namespaces let residents see different door numbers without restricting their CPU or memory usage. If a layer of workloads continuously requests resources, it might still slow down the entire stack. Cgroups handle the other half of the boundary: organizing, measuring, and enforcing limits.
cgroup organizes processes in a hierarchical structure and provides resource control and accounting via controllers. cgroup namespace only virtualizes the cgroup path as seen by a process; it does not enforce actual resource limits. Do not confuse the two.
Modern Linux commonly uses cgroup v2 for unified hierarchy. Key interfaces include:
| File | Meaning |
|---|---|
cpu.max | Bandwidth quota and cycle, default max |
memory.high | Memory pressure threshold that triggers throttling and forced garbage collection |
memory.max | Primary hard limit; this cgroup's OOM can be triggered if memory cannot be reclaimed |
pids.max | Maximum task count |
io.max | Bandwidth or IOPS limited by device |
cgroup.procs | processes in cgroup |
The format of cpu.max is:
$MAX $PERIODFor example, 200000 100000 means that this cgroup can consume up to 200,000 microseconds of CPU time every 100,000 microsecond period. It allows parallel consumption of this budget, rather than assigning tasks to two specific cores. CPU affinity is a separate mechanism.
memory.high is suitable for allowing workloads to endure recovery pressure before a hard failure; memory.max serves as the primary hard boundary. When usage reaches memory.max and cannot be reduced, the kernel can trigger an OOM within that cgroup. Temporary overruns and certain special allocations still have exceptions; monitoring should not solely check whether a process is immediately killed.
7. Resource limits don't guarantee performance
Setting "2 CPUs, 1 GiB" to a container still has many unaddressed issues:
- Is CPU about quota, weight, or affinity?
- Do other tasks on the same host contend for shared cache, memory bandwidth, and I/O?
- Does the memory limit include page cache and socket memory?
- Is swapping allowed?
- What physical device does I/O limitation apply to?
- Does NUMA placement match CPU sets?
CPU quotas can prevent a tenant from monopolizing the entire machine for extended periods, but during short bursts of activity they might still cause throttling and sharp tail latency spikes. Memory limits can cap usage, but they can't guarantee that a requested amount will be allocated. SLOs require monitoring both cgroup metrics, host load, and application queue depth.
8. Container Security Is a Multi-Layer Constraint
A namespace is not a fully secure sandbox. A more reliable configuration would combine:
- Non-root users or rootless mode;
- Remove unnecessary Linux capabilities;
no_new_privs;- seccomp limits the system call interface;
- SELinux, AppArmor, and other LSM policies;
- Read-only root filesystem and minimal mount;
- Doesn't expose the Host PID/network namespace;
- Not mounting the container runtime socket;
- Image source, signing, scanning, and minimal dependencies;
- Promptly patch the shared Host kernel.
Mounting the management socket of Docker or another runtime into a container typically grants it the ability to create high-privilege workloads. Even if the container process itself appears to have limited permissions, the management interface expands its attack surface.
Security choices can't be made by simply comparing how difficult "container escape" and "VM escape" are. Threat modeling also includes the control plane, image supply chains, virtual devices, shared hardware, and credentials.
9. From Image to Process
A container startup can be summed up as:
image manifest + layers + runtime config
↓
Prepare rootfs and mount it
↓
Create namespace / cgroup
↓
Set UID, capabilities, seccomp, LSM
↓
Execute the container entrypoint processImages are not running containers, and containers are not natural boundaries for long-term data persistence. Images describe read-only content and configuration; runtime adds a writable layer, network settings, volumes, secrets, and resource limits.
The orchestration system must also handle restarts, probes, rolling updates, service discovery, and scheduling. Namespaces and cgroups provide mechanisms, but do not replace applications in determining health status or data recovery.
10. Common Misconceptions
"Seeing PID 1 inside a container means it's a standalone machine." This is just the view from PID namespace; the kernel is still shared with the host.
"The namespace handles CPU and memory limits." Limits are primarily provided by the cgroup controller.
"cgroup namespace creates new resource quotas." It changes the cgroup path view.
"Container images include the kernel." Ordinary Linux container images provide user space, and system calls are handled by the host kernel.
"Setting memory.max does not affect the Host." The container still shares the kernel and the physical memory system, so memory recycling and I/O contention can impact the entire host.
"Rootless doesn't require other protections." It reduces host privileges but doesn't eliminate kernel, configuration, or supply chain risks.
11. Summary
Linux containers are a combination of various kernel mechanisms:
- Isolate PID, mount, network, and identity views using namespaces;
- cgroup organizes, tracks, and controls CPU, memory, I/O, and the number of tasks;
- rootfs and the image provide user-space files;
- Reduce the attack surface of the shared kernel with capabilities, seccomp, and LSM;
- The runtime and orchestration layer manage creation, termination, networking, and persistent data.
The next section returns to the VM boundary via Minimal KVM Experiment. If you're only interested in system principles, jump directly to Chapter 15: CPU Pipeline.