01Why Run Docker Rootless?
By default, Docker requires root privileges and containers run as root. This creates security risks: a container escape could give an attacker root access to your host.
**Rootless Docker mitigates this by:**
• Running the Docker daemon as a non-root user
• Mapping container root to an unprivileged host user
• Limiting damage from container escapes
• Meeting compliance requirements
**Trade-offs:**
• Some features require workarounds
• Slight performance overhead
• Networking options are limited
• Not all images work out of the box
Rootless Docker is now ready and recommended for security-conscious deployments.
02Installing Rootless Docker
Rootless mode can be installed alongside regular Docker or as a standalone installation. Here's the standalone approach for a user named 'docker-user'.
1# Prerequisites: install uidmap2sudo apt-get install -y uidmap dbus-user-session34# Create a dedicated user (optional, can use existing)5sudo useradd -m docker-user6sudo loginctl enable-linger docker-user78# Switch to the user9sudo su - docker-user1011# Install rootless Docker12curl -fsSL https://get.docker.com/rootless | sh1314# Add to PATH and set DOCKER_HOST15echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc16echo 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> ~/.bashrc17source ~/.bashrc1819# Start the daemon20systemctl --user start docker21systemctl --user enable dockerRootless Docker uses different paths and socket locations. Ensure DOCKER_HOST is set correctly.
03Using Docker Compose with Rootless
Docker Compose works with rootless Docker, but you need to ensure the correct socket is used and understand the user mapping implications.
1# docker-compose.yml for rootless Docker2services: 3 app: 4 image: myapp:latest5 # Files will be owned by subordinate UIDs6 # Use PUID/PGID for linuxserver.io images7 environment: 8 - PUID=10009 - PGID=100010 volumes: 11 # Use paths in user's home directory12 - ./data:/app/data13 ports: 14 # Ports < 1024 require special handling15 - "8080:80"1617 db: 18 image: postgres:16-alpine19 environment: 20 - POSTGRES_PASSWORD=${DB_PASSWORD}21 volumes: 22 - db_data:/var/lib/postgresql/data2324volumes: 25 db_data: In rootless mode, use ports above 1024 or configure net.ipv4.ip_unprivileged_port_start for lower ports.
04User Namespace Remapping
An alternative to full rootless mode is user namespace remapping. This runs Docker normally but maps container users to unprivileged host users.
**How it works:**
• Container root (UID 0) maps to high UID on host
• Container files are owned by subordinate UIDs
• Host root is protected even if container escapes
1# /etc/docker/daemon.json2{3 "userns-remap": "default"4}56# Or remap to specific user7{8 "userns-remap": "docker-remap:docker-remap"9}1011# Setup subordinate IDs for the remap user12# /etc/subuid and /etc/subgid13dockremap:100000:655361415# Restart Docker16sudo systemctl restart docker1718# Verify remapping is active19docker info | grep -i usernsUser namespace remapping changes file ownership. Existing volumes may need permission fixes.
05PUID/PGID Pattern for Permissions
Many images (especially LinuxServer.io) support PUID and PGID environment variables to run internal processes as a specific user. This works with both rootless Docker and user namespaces.
1services: 2 # LinuxServer.io images support PUID/PGID3 jellyfin: 4 image: linuxserver/jellyfin:latest5 environment: 6 - PUID=1000 # Your user's UID7 - PGID=1000 # Your user's GID8 - TZ=Europe/London9 volumes: 10 - ./config:/config11 - /media:/media:ro1213 # For images without PUID support, use user directive14 custom-app: 15 image: myapp:latest16 user: "1000:1000"17 volumes: 18 - ./data:/app/data1920 # Or run as non-root within Dockerfile21 # USER appuserFind your UID/GID with: id -u && id -g
06Solving Permission Issues
Permission problems are common with rootless Docker and user namespaces. Here's how to diagnose and fix them.
1# Check container user2docker exec mycontainer id34# Check file ownership inside container5docker exec mycontainer ls -la /app/data67# Check file ownership on host8ls -la ./data910# Fix ownership for rootless Docker11# Container root maps to your user, so this usually works:12sudo chown -R $USER:$USER ./data1314# For user namespace remapping, find the mapped UID15cat /etc/subuid | grep dockremap16# Output: dockremap:100000:6553617# Container root (0) maps to host UID 1000001819# Fix ownership for remapped containers20sudo chown -R 100000:100000 ./data2122# Or use ACLs for more flexibility23setfacl -R -m u:100000:rwx ./data07Limitations and Workarounds
Some Docker features work differently or have limitations in rootless mode:
**Networking:**
• Bridge networking uses slirp4netns or pasta
• Host networking not available
• Ports < 1024 need configuration
**Storage:**
• Some volume drivers may not work
• Overlay2 requires kernel 5.11+ for rootless
**Capabilities:**
• Many capabilities are unavailable
• --privileged is very limited
1# Allow binding to privileged ports2sudo sysctl net.ipv4.ip_unprivileged_port_start=8034# Or make it permanent5echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/99-rootless.conf6sudo sysctl --system78# Use pasta for better networking (if available)9# In ~/.config/docker/daemon.json:10{11 "features": {12 "containerd-snapshotter": true13 }14}1516# Check what's working17docker info 2>/dev/null | grep -E "rootless|Runtimes|Storage"Most standard workloads run fine in rootless mode. Test your specific stack before migrating production.