Concept · Running systems

Namespaces and cgroups

A container is a normal Linux process with a restricted view (namespaces) and a budget (cgroups).

3 mincontainerscgroupsLinux namespacescontainer escape

A container isn't a small virtual machine. It's an ordinary Linux process that the kernel shows a restricted view of the system and puts on a resource budget. Two kernel features do most of the work.

Namespaces: what the process can see

Each namespace type gives the process its own private copy of one kind of system resource:

NamespaceIsolates
pidProcess IDs: the container sees itself as PID 1 and can't see host processes
mntMount points: its own root filesystem (the image)
netNetwork interfaces, IPs, ports, routing tables
utsHostname
ipcShared memory and message queues
userUser IDs: root inside can map to an unprivileged user outside
cgroupIts view of the cgroup tree
bash
docker run --rm alpine ps      # shows only the container's processes

cgroups: what the process can use

Control groups limit and account for CPU, memory, I/O and number of processes:

yaml
resources:
  requests: { cpu: 250m, memory: 256Mi }   # scheduling guarantee
  limits:   { cpu: "1",  memory: 512Mi }   # hard ceiling
  • Over the memory limit, the process is OOM-killed.
  • Over the CPU limit, it's throttled, which shows up as latency spikes, not errors.
  • A pids limit stops a fork bomb from taking down the node.

Language runtimes have to read these limits. Go reads cgroup CPU limits for GOMAXPROCS from Go 1.25, and GOMEMLIMIT should be set a little below the memory limit.

The security boundary is thinner than a VM's

All containers on a host share one kernel. A kernel vulnerability, or a container given too much privilege, can lead to an escape onto the host. The defaults are reasonable, and most hardening is about not undoing them:

  • Don't run as root. Set USER in the image and runAsNonRoot: true. Use user namespaces where available.
  • Drop Linux capabilities (drop: [ALL]) and add back only what's needed.
  • Never use --privileged or mount the Docker socket or host paths into an application container. Either one is effectively root on the host.
  • Keep seccomp and AppArmor/SELinux profiles on, with RuntimeDefault at minimum, and use a read-only root filesystem.
  • For untrusted code, add a stronger boundary: gVisor, Kata Containers or Firecracker microVMs.

All of this is least privilege applied to a process. The container security lesson adds image scanning and minimal base images.

Learn it in depth