Run Docker in rootless mode
# As the unprivileged user who will own the containers:dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.socksystemctl --user enable --now dockerWhy it matters
Section titled “Why it matters”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.sockand grants your privileges, not root’s. - The
dockergroup 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.
What it costs
Section titled “What it costs”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_SERVICEtorootlesskit. - 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
slirp4netnstogvisor-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=hostand--pid=hostbehave 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.
Podman may be the shorter path
Section titled “Podman may be the shorter path”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.
Related
Section titled “Related”- Protect the Docker socket — the problem this solves.
- Container escape — what changes when the daemon isn’t root.