Namespaces and cgroups: how containers work
A container is not a second computer: it is ordinary processes on the one shared Linux kernel. The kernel gives them a private view with namespaces (their own process IDs, hostname and mounts) and a budget with control groups (cgroups). Build a tiny container by hand: unshare starts bash as PID 1 in new namespaces, then a cgroup caps its memory at 100 MiB and the OOM killer enforces it.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01After unshare -upf --mount-proc bash, the new bash shows PID 1. Why?
The first process in a new PID namespace gets PID 1 there. The host still sees the same process under its own, different PID (4242 in the example).
02In the host terminal, which number do you write into cgroup.procs to move the box's bash?
A PID written to cgroup.procs is read in the writer's PID namespace. The host shell lives in the host's PID namespace, so it uses the host PID.
03Which of these is NOT one of Linux's eight namespace kinds?
The eight kinds are cgroup, ipc, mnt, net, pid, time, user and uts. Limiting CPU time is a job for cgroups, not namespaces: namespaces change what a process sees, cgroups limit what it uses.
04You write 100M to memory.max. What does cat memory.max print?
The suffix M means 1024 × 1024 bytes, so 100M is 100 × 1024 × 1024 = 104857600 bytes, and the file reports bytes.
05A program in the box keeps asking for memory past memory.max. What happens?
memory.max is a hard limit. When usage reaches it and cannot be reduced, the kernel invokes the OOM killer within that cgroup; processes outside it are not touched.
Glossary
- Container
- One or more ordinary processes that the kernel isolates with namespaces and limits with cgroups. They share the host's kernel.
- Namespace
- A kernel feature that gives processes their own copy of one global resource, such as process IDs, the hostname or mount points. Linux has 8 kinds.
- PID namespace
- A separate set of process IDs. Its first process is PID 1 inside it, and also has a different PID on the host.
- UTS namespace
- A namespace for the hostname (and NIS domain name), so a container can have its own name.
- unshare
- A command (and system call) that moves the calling process into new namespaces. A new PID namespace only applies to the caller's children, so -f forks a child that becomes the first process inside.
- cgroup
- Control group: a set of processes whose CPU, memory and I/O use the kernel counts and can limit. Version 2 is a tree of folders at /sys/fs/cgroup.
- memory.max
- A cgroup file holding the hard memory limit in bytes; max means no limit.
- OOM killer
- The kernel's out-of-memory killer: when memory cannot be freed, it stops a process to recover memory, inside the cgroup that hit its limit.
Notes and sources
- Run these as root (sudo -i) in a throwaway virtual machine, not on a server you care about. To clean up: type exit in the box shell, then rmdir /sys/fs/cgroup/box in the host terminal (it must be empty).
- The host PID 4242 and the hostname lab are examples. Find bash's real host PID with lsns -t pid or ps -ef on the host. Inside the box, PIDs count up from 1: hostname ran as PID 2, so ps got 3 (checked on Debian 12). If your shell's start-up files run programs, ps gets a higher number. Column spacing differs between ps versions.
- If memory.max is missing, the memory controller is not switched on for children of the root: echo +memory > /sys/fs/cgroup/cgroup.subtree_control. Current Fedora, Debian and Ubuntu use cgroup v2 at /sys/fs/cgroup.
- When a process moves to a new cgroup, the memory it already uses stays counted in its old cgroup; only new memory is charged to box. The OOM killer picks a process inside the cgroup that hit its limit. If the machine has swap, the kernel may swap the box's memory out first; echo 0 > memory.swap.max makes the limit bite at once.
- Real container tools also add a user namespace (root in the box is not root on the host), a network namespace, seccomp filters and capabilities. Namespaces and cgroups are the two kernel features underneath.
- Linux man-pages: namespaces(7), overview of Linux namespaces
- Linux man-pages: pid_namespaces(7)
- Linux man-pages: uts_namespaces(7)
- Linux man-pages: mount_namespaces(7)
- Linux man-pages: unshare(1)
- Linux man-pages: unshare(2)
- Linux man-pages: proc(5), the /proc file system
- Linux man-pages: lsns(8), list namespaces
- Linux man-pages: cgroups(7), Linux control groups
- The Linux kernel documentation: Control Group v2
- The Linux kernel documentation: kernel parameters (size suffixes K, M, G)
- Docker docs: Docker Engine security (namespaces and control groups)
- Podman docs: podman-run(1)
Quiz Unlocks after the last step.