docker.recipes
Security9 min read

Rootless Docker and User Namespaces

Run Docker without root privileges for improved security using rootless mode and user namespace remapping.

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 uidmap
2sudo apt-get install -y uidmap dbus-user-session
3
4# Create a dedicated user (optional, can use existing)
5sudo useradd -m docker-user
6sudo loginctl enable-linger docker-user
7
8# Switch to the user
9sudo su - docker-user
10
11# Install rootless Docker
12curl -fsSL https://get.docker.com/rootless | sh
13
14# Add to PATH and set DOCKER_HOST
15echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
16echo 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> ~/.bashrc
17source ~/.bashrc
18
19# Start the daemon
20systemctl --user start docker
21systemctl --user enable docker

Rootless 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 Docker
2services:
3 app:
4 image: myapp:latest
5 # Files will be owned by subordinate UIDs
6 # Use PUID/PGID for linuxserver.io images
7 environment:
8 - PUID=1000
9 - PGID=1000
10 volumes:
11 # Use paths in user's home directory
12 - ./data:/app/data
13 ports:
14 # Ports < 1024 require special handling
15 - "8080:80"
16
17 db:
18 image: postgres:16-alpine
19 environment:
20 - POSTGRES_PASSWORD=${DB_PASSWORD}
21 volumes:
22 - db_data:/var/lib/postgresql/data
23
24volumes:
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.json
2{
3 "userns-remap": "default"
4}
5
6# Or remap to specific user
7{
8 "userns-remap": "docker-remap:docker-remap"
9}
10
11# Setup subordinate IDs for the remap user
12# /etc/subuid and /etc/subgid
13dockremap:100000:65536
14
15# Restart Docker
16sudo systemctl restart docker
17
18# Verify remapping is active
19docker info | grep -i userns

User 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/PGID
3 jellyfin:
4 image: linuxserver/jellyfin:latest
5 environment:
6 - PUID=1000 # Your user's UID
7 - PGID=1000 # Your user's GID
8 - TZ=Europe/London
9 volumes:
10 - ./config:/config
11 - /media:/media:ro
12
13 # For images without PUID support, use user directive
14 custom-app:
15 image: myapp:latest
16 user: "1000:1000"
17 volumes:
18 - ./data:/app/data
19
20 # Or run as non-root within Dockerfile
21 # USER appuser

Find 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 user
2docker exec mycontainer id
3
4# Check file ownership inside container
5docker exec mycontainer ls -la /app/data
6
7# Check file ownership on host
8ls -la ./data
9
10# Fix ownership for rootless Docker
11# Container root maps to your user, so this usually works:
12sudo chown -R $USER:$USER ./data
13
14# For user namespace remapping, find the mapped UID
15cat /etc/subuid | grep dockremap
16# Output: dockremap:100000:65536
17# Container root (0) maps to host UID 100000
18
19# Fix ownership for remapped containers
20sudo chown -R 100000:100000 ./data
21
22# Or use ACLs for more flexibility
23setfacl -R -m u:100000:rwx ./data

07Limitations 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 ports
2sudo sysctl net.ipv4.ip_unprivileged_port_start=80
3
4# Or make it permanent
5echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/99-rootless.conf
6sudo sysctl --system
7
8# Use pasta for better networking (if available)
9# In ~/.config/docker/daemon.json:
10{
11 "features": {
12 "containerd-snapshotter": true
13 }
14}
15
16# Check what's working
17docker info 2>/dev/null | grep -E "rootless|Runtimes|Storage"

Most standard workloads run fine in rootless mode. Test your specific stack before migrating production.