Quick Answer: What Is Docker Rootless Mode?
Docker rootless mode runs the Docker daemon and containers entirely as a non-root user. Instead of running as root with full system privileges, the daemon and every container it spawns operate under your regular user account using Linux user namespaces to map UIDs internally.
- No root daemon - The Docker daemon itself never has root privileges on the host.
- Kernel user namespaces - Container processes believe they are root inside the container; on the host they map to your unprivileged UID.
- Container escape is contained - If an attacker escapes the container, they land as your unprivileged user, not as system root.
- cgroup v2 required - Resource limits require proper kernel and cgroup v2 delegation.
- Trade-offs exist - Port binding below 1024, some network modes, and a handful of Docker features are restricted or unavailable.
What Rootless Mode Actually Changes
In traditional Docker, the daemon runs as root. When you execute docker run, the daemon forks processes that start as root inside the container and often retain elevated capabilities on the host. Even with --user flags, the daemon socket itself is owned by root, and any process that can reach it effectively has root on the machine.
Rootless mode rewires this at the process level:
User Namespace Remapping
Linux user namespaces allow a process to declare its own UID/GID range. Inside the namespace, UID 0 looks like root; outside, the kernel maps it to a high, unprivileged UID (e.g., 100000+offset). Docker rootless uses /etc/subuid and /etc/subgid to define these ranges.
# /etc/subuid example
alice:100000:65536
# This means alice can map UIDs 0-65535 inside a user namespace
# to host UIDs 100000-165535
When a container process runs as UID 0 inside the namespace, the host sees it as UID 100000 - a non-privileged account with no special capabilities on the real system.
Daemon Socket Location
In rootless mode, each user gets their own daemon socket at /run/user/1000/docker.sock rather than the system-wide /var/run/docker.sock. This means other users on the system cannot access your containers, and the daemon is completely isolated per-user.
Filesystem Isolation
Container storage moves from /var/lib/docker to ~/.local/share/docker. The daemon and container images are fully owned by and accessible to your user only. No shared storage pool between users.
Step-by-Step Setup
Prerequisites
Before installing, verify your kernel version (5.4+ minimum, 5.11+ recommended) and that subuid/subgid entries exist for your user:
grep $USER /etc/subuid /etc/subgid
If no entry exists for your user, add it:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
Ubuntu 22.04 / 24.04
sudo apt-get update
sudo apt-get install -y docker.io uidmap dbus-user-session fuse-overlayfs
# Install rootless Docker for your user
dockerd-rootless-setuptool.sh install
# Set DOCKER_HOST for your session
export DOCKER_HOST=unix://\$XDG_RUNTIME_DIR/docker.sock
echo export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock >> ~/.bashrc
Debian 12 (Bookworm)
sudo apt-get install -y docker.io uidmap dbus-user-session
# Debian 12 ships with unprivileged user namespaces enabled by default
dockerd-rootless-setuptool.sh install
AlmaLinux 9 / Rocky Linux 9
sudo dnf -y install dnf-plugins-core
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-rootless-extras
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix://\$XDG_RUNTIME_DIR/docker.sock
Verify the installation
docker run --rm hello-world
# Confirm the daemon process owner (should show your username, NOT root)
ps aux | grep dockerd
cgroup v2 Delegation Requirements
Resource limits (memory caps, CPU quotas) in rootless mode require cgroup v2 with proper delegation. Without delegation, Docker can run containers but cannot enforce resource limits.
Check cgroup version
mount | grep cgroup
# Look for: cgroup2 on /sys/fs/cgroup type cgroup2
Configure delegation
Create a service drop-in override at /etc/systemd/system/[email protected]/delegate.conf with the following content to enable resource delegation per user:
[Service]
Delegate=cpu cpuset io memory pids
After reloading the service manager, resource limits like --memory 512m and --cpus 0.5 will be enforced correctly for rootless containers.
Networking Limitations
Networking is where rootless mode diverges most from traditional Docker. Understanding these constraints prevents surprises in production.
No Binding to Ports Below 1024
Binding privileged ports (80, 443, 25, etc.) requires CAP_NET_BIND_SERVICE. Non-root processes do not have this capability. Your options:
- Use ports above 1024 - Run your service on port 8080 and front it with a reverse proxy (nginx, Caddy) that runs as root and forwards traffic.
- Enable the sysctl - Setting
net.ipv4.ip_unprivileged_port_start=80lowers the privileged port threshold. Security trade-off: any user process can then bind port 80. - Use CAP_NET_BIND_SERVICE on the binary -
setcap cap_net_bind_service=ep /path/to/binary. Not applicable to the Docker daemon itself.
slirp4netns vs vpnkit
Rootless Docker uses userspace networking instead of the kernel bridge networking used in rootful mode:
- slirp4netns - Default on Linux. Userspace TCP/IP stack. Lower performance than kernel networking, but no root required. Suitable for most workloads.
- vpnkit - Used on Docker Desktop (macOS/Windows). Similar concept, different implementation.
- pasta (newer alternative) - Faster than slirp4netns for some workloads, available in Docker 24+.
For high-throughput services, the userspace networking overhead is measurable - typically 10-20% throughput reduction compared to rootful with kernel bridging. For most web applications, the difference is imperceptible.
When Rootless Is Not Enough
Rootless Docker significantly raises the bar for container escape attacks, but it is not a complete security solution on its own.
Kernel-Level Vulnerabilities
Container escapes that target the kernel itself are not prevented by rootless mode. If an attacker can trigger a kernel bug, they can escalate regardless of whether the daemon runs as root or not. Rootless reduces the blast radius - they land as your user, not root - but a kernel privilege escalation from there is still possible on an unpatched kernel.
What Still Needs Hardening
- Seccomp profiles - Filter system calls that containers can make. Docker ships a default seccomp profile; use it or a stricter custom profile.
- AppArmor / SELinux - Mandatory access control prevents containers from accessing files or system resources outside their defined scope.
- Read-only root filesystem - Mount the container root as read-only. Writable state goes to specific volumes only.
- No new privileges - The
--security-opt no-new-privilegesflag prevents processes inside the container from gaining additional privileges via setuid binaries. - Capability dropping - Drop all capabilities and add back only what the application needs.
Shared Kernel Risk
Containers share the host kernel. Unlike virtual machines, there is no hypervisor providing hardware-level isolation. Rootless reduces privilege, but a sufficiently powerful kernel exploit bypasses that. For truly sensitive multi-tenant workloads, gVisor or Kata Containers provide an additional kernel isolation layer.
Rootful vs Rootless: Full Comparison
| Aspect | Rootful Docker | Rootless Docker |
|---|---|---|
| Daemon privilege | Runs as root | Runs as your user |
| Container escape impact | Full root on host | Unprivileged user on host |
| Port binding below 1024 | Supported | Not supported by default |
| Network performance | Full kernel speed | slirp4netns overhead (10-20%) |
| cgroup resource limits | Full support | Requires cgroup v2 delegation |
| Docker socket exposure | Shared, root-owned | Per-user, user-owned |
| Multi-user isolation | None (shared daemon) | Complete (separate daemon per user) |
| Image storage location | /var/lib/docker | ~/.local/share/docker |
| Compose support | Full | Full (Docker Compose v2) |
| GPU passthrough | Full support | Limited (NVIDIA CDI required) |
| Swarm mode | Supported | Not supported |
| BuildKit | Supported | Supported |
Production Hardening Checklist
Whether you run rootful or rootless, these flags should be part of every production container configuration:
docker run \
--rm \
--read-only \
--tmpfs /tmp \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--security-opt no-new-privileges=true \
--memory 512m \
--cpus 0.5 \
--pids-limit 100 \
--user 1000:1000 \
my-image:1.2.3
For Docker Compose, the equivalent in docker-compose.yml:
services:
app:
image: my-image:1.2.3
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 0.5
pids_limit: 100
user: "1000:1000"
AppArmor Profile
Docker ships a default AppArmor profile (docker-default) applied automatically on Ubuntu and Debian. Verify it is active:
docker inspect --format {{ .HostConfig.SecurityOpt }} container_name
# Should show: [apparmor=docker-default]
TL;DR
- Rootless Docker moves the daemon and all container processes to run as your unprivileged user via Linux user namespaces.
- A container escape lands an attacker as your user, not as root - significantly reducing blast radius.
- Setup requires uidmap, subuid/subgid entries, and lingering enabled for persistence across reboots.
- cgroup v2 with delegation is required for resource limits to work correctly.
- Port binding below 1024 requires additional configuration or a reverse proxy.
- Combine rootless mode with read-only filesystems, capability dropping, seccomp, and AppArmor for defense in depth.
Managing Docker containers across multiple users and websites from a single interface takes more than rootless configuration. Panelica gives you per-container cgroup isolation, a visual Docker Compose manager, one-click application templates, and automatic SSL for every exposed service - all without touching configuration files.