What "root in a container" really is

UID 0, and why it reaches the host kernel.

Advanced12 min · lesson 4 of 25

The most consequential fact in container security: by default, root inside a container is root on the host kernel. There is no translation layer unless you add one — UID 0 in the container is UID 0 the kernel enforces file permissions and capability checks against. A process that breaks out of a container running as root breaks out as root; the same escape from a non-root container lands as an unprivileged user with far less to work with.

terminal
# a container running as root, touching a host-mounted file, writes as host root
$ docker run --rm -v /tmp/host:/mnt alpine sh -c 'id; touch /mnt/owned; ls -l /mnt/owned'
uid=0(root) gid=0(root)
-rw-r--r-- 1 root root 0 ... /mnt/owned # owned by REAL root on the host

User namespaces break the equivalence

The clean fix is a user namespace, which maps the container’s UID range to a different, unprivileged range on the host: container root (0) becomes, say, host UID 100000 — full power inside its own namespace, an ordinary unprivileged user everywhere that matters. This is the single strongest mitigation for the root-equals-root problem, and it is what rootless Docker and Podman lean on. (It is covered in depth in the daemon-hardening section.)

With vs without a user namespace
1container UID 0
the process thinks it is root
2no userns
→ host UID 0 (real root)
3with userns
→ host UID 100000
4escape lands as
unprivileged, not root
The mapping is the whole difference between a container breakout owning the host and merely being an unprivileged nuisance.

So the rule writes itself

Because root-in-container is a live risk, the baseline for every image is to not run as root at all: create a dedicated non-root user in the Dockerfile and USER to it, or enforce runAsNonRoot at the platform. Then, where the threat model warrants, add a user namespace so that even a container that must do something as “root” is not the host’s root. Non-root first, user namespace second — the next section makes both concrete.

This is why "just run as root" is never fine
Every “it only works as root” shortcut is a decision to make a container breakout equal a host takeover. Almost no application genuinely needs container root at runtime; the ones that seem to usually need one capability instead. Treat root-in-container as a finding to fix, not a default to accept.