Skip to main content
RunBook Academy

LinuxLXXVIII · Containers from the Linux PerspectiveBoundaries

What containers do not isolate

Advanced⏱ ~17 minutil-linux

What you'll learn

  • List what namespaces do not cover and why
  • Explain why procfs reports host resources inside a container
  • Read the real resource limits of a container from the cgroup filesystem
  • Choose the right signal for a container under pressure

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-11

Not yet marked complete on this device.

Namespaces are an allow-list, not a boundary. The kernel virtualises a specific set of resources and shares everything else, and the things it shares are not obvious from the outside - which is how a container ends up sizing its thread pool from somebody else’s CPU count.

This lesson is the other half of linux-container-primitives: what the primitives do not do.

One kernel

Every container on a host runs on the same kernel, executing the same code, through the same syscall table. Nothing about a namespace changes that.

The consequences are structural:

  • A kernel vulnerability is a fleet vulnerability. A local privilege escalation reachable from an unprivileged process is reachable from inside every container on every host running that kernel. Container images are irrelevant to it, and patching the images does nothing.
  • A kernel panic takes every container with it. There is no per-container failure domain below the host.
  • Kernel tunables are host state. A container that could set a sysctl would be setting it for its neighbours.

Seccomp narrows which syscalls a container may reach, and an LSM profile narrows what it may do with them. Both reduce the reachable surface. Neither creates a second kernel, which is the line between a container and a virtual machine and the reason genuinely hostile multi-tenant workloads still get a hypervisor or a sandboxed runtime.

Which sysctls are namespaced

Some of /proc/sys follows a namespace. Most does not, and the split is worth knowing because it decides which knobs a container may be given.

AreaNamespaced byNotes
net.*Network namespaceEach netns has its own full network stack tunables
kernel.hostname, kernel.domainnameUTS namespaceThis is all the UTS namespace covers
kernel.msgmax, kernel.sem, kernel.shm*IPC namespaceSysV IPC limits
fs.mqueue.*IPC namespacePOSIX message queues
user.max_*_namespacesUser namespaceNesting limits
Everything elseNothingvm.*, fs.file-max, kernel.pid_max, module parameters

So net.core.somaxconn can safely be set per container - it lives in the network namespace. vm.max_map_count, which several databases and search engines require raised, cannot: it is host-wide, and the container asking for it is asking you to change the host. That is a legitimate request, but it belongs in the host configuration management, not in the container spec.

Not namespaced at all

  • /sys. sysfs presents host hardware. A container reading /sys/class/thermal or /sys/block sees the machine, not itself. The one partial exception is /sys/class/net, which follows the network namespace.
  • The kernel log ring buffer. dmesg is shared. Whether a container can read it is governed by kernel.dmesg_restrict and CAP_SYSLOG, not by a namespace - and the ring buffer contains messages about every container on the host.
  • The wall clock. The time namespace virtualises CLOCK_MONOTONIC and CLOCK_BOOTTIME only. CLOCK_REALTIME is the host’s, always. A container cannot have its own date, and date -s inside a privileged container sets the host clock - which is a genuinely dangerous thing to discover by accident on a cluster with a certificate expiry.
  • Devices. /dev inside a container is a small tmpfs the runtime populates. Access is governed by the cgroup device controller and the capability set, not by a namespace. There is no “device namespace”.
  • Entropy and the random pool. Shared.

The resource visibility problem

This is the one that costs real money, and it has a simple cause: procfs predates cgroups and was never made cgroup-aware.

/proc/cpuinfo, /proc/meminfo, /proc/loadavg and /proc/uptime are per-namespace only insofar as the PID namespace hides other processes. The numbers in them describe the host. Inside a container limited to 2 CPUs and 512 MB, the tools everybody uses report something else entirely:

Read-only / Safe
$ nproc
12
Read-only / Safe
$ head -3 /proc/meminfo
MemTotal:       48276768 kB
MemFree:        40395356 kB
MemAvailable:   45110836 kB

That is the host: 12 CPUs and 48 GB. The container’s actual limits live somewhere else entirely - in the cgroup:

# The container's own cgroup, from inside it
cat /proc/self/cgroup

# The limits that will actually be enforced
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.high
cat /sys/fs/cgroup/cpu.max
Read-only / Safe
$ cat /sys/fs/cgroup/user.slice/cpu.max
max 100000

cpu.max is two numbers: quota and period, both in microseconds. max 100000 means no quota. 200000 100000 means two CPUs’ worth of runtime per 100 ms period - which is what a “2 CPU” container limit actually is. Note what it is not: it is not two specific cores, and the process will still see twelve in /proc/cpuinfo and may still run on any of them.

Load average lies too

/proc/loadavg is host-wide. A container reporting a load average of 40 is reporting the host’s load average of 40, contributed to by every other container on the machine.

Read-only / Safe
$ cat /proc/loadavg
0.71 1.00 0.98 1/702 3146816

The fourth field - running/total tasks - is namespaced, because it counts processes and the PID namespace hides the rest. The three load figures are not.

The correct per-container signal is pressure stall information, which cgroup v2 accounts per cgroup:

Read-only / Safe
$ cat /sys/fs/cgroup/user.slice/cpu.pressure
some avg10=0.00 avg60=0.00 avg300=0.00 total=422673330
full avg10=0.00 avg60=0.00 avg300=0.00 total=391053555

some is the share of time at least one task in the cgroup was stalled waiting for the resource; full is the share where every task was. Unlike load average these are percentages, they are bounded, and they describe this cgroup. memory.pressure and io.pressure sit beside it.

If you monitor one thing per container, monitor pressure rather than utilisation. Utilisation tells you a limit is being used; pressure tells you it is hurting.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A container is limited to 512 MB. Inside it, free reports 48 GB and the application sizes its cache accordingly, then dies with exit code 137. Why does free report the host figure?

  2. Q2. Which of these are shared between a container and its host? Select all that apply.

  3. Q3. A database container needs vm.max_map_count raised. Where does that setting belong?

  4. Q4. The load average reported inside a container reflects only that container workload.

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