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

How It Works

Rootless containers use user namespaces to remap UIDs:

  • Inside container: Process runs as UID 0 (root)

  • On host: Process runs as your UID (e.g., 1000)

  • Subordinate IDs: Additional UIDs/GIDs from /etc/subuid and /etc/subgid

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

Install Required Packages

# Arch Linux
sudo pacman -S slirp4netns fuse-overlayfs shadow

# Debian/Ubuntu
sudo apt install slirp4netns fuse-overlayfs uidmap

# RHEL/Fedora
sudo dnf install slirp4netns fuse-overlayfs shadow-utils

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

Start Rootless Docker

# Start daemon
systemctl --user start docker

# Enable on login
systemctl --user enable docker

# Enable lingering
loginctl enable-linger $USER

# Verify
docker info | grep "Root Dir"
# Shows path under /home/user/

Usage

# Run containers normally
docker run -d --name web -p 8080:80 nginx

# Build images
docker build -t myapp .

# Note: Some features limited in rootless mode
# - No privileged containers
# - Limited network drivers
# - Some volume permissions differ

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

Rootless CNI

# Create rootless network
podman network create mynet

# Run container on network
podman run -d --network mynet --name web nginx
podman run -d --network mynet --name app myapp

# Containers can communicate by name
podman exec app ping web

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

VFS Storage (Fallback)

# No kernel overlay support needed
# Higher disk usage (full copy each layer)

cat > ~/.config/containers/storage.conf << 'EOF'
[storage]
driver = "vfs"
EOF

Volume Permissions

# Volume mounted as your user (not container's root)

# Use :U to fix permissions (Podman)
podman run -v /host/path:/container/path:U nginx

# Or set correct ownership before mounting
mkdir -p ~/data
podman unshare chown 0:0 ~/data
podman run -v ~/data:/data nginx

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

Storage Issues

# Check fuse-overlayfs
which fuse-overlayfs

# Verify overlay support
cat /proc/filesystems | grep overlay

# Check storage usage
podman system df
du -sh ~/.local/share/containers/storage/

Container Won’t Start

# Check logs
podman logs container_name
journalctl --user -xe

# Run interactively to debug
podman run -it --name debug nginx /bin/sh

# Check for cgroup issues (systemd-based hosts)
loginctl show-user $USER | grep Linger
# Should be "Linger=yes"

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