Rootless Docker runs the Docker daemon and containers without root privileges, significantly reducing the attack surface. If a container escape occurs, the attacker gains only unprivileged user access instead of root. This guide covers setting up rootless Docker on production systems.
Prerequisites
# Install required packages
sudo apt install uidmap dbus-user-session fuse-overlayfs slirp4netns
# Check subordinate UID/GID ranges exist for your user
grep $USER /etc/subuid /etc/subgid
# Should show: username:100000:65536
Installation
# If rootful Docker is installed, stop it first (not required to remove)
sudo systemctl disable --now docker docker.socket
# Install rootless Docker
dockerd-rootless-setuptool.sh install
# Or install from scratch
curl -fsSL https://get.docker.com/rootless | sh
# Add to shell profile
export PATH=$HOME/bin:$PATH
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
Managing the Rootless Daemon
# Start/stop (uses systemd user services)
systemctl --user start docker
systemctl --user stop docker
systemctl --user enable docker
# Enable lingering (keeps service running after logout)
sudo loginctl enable-linger $USER
# Check daemon status
systemctl --user status docker
docker info | grep -i rootless
Networking Differences
# Rootless Docker uses slirp4netns for networking
# Port mapping works but binds to unprivileged ports (>1024) by default
# To use privileged ports (80, 443):
# Option 1: Use sysctl
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
# Option 2: Use a reverse proxy (Nginx/Caddy) on the host
# Option 3: Use rootlesskit port driver
# ~/.config/docker/daemon.json
{
"features": {
"rootless": true
}
}
Storage Configuration
# Rootless Docker stores data in ~/.local/share/docker
# Use overlay2 with fuse-overlayfs
# ~/.config/docker/daemon.json
{
"storage-driver": "fuse-overlayfs"
}
# Or if kernel supports native overlayfs with user namespaces:
{
"storage-driver": "overlay2"
}
Docker Compose with Rootless
# Docker Compose works normally with rootless Docker
# Ensure DOCKER_HOST is set
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker compose up -d
docker compose ps
Limitations
- Cannot use
--privilegedcontainers - Limited network performance (slirp4netns overhead)
- Cannot bind to ports below 1024 without configuration
- No support for
--net=host - AppArmor/SELinux profiles may need adjustment
Best Practices
- Use rootless Docker for all development environments and non-privileged workloads
- Enable lingering so the daemon survives user logout
- Use a reverse proxy for ports 80/443 instead of lowering unprivileged port start
- Use
overlay2storage driver with kernel 5.13+ for best performance - Combine with user namespace remapping for defense in depth