User Namespaces in Kubernetes: A Guide to Running Rootless Pods Without Breaking the Cluster
UID/GID isolation between container and host has been stable since 1.36. See the kernel, filesystem, and runtime prerequisites, plus a checklist of what breaks in volumes and security policies before turning on hostUsers: false.

A pauta que me chegou falava em "beta no 1.37", mas a documentação oficial deixa claro que o barco já passou: User Namespaces has been stable since Kubernetes v1.36, available since v1.28 as alpha. The UserNamespacesSupport feature gate is locked. If you set an explicit value, Kubernetes ignores it without complaint. In other words: there's nothing left to "enable" via feature gate. The real work now is preparing the node and migrating workloads without breaking volumes, sidecars, or security policies.
I'll treat this for what it is: a security feature you should be planning to adopt, with concrete kernel and filesystem trade-offs.
What User Namespaces Solve
The idea is to map the user inside the container to a different user on the host. A process running as root inside the container is, in practice, an unprivileged user on the host. It has full privilege inside the namespace and none outside it.
The gain is measurable in attack surface. The docs cite several vulnerabilities rated HIGH or CRITICAL that were not exploitable with user namespaces enabled. Two concrete examples of how capabilities lose effect outside the namespace:
CAP_SYS_MODULEhas no effect at all: the pod can't load kernel modules.CAP_SYS_ADMINis limited to the pod's user namespace and is invalid outside of it.
The docs also reference CVE-2021-25741, in which a pod could read arbitrary files on the host. With the pod's UIDs/GIDs not overlapping with the host's, the file's owner/group simply doesn't match, and the damage stays contained.
Prerequisites: Where Most People Trip Up
This is the point that separates "works on my kind cluster" from "works across the production fleet." It's a Linux-only feature and depends on idmap mounts support in the filesystems used.
In practice you need at least Linux kernel 6.3, because that's the version in which tmpfs started supporting idmap mounts, and Kubernetes uses tmpfs all the time (the service account token mounted by default uses tmpfs, Secrets use tmpfs). Filesystems that support idmap mounts as of 6.3: btrfs, ext4, xfs, fat, tmpfs, and overlayfs.
The runtime stack also needs to keep up:
| Component | Minimum version | |---|---| | OCI runtime crun | 1.9 (1.13+ recommended) | | OCI runtime runc | 1.2 | | containerd (CRI) | 2.0 | | CRI-O (CRI) | 1.25 |
Notice the jump to containerd 2.0: a lot of people are still running the 1.x series. Before any testing, the node's containerd --version needs to be on the upgrade plan. cri-dockerd still has support open, tracked in a GitHub issue.
How a Pod Enters Rootless Mode
The opt-in is a single line in the spec: setting pod.spec.hostUsers to false.
apiVersion: v1
kind: Pod
metadata:
name: rootless-demo
spec:
hostUsers: false
containers:
- name: app
image: debian:stable
command: ["sleep", "infinity"]The kubelet chooses the host UIDs/GIDs the pod is mapped to, ensuring that two pods on the same node never use the same mapping. That's what isolates one pod from another.
One detail that avoids headaches with volumes: the runAsUser, runAsGroup, and fsGroup fields still refer to the user inside the container. The inodes created/read in volumes remain the same as for a pod without a user namespace. Practical consequence: you can turn the feature on and off without changing file ownership, and even share volumes with pods that don't use user namespaces, as long as the users inside the container match up.
To verify the mapping is actually happening, the classic trick: run ps aux on the host and id inside the container. The users won't match. If they match, something didn't take.
The Checklist of What Breaks
This is where the risk lies if you go to production without measuring. When you set hostUsers: false, some things become forbidden:
hostNetwork: true,hostIPC: true, andhostPID: trueare blocked. No sharing host namespaces alongside it.- No container in the pod can use
volumeDevices(raw block volumes, like/dev/sda). This applies tocontainers,initContainers, andephemeralContainers. - NFS volumes don't mount: the Linux NFS client still doesn't support idmap mounts. If your workload depends on NFS, it's left out for now.
If the volume's filesystem doesn't support idmap mount, the pod fails creation with an event that looks like this:
Warning Failed 1s kubelet Error: failed to create containerd task: ...
failed to set MOUNT_ATTR_IDMAP on ${mount path} invalid argument
(maybe the filesystem used doesn't support idmap mounts on this kernel?)When I see MOUNT_ATTR_IDMAP in an event, check number one is the node's kernel version, and check number two is which filesystem backs that volume path.
Integration with Pod Security Admission and OPA
This part is counterintuitive and important. For pods with user namespaces, Kubernetes relaxes, in a controlled way, the enforcement of the Pod Security Standards. Fields that would normally be blocked under the Baseline or Restricted profiles stop being checked, including runAsNonRoot and runAsUser across all container arrays. The logic: root inside a pod with a user namespace is never mapped to a privileged user on the host, so it poses no risk.
Under the Baseline profile, procMount is also relaxed. Under Restricted, the pod still needs to use the default or empty ProcMount.
The takeaway for anyone using OPA/Gatekeeper or Kyverno: your custom policies probably don't know about this exception. If you have a rule that blindly blocks runAsNonRoot: false, it will reject legitimate pods with user namespaces. Before migrating, review your constraints to treat hostUsers: false as a signal that root there is safe.
Configuring the ID Range on the Node
By default the kubelet assigns pods UIDs/GIDs above the 0-65535 range, avoiding overlap with the host. You can customize this, but with rigid rules. The node needs a kubelet system user, the getsubids binary (from shadow-utils) on the PATH, and a subordinate ID configuration. Example in /etc/subuid and /etc/subgid:
# name:firstID:count
kubelet:65536:7208960Here 65536 is the minimum possible and 7208960 is 110 * 65536 (110 pods per node). The restrictions: firstID must be a multiple of 65536 and ≥ 65536, count must be a multiple of 65536 and at least 65536 x maxPods, the same range for UID and GID, no overlap with any other assignment, and a single line only.
Since v1.33 you can set the per-pod count in KubeletConfiguration:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
userNamespaces:
idsPerPod: 1048576idsPerPod needs to be a multiple of 65536 (default 65536) and only applies to containers created after the kubelet starts up with this config.
Be careful when reconfiguring an existing node: the change must be made with no pods using user namespaces running. drain first, apply the config, and restart the kubelet. If it can't honor the new range for existing pods, it fails to start. And remember that drain doesn't evict DaemonSets or pods that tolerate the unschedulable taint.
How to Measure Adoption
No metrics, no production. The kubelet exports two specific Prometheus metrics:
started_user_namespaced_pods_total: counter of user-namespaced pods that were attempted to be created.started_user_namespaced_pods_errors_total: counter of errors creating these pods.
A simple alert on the ratio between the two already tells you whether the migration is stalling on some node, probably due to an old kernel or a filesystem without idmap support. Start with a staging cluster (kind or kubeadm), run workloads with hostUsers: false, watch these counters, and only then roll out by node pool. The User Namespaces documentation has the reference step-by-step.
Translated from the Brazilian Portuguese original · Read the original
Kubernetes: The Practical Guide to Migrating from PodSecurityPolicy to Pod Security Admission
The admission controller that replaced PodSecurityPolicy has been stable since Kubernetes 1.25, but configuring the privileged, baseline, and restricted profiles per namespace still breaks workloads that weren't audited before enforcement.
