Skip to main content

Rootless Kubelet Hits Beta in Kubernetes 1.37: What Unprivileged Nodes Buy a Multi-Tenant Fleet

10 min readDora NodaDora Noda
Share
On this page

For over a decade, the standard Kubernetes node has run its most powerful agent as root. The kubelet — the process that mounts your volumes, configures your pod networks, and executes your containers — has historically held full host-root privileges on every node, which meant every container-breakout vulnerability in the node stack was one step away from owning the machine. On September 4, 2026, Akihiro Suda (NTT) announced on the Kubernetes blog that Kubernetes v1.37 promotes the KubeletInUserNamespace feature gate to beta, letting the kubelet, the CRI and OCI runtimes, CNI plugins, and kube-proxy all run as an unprivileged user inside a Linux user namespace.

The short version: eight years after the first experiment and five years as an alpha, rootless nodes are now a supported beta with upstream CI behind them — but beta here means "pilot as a separate, tainted node pool," not "migrate the fleet." The security payoff is concrete and evidenced by five years of CVEs, while the adoption price is equally concrete: cgroup v2 delegation, no block-device storage drivers, a short list of known-working CNIs, and userspace networking on the node boundary that you must benchmark before promising tenants line rate. If you run a multi-tenant fleet on machines you own, this is the release to start measuring — not the release to flip the default.

What "rootless" actually moves​

Rootless mode maps a host-level unprivileged user (say, UID 1000) to a fake root inside a user namespace, and runs the entire node stack as that fake root. UID 0 privileges exist only inside the namespace, which turns out to be enough for most of what node components do: mounting volumes, creating cgroups, and configuring pod network namespaces. What the fake root cannot do is touch the real host as root — so a breakout that escapes a container lands in an unprivileged account, not in a shell that can rewrite the kernel, the bootloader, or the firmware to hide the intrusion.

Do not confuse this with user namespaces for pods (hostUsers: false under the UserNamespacesSupport gate, GA since v1.36), which we covered when it went GA. Pod user namespaces isolate workloads from each other while the node components stay root; KubeletInUserNamespace de-privileges the node components themselves. The two compose: combined, they can nest Kubernetes inside Kubernetes without resorting to privileged: true, which is the strictest tenant-isolation shape short of separate clusters.

One mechanical fact shapes everything else: the user namespace must be created outside Kubernetes, typically by rootless Docker, Podman, or nerdctl (k3s is the exception — its rootless mode needs no external runtime). The feature gate itself is, in Suda's own words, "boring": it mostly teaches the kubelet to ignore permission errors when setting node-level sysctls like vm.overcommit_memory and kernel.panic, and when watching kernel messages via /dev/kmsg. Beta did not make rootless automatic; it made it supported.

The receipts: five breakouts, five years​

The announcement post justifies rootless mode with a CVE list that reads like a calendar — roughly one root-on-the-host node-stack breakout per year, across four different components:

CVEComponentWithout user namespacesWith rootless mode
CVE-2022-0811 ("cr8escape")CRI-OTricked into setting arbitrary sysctls like kernel.core_pattern — arbitrary code execution as host rootSysctl writes land inside the namespace; no host-root execution
CVE-2023-27561runcVolume-mount race bypassed masked paths, exposing host procfsAttacker sees the namespace's procfs, as an unprivileged UID
CVE-2024-10220kubeletgitRepo volumes allowed arbitrary command execution as rootCommands execute as the unprivileged node user
CVE-2025-31133runcBind-mounted attacker paths, wrote to host procfs (/proc/sysrq-trigger, core_pattern)Writes confined to the user account; host procfs untouched
CVE-2026-53488containerdCrafted image labels led to arbitrary host command executionExecution inherits the unprivileged UID, not host root

This table is the honest core of the feature: user namespaces do not fix the bugs, they cap what the bugs are worth. And the cap has a stated boundary — user namespaces do nothing against vulnerabilities in the kernel itself, so the announcement explicitly pairs rootless mode with traditional hardening like seccomp rather than presenting it as a replacement. For a fleet packing untrusted tenant workloads onto shared nodes, that is still a meaningful downgrade of every future node-stack CVE from "patch within hours or lose the host" to "patch on the normal cycle while the blast radius stays inside one UID."

What beta changed — and what it pointedly didn't​

Kubernetes v1.37 (Garhwal, shipped August 26, 2026 with 67 enhancements) contains three concrete changes for this feature. First, the gate is now enabled by default — but enabling the gate does not move any kubelet into a user namespace, so existing rootful clusters change in no way at all. Default-on here means "stop carrying the alpha stigma and the explicit opt-in," not "you are now rootless."

Second, nodes now report the fact. kubectl get nodes -o yaml exposes runningInUserNamespace in NodeSystemInfo, which gives administrators the hook to keep root-dependent workloads off rootless nodes:

bash
kubectl get nodes -o yaml | grep runningInUserNamespace
kubectl taint nodes <rootless-node> node-role.kubernetes.io/rootless=true:NoSchedule

Any DaemonSet that genuinely needs host root — CNI installers, host-level observability agents, device plugins — then needs an explicit toleration to land there, which is exactly the failure mode you want to be loud instead of silent.

Third, upstream now tests what it ships: node conformance end-to-end tests run on a rootless kind cluster (ci-kubernetes-e2e-kind-rootless), so regressions against the rootless path break CI instead of your pilot. A few outside-the-gate enablers landed in the same era and matter as much as the promotion itself: idmapped tmpfs in kernel 6.3, UserNamespacesSupport on by default in v1.33, and writable cgroups in containerd v2.1. Together they are what make the Kubernetes-in-Kubernetes nesting story real rather than theoretical.

What beta is not: GA, automatic, or a compatibility promise for your drivers. The announcement's caveat pointer sends you to KEP-2033, whose constraint list is the actual adoption contract. Read it as a checklist, which brings us to the price.

The adoption price, itemized​

Everything below comes from the official "non-root user" task docs and KEP-2033. Treat each row as a go/no-go gate for a rootless node pool:

RequirementWhat it takesWhat breaks if you skip it
cgroup v2 + systemd delegationA systemd unit with Delegate=yes handing the node user a writable cgroup tree; cgroup v1 is explicitly unsupported for security reasonsNo resource accounting or limits on the node at all
cgroupfs driver, no hugetlbcgroupDriver: "cgroupfs" in the kubelet config; systemd does not delegate the hugetlb controller, so containerd must disable itKubelet fights systemd for cgroup ownership; hugepage workloads have no controller
subuid/subgid + sysctlsUnprivileged user listed in /etc/subuid and /etc/subgid, plus distro-specific sysctl setupUser namespace creation fails before anything starts
Storage: local only, FUSE-flavoredNon-local drivers (nfs, iscsi) do not work; block-device mounts need real host privilege. containerd must use the fuse-overlayfs snapshotter unless the kernel is 5.11+ and SELinux is disabledAny CSI driver that attaches host block volumes — including the Hetzner Cloud CSI most self-hosted fleets rely on — has no path onto these nodes
Network: userspace uplink + port forwardingThe node netns gets its non-loopback interface from slirp4netns, pasta/VPNKit, or lxc-user-nic; kubelet 10250 and NodePorts are exposed via an external forwarder (RootlessKit, socat)The node is unreachable and its NodePorts are dark
CNI shortlistFlannel (VXLAN) is the documented known-good plugin; "some CNI plugins may not work"Your current CNI may simply fail — verify, don't assume
kube-proxy in degraded modeiptables or userspace mode only, with conntrack sysctl writes zeroed outIPVS mode and conntrack tuning are unavailable
Hardening trade-offsAppArmor disabled in the containerd config, oom_score_adj restriction relaxed, node sysctls silently ignoredYou trade host-root exposure for weaker per-container MAC — size that trade honestly

Read that table as a fleet operator and the shape of a pilot picks itself: stateless, Flannel-networked, local-storage workloads first — the exact profile of build workers, preview environments, and AI-agent sandboxes. Storage-heavy tenants and anything needing your block CSI stay on rootful pools behind the taint from the previous section.

Measure before you promise​

Beta demands benchmarks, and rootless mode has two known slow paths to quantify. The first is the node-boundary network: userspace TCP/IP relay is inherently slower than the kernel path, and the project's own slirp4netns benchmark (admittedly from 2018, so re-run it) showed about 1 Gbps at MTU 1500, scaling with MTU to roughly 9 Gbps — while a bypass4netns study measured rootful container-to-host throughput near 39 Gbps and a 30x gap attributable to the slirp path. Scope this correctly, though: it taxes traffic crossing the node boundary (kubelet API, NodePort ingress, cross-node VXLAN egress), not pod-to-pod traffic inside the CNI fabric. KEP-2033's own guidance is that slirp4netns favors security while lxc-user-nic favors performance, and Podman 6 ripped out slirp4netns for pasta as the faster default — which gives you three uplinks to benchmark, not one.

The second slow path is FUSE storage: fuse-overlayfs pays a userspace round trip per filesystem operation that native overlayfs does not, and the escape hatch (native overlayfs unprivileged) costs you SELinux. Your benchmark plan for a pilot pool is therefore short and specific: NodePort/ingress throughput and tail latency against the same workload on a rootful node, image pull-and-unpack time, and pod churn rate under a burst scaler. If those three are within your tenant SLOs, the security table above is close to free; if they are not, you have learned it on a tainted pool instead of in an incident.

Where a Cluster API fleet would start​

The announcement lists six use cases, and two of them are written for operators like us. The first is bootstrapping: a temporary unprivileged cluster — kind on a laptop, rootless k3s on a spare box — can run Cluster API to bring up the real fleet, so the machine that births your infrastructure never needed root to do it. That is adoptable this quarter with zero changes to your production nodes.

The second is the sandbox-shaped node pool from the previous sections: untrusted, stateless, network-ordinary workloads on rootless nodes, everything else tainted away. AI-agent sandboxes are the canonical example — the announcement names them explicitly — and preview environments and CI build workers fit the same profile. Kubernetes-in-Kubernetes, now possible without privileged: true, is the longer-term prize: tenant-cluster isolation stronger than API namespaces without paying for separate machines per tenant.

Be clear-eyed about one thing: as of this writing, no mainstream Cluster API provider ships a rootless node image, and minimal-OS projects like Talos take a different hardening path (no shell, API-only, but a root kubelet). A rootless pool today means a custom image build carrying the cgroup delegation, subuid ranges, and CRI config from the checklist above. That is real work — which is precisely why v1.37 is the release to start it, with beta support and upstream CI at your back, rather than the release to finish it.

The trajectory from here is visible: GA "in a future release" depending on feedback and adoption, with KEP-5474 (writable cgroups for unprivileged containers) and KEP-5714 (cgroup namespace control) queued to simplify the nesting story further. Eight years from experiment to beta suggests GA will not be rushed — all the more reason to have measurements, not opinions, ready when it lands.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex