What "root in a container" really is
UID 0, and why it reaches the host kernel.
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.
# 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.)
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.