Rootless Containers
Quick Reference
# Configure user namespaces
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# Run rootless container (Podman - works out of box)
podman run -d --name web -p 8080:80 nginx
# Run rootless container (Docker)
dockerd-rootless-setuptool.sh install
docker context use rootless
docker run -d --name web -p 8080:80 nginx
# Check rootless status
podman info | grep rootless
Understanding Rootless Containers
Why Rootless?
Traditional container runtimes require root privileges, creating security concerns:
-
Container escape - A compromised container with root could affect the host
-
Privilege escalation - Root in container maps to root on host
-
Socket exposure - Docker socket access grants full root access
Rootless containers run entirely as a regular user:
-
No root daemon - Container runtime runs as your user
-
User namespace isolation - Root in container maps to your UID on host
-
Reduced attack surface - Container compromise limited to user privileges
Prerequisites
User Namespace Support
# Check kernel support
cat /proc/sys/kernel/unprivileged_userns_clone
# 1 = enabled
# Enable if disabled (temporary)
sudo sysctl kernel.unprivileged_userns_clone=1
# Enable permanently
echo "kernel.unprivileged_userns_clone=1" | sudo tee /etc/sysctl.d/userns.conf
sudo sysctl -p /etc/sysctl.d/userns.conf
Configure Subordinate IDs
# Check current allocation
grep $USER /etc/subuid
grep $USER /etc/subgid
# Add subuid/subgid range (65536 IDs)
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# Or manually edit files
# /etc/subuid: username:100000:65536
# /etc/subgid: username:100000:65536
# Verify
cat /etc/subuid
cat /etc/subgid
Podman Rootless
Podman is designed for rootless operation and works without additional setup.
Basic Usage
# Run container (as regular user)
podman run -d --name web -p 8080:80 nginx
# Verify running as your user
ps aux | grep nginx
# Shows your username, not root
# Check container user mapping
podman top web user huser
# user = container UID, huser = host UID
Configuration
# Rootless storage location
~/.local/share/containers/storage/
# Configuration files
~/.config/containers/containers.conf
~/.config/containers/registries.conf
~/.config/containers/storage.conf
# Example storage.conf for rootless
cat > ~/.config/containers/storage.conf << 'EOF'
[storage]
driver = "overlay"
runroot = "/run/user/1000/containers"
graphroot = "/home/user/.local/share/containers/storage"
[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"
EOF
Networking
# Rootless uses slirp4netns for networking
# Ports < 1024 require workaround
# Option 1: Use high ports
podman run -d -p 8080:80 nginx
# Option 2: Allow unprivileged ports (system-wide)
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
# Option 3: Port forwarding with rootful helper
sudo podman run -d -p 80:80 nginx
# Check network mode
podman info | grep network
Systemd Integration
# Generate user systemd unit
podman generate systemd --new --name web > ~/.config/systemd/user/container-web.service
# Enable user lingering (keeps services running after logout)
loginctl enable-linger $USER
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now container-web.service
# Check status
systemctl --user status container-web.service
# View logs
journalctl --user -u container-web.service
Docker Rootless
Docker supports rootless mode through a separate daemon installation.
Installation
# Install rootless setup tools
# Usually included with docker-ce package
# Run setup script
dockerd-rootless-setuptool.sh install
# Or manually:
# 1. Install dependencies
sudo apt install uidmap dbus-user-session
# 2. Run install
dockerd-rootless-setuptool.sh install
Configure Environment
# Add to ~/.bashrc or ~/.zshrc
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
# Or use Docker context
docker context use rootless
Comparison: Rootless vs Rootful
| Feature | Rootful | Rootless |
|---|---|---|
Daemon privileges |
Root |
Regular user |
Container escape impact |
Full host access |
User-level access only |
Privileged containers |
Yes |
No |
Ports < 1024 |
Yes |
Requires workaround |
Network performance |
Native |
Slight overhead (slirp4netns) |
Storage drivers |
All |
overlay/fuse-overlayfs |
Host volume mounts |
Full access |
User permission only |
Networking Options
slirp4netns (Default)
# Default for rootless - userspace networking
# Slower but fully unprivileged
# Port forwarding
podman run -d -p 8080:80 nginx
# Check slirp4netns process
ps aux | grep slirp4netns
pasta (Modern Alternative)
# Install pasta (passt package)
sudo pacman -S passt # Arch
sudo apt install passt # Debian/Ubuntu
# Configure Podman to use pasta
cat > ~/.config/containers/containers.conf << 'EOF'
[network]
default_rootless_network_cmd = "pasta"
EOF
# Faster than slirp4netns, same functionality
Storage Considerations
Overlay with fuse-overlayfs
# Default for rootless
# Requires fuse-overlayfs installed
# Verify storage driver
podman info | grep -A5 "graphDriverName"
# Configure explicitly
cat > ~/.config/containers/storage.conf << 'EOF'
[storage]
driver = "overlay"
[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"
EOF
Troubleshooting
Permission Denied Errors
# Check subuid/subgid
grep $USER /etc/subuid /etc/subgid
# Reset storage (Podman)
podman system reset
# Check namespace creation
unshare --user --map-root-user echo "User namespace works"
Networking Issues
# Check slirp4netns
which slirp4netns
slirp4netns --version
# Check port availability
ss -tlnp | grep 8080
# Enable unprivileged low ports
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
Security Comparison
Attack Surface
Rootless containers significantly reduce security risk:
-
No root daemon - Compromising container doesn’t grant root
-
User namespace - Container root is regular user on host
-
Limited capabilities - Can’t load kernel modules, access raw devices
-
Resource isolation - cgroups v2 user delegation
Remaining Risks
Even rootless containers have considerations:
-
User data access - Container can access files your user can access
-
Network - Can establish outbound connections
-
Resource exhaustion - Could consume CPU/memory/disk
Best Practices
# Run as non-root inside container too
podman run --user 1000:1000 nginx
# Read-only root filesystem
podman run --read-only nginx
# Drop all capabilities
podman run --cap-drop=all nginx
# Limit resources
podman run --memory=512m --cpus=1 nginx
# Use seccomp profile
podman run --security-opt seccomp=/path/to/profile.json nginx
Quick Command Reference
# Setup
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
loginctl enable-linger $USER
# Podman (rootless by default)
podman run -d --name web -p 8080:80 nginx
podman info | grep rootless
# Docker rootless
dockerd-rootless-setuptool.sh install
docker context use rootless
# Systemd user services
podman generate systemd --new --name web > ~/.config/systemd/user/container-web.service
systemctl --user enable --now container-web.service
# Troubleshooting
grep $USER /etc/subuid /etc/subgid
podman system reset
podman info