K3s in rootless mode is less about running Kubernetes without root, and more about running it as a non-privileged user, which is a fundamentally different security posture.

Let’s see it in action. Imagine you’re logged in as a regular user, k3s-user, on a machine.

# First, download the k3s binary and make it executable
curl -sfL https://get.k3s.io | INSTALL_DIR=/tmp/k3s sh -
sudo mv /tmp/k3s/k3s /usr/local/bin/

# Now, start k3s as the non-privileged user
mkdir -p $HOME/.config/k3s
k3s server --config $HOME/.config/k3s/config.yaml &

The config.yaml might look something like this:

write-kubeconfig-mode: "0644"
kubelet-arg:
  - "root-dir=/home/k3s-user/.k3s/kubelet"
  - "cert-dir=/home/k3s-user/.k3s/kubelet/pki"
containerd-args:
  - "--root=/home/k3s-user/.k3s/containerd"
  - "--state=/home/k3s-user/.k3s/containerd/state"
disable:
  - "traefik"

After a moment, you can check the status and see pods running, all managed by k3s-user:

# Make sure KUBECONFIG is set correctly for the user
export KUBECONFIG=$HOME/.config/k3s/k3s.yaml
kubectl get pods -A

You’ll see output like:

NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   coredns-xxxxxxx-yyyyy                     1/1     Running   0          2m
kube-system   metrics-server-xxxxxxx-yyyyy              1/1     Running   0          2m
kube-system   local-path-provisioner-xxxxxxx-yyyyy      1/1     Running   0          2m

This isn’t just a user-space trick; it fundamentally changes how K3s interacts with the operating system. Normally, Kubernetes components like kubelet and containerd require root privileges to:

Rootless mode leverages user namespaces and subuid/subgid mappings to give these components the illusion of root privileges within their own isolated environment, without granting them actual root access on the host. The k3s binary itself runs as the specified non-privileged user, and its child processes inherit these user namespace capabilities. This means containerd and kubelet are also running as that same non-privileged user, but inside user namespaces that provide them with UID 0 (root) and GID 0 (root) for operations that don’t require host-level privileges.

The key components that enable this are containerd and kubelet, which have been modified or configured to operate within user namespaces. containerd uses runc (or a compatible OCI runtime) that supports user namespaces. kubelet uses these user-namespaced containerd instances to manage pods. Networking is handled by a CNI plugin that also supports rootless operation, often using slirp4netns for network address translation, which is less performant than host-level networking but crucial for isolation.

The primary benefit is enhanced security. If a vulnerability is exploited within a container or even within containerd or kubelet running in rootless mode, the attacker’s privileges are still confined to the user namespace of the k3s-user, severely limiting their ability to impact the host system.

The one thing that often trips people up is the expectation of full feature parity with rootful Kubernetes, especially around networking and storage. While rootless mode is incredibly powerful for isolated development or trusted environments, advanced networking setups (like host-mode networking for DaemonSets) or certain CSI drivers that require direct host device access might not function correctly or at all. For instance, slirp4netns is used by default for networking, which means pods don’t get direct IP addresses on the host’s network; instead, traffic is NAT’d, impacting certain peer-to-peer communication patterns.

The next hurdle you’ll likely encounter is understanding how to manage persistent storage for rootless pods, especially when dealing with complex storage solutions.

Want structured learning?

Take the full K3s course →