Skip to main content
RunBook Academy

LinuxLXXVIII · Containers from the Linux PerspectivePrimitives

Container primitives - namespaces, cgroups, OverlayFS, capabilities

Advanced⏱ ~12 minbashunshare

What you'll learn

  • Explain namespaces, cgroups, OverlayFS, capabilities
  • Describe how containers are built from primitives
  • Recognise the role of each primitive
  • Apply the primitives for debugging

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

Containers are built on Linux primitives: namespaces for isolation, cgroups for resources, OverlayFS for layers, and capabilities for permissions. This lesson covers each primitive and how they work together.

Namespaces

Namespaces provide isolation: each namespace has its own view of a global resource.

  • PID namespace: own set of PIDs. Container cannot see host processes.
  • Network namespace: own network stack. Container has its own interfaces, routes, firewall.
  • Mount namespace: own mount points. Container cannot see host filesystem.
  • UTS namespace: own hostname and domain.
  • IPC namespace: own IPC facilities.
  • User namespace: own UID/GID mappings.
  • Time namespace: own time (for clock skew).

A container is a collection of namespaces. The host sees the container as a process tree; the container sees itself as the only process.

cgroups

cgroups control resources: CPU, memory, I/O, network, processes.

A container has its own cgroup. The cgroup sets the limits:

  • CPU quota: how much CPU time per period.
  • Memory limit: maximum RSS.
  • I/O weight: priority for I/O.
  • Process count: maximum PIDs.

When the container exceeds a limit, the kernel enforces it (throttles, OOMs, etc.). cgroups v2 is the modern unified hierarchy.

OverlayFS

OverlayFS is a union filesystem: multiple layers stacked together.

There are four paths, matching the mount syntax:

mount -t overlay overlay \
  -o lowerdir=/lower,upperdir=/upper,workdir=/work \
  /merged
  • lowerdir: the read-only image layers, stacked.
  • upperdir: the read-write layer where the container’s changes land.
  • workdir: an empty scratch directory on the same filesystem as upperdir, which the kernel uses to stage copy-up and rename operations atomically. man 8 mount: “The workdir needs to be an empty directory on the same filesystem as upperdir.” It never holds anything you can use — do not go looking there for a container’s files.
  • the mount point (/merged above): the merged view, and the thing the container sees as /.

When a container reads a file, OverlayFS checks the upper dir first, then the lower dir. When a container writes, the change goes to the upper dir.

This is how Docker images work: each layer is a diff; the container is the merged view.

Copy-up, whiteouts, and opaque directories

Three behaviours follow from that structure, and each one surprises somebody in production.

Copy-up. A file that exists only in lowerdir cannot be modified in place, because the lower layers are read-only. The first write copies the entire file to upperdir first, then applies the change. Appending one line to a 10 GB log that came from the image consumes 10 GB in the container layer, and the pause while the copy runs is real. This is the mechanism behind “the container filled the disk and I only wrote a few kilobytes”, and it is the reason large mutable files belong on a volume rather than in an image layer.

Whiteouts. Deleting a file that lives in lowerdir cannot remove it either. OverlayFS records the deletion by creating a character device with major and minor 0/0 at that path in upperdir. The merged view then hides the file. The consequence people care about: rm inside a container frees no space — it adds a marker. A RUN rm -rf in a Dockerfile that follows the RUN which created the files leaves both layers in the image.

Opaque directories. When a whole directory is replaced rather than merged, OverlayFS sets the trusted.overlay.opaque="y" xattr on the upper directory, which tells the kernel to stop looking at the lower layers for that path.

Capabilities

Linux capabilities split root’s power into discrete pieces. A container runs with a restricted capability set:

  • Allowed: CHOWN, SETUID, NET_BIND_SERVICE (for binding ports <1024).
  • Denied: SYS_ADMIN, SYS_MODULE, raw I/O, mount, etc.

A container with no capabilities is a non-root user inside the kernel. Even if a process escapes the container, it cannot mount filesystems or load modules.

Putting it together

A container is:

  • A process tree in its own PID namespace.
  • A network stack in its own network namespace.
  • A filesystem view in its own mount namespace.
  • Resource limits in its own cgroup.
  • A restricted capability set.
  • An OverlayFS image as the root filesystem.

The host kernel enforces all of these. The container runtime (Docker, containerd) sets them up.

Debugging containers

For debugging, use the host tools to see inside the container’s namespace:

# Enter the container\'s namespace
nsenter -t <pid> -m -u -i -n -p -- /bin/bash

# View the container\'s filesystem
ls /proc/<pid>/root

# View the container\'s network
cat /proc/<pid>/net/tcp

Tools like nsenter, crictl, and ctr let you inspect containers without entering them.

Knowledge check

Knowledge check · 3 questions

  1. Q1. What is the role of namespaces in containers?

  2. Q2. By default, a container process running as UID 0 is UID 0 to the host kernel as well.

  3. Q3. Which of the following are container primitives? Select all that apply.

Passing score: 75%. Answers are checked in this browser.