Skip to content

Run Docker in rootless mode

Severity: mediumApplies to: Docker Engine 27.x+Applies to: Docker Engine 29.x
The fix
Terminal window
# As the unprivileged user who will own the containers:
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
systemctl --user enable --now docker

Every other page in this cluster narrows what a container can do to a host whose daemon runs as root. Rootless mode removes the root daemon.

dockerd runs as your unprivileged user inside a user namespace. Container root maps to your UID on the host, not to real root. That changes the arithmetic on the thing this whole cluster is built around:

  • The socket is no longer root-equivalent. It lives in $XDG_RUNTIME_DIR/docker.sock and grants your privileges, not root’s.
  • The docker group problem disappears, because there’s no root daemon to be a member of.
  • A container escape lands as you, not as root. It’s still bad — your files, your keys — but it isn’t the machine.

This is the only structural fix on the list. Everything else is mitigation.

Rootless is production-ready and has been for years, but it is not free, and the honest list matters:

  • Ports below 1024 need a workaround. An unprivileged process can’t bind them. Either publish high ports and put a reverse proxy in front, or grant CAP_NET_BIND_SERVICE to rootlesskit.
  • No AppArmor, and cgroup controls depend on cgroup v2 with delegation configured. On cgroup v1 you get no resource limits at all.
  • Networking is slower and different. Docker 29.5 changed the default rootless network driver from slirp4netns to gvisor-tap-vsock; both are userspace network stacks and neither matches native bridge throughput.
  • Overlay networks and some storage drivers have limits or don’t work.
  • --privileged, --network=host and --pid=host behave differently or not at all — which is mostly the point, but it does break things.

For a web application, none of these matter. For a CI runner doing exotic networking, some will.

Worth saying plainly rather than burying: if you are on RHEL, Fedora or CentOS, Podman 5.x is already installed and is rootless by default. Red Hat ships and supports it, and there is no daemon at all — containers are child processes of your shell, so there is no socket to protect and no daemon to compromise.

Podman is largely CLI-compatible (alias docker=podman covers a lot), reads the same images, and RHEL 9.5+ uses pasta for rootless networking. podman generate systemd produces proper unit files, which is genuinely nicer than Docker’s restart policies.

That’s not a recommendation to migrate a working Docker install. It is a recommendation to check what’s already on the box before doing the work of retrofitting rootless Docker onto a distro that ships a rootless-by-default alternative.