Why Running Docker as Root Is Dangerous and How Rootless Mode Secures Your Containers

This guide explains the security risks of running Docker with root privileges—including container escape, privilege escalation, filesystem and network abuse—and provides step‑by‑step instructions for configuring Docker Rootless mode, its limitations, debugging tips, and comparison with Podman.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Why Running Docker as Root Is Dangerous and How Rootless Mode Secures Your Containers

Running Docker as the root user introduces several security risks:

Container escape : A vulnerable container running as root can break out of its isolation and execute malicious actions on the host.

Privilege escalation : Processes inside a root‑owned container may attempt to gain root privileges on the host.

Filesystem access : Root containers can read or modify host files, leading to data leakage or corruption.

Network privileges : Root containers can abuse network capabilities, such as port scanning or DDoS attacks.

To mitigate these risks, adopt the following practices:

Run containers as non‑root users : Use the USER directive to drop root privileges inside the container.

Restrict container capabilities : Apply --security-opt to limit what the container can access.

Update and monitor containers : Keep base images and applications up‑to‑date and use monitoring/auditing tools.

Limit capabilities : Provide only the minimal set of capabilities required for the workload.

What Looks Like Rootless but Isn’t

docker run --user foo

: Runs the process as a non‑root user, but the underlying runc and containerd still run as root. usermod -aG docker foo: Grants a non‑root user access to the Docker socket, effectively allowing root‑level operations. sudo docker and chmod +s dockerd: Both give root‑like privileges. dockerd --userns-remap: Allows non‑root operation of containers, yet runc and containerd remain root‑owned.

Docker Rootless Basic Concept

Docker Rootless runs Docker in an unprivileged mode, allowing the daemon and containers to be managed by a non‑root user, which reduces the attack surface. The daemon can be started without any root privileges, preventing many privilege‑related exploits.

Rootless mode lets the Docker daemon (dockerd) and containers run as non‑root users, mitigating potential vulnerabilities. Rootless was introduced as an experimental feature in Docker v19.03 and became GA in Docker v20.10.

How Rootless Works

Rootless leverages user namespaces to map the container’s root user to an unprivileged UID on the host. The --userns-remap flag provides this capability, and Rootless extends it so the Docker daemon itself also runs inside the remapped namespace.

User namespaces have existed since Linux kernel v3.8, and Docker has supported them for a long time.

Rootless runs the daemon and containers inside a user namespace, similar to userns‑remap but without any root privileges for either the daemon or the containers.

Rootless does not use binaries with the SETUID bit; only newuidmap and newgidmap are required for UID/GID mapping.

Known Limitations

Supported storage drivers: overlay2 (kernel ≥ 5.11, Ubuntu/Debian style kernels) fuse‑overlayfs (kernel ≥ 4.18 with fuse‑overlayfs installed) btrfs (kernel ≥ 4.18 or with appropriate mount options) vfs Cgroup v2 is supported only when used with systemd.

Unsupported features: AppArmor, seccomp, overlay networking, SCTP port exposure.

Ping command requires special routing configuration.

Privileged TCP/UDP ports (<1024) need extra configuration.

IP addresses shown by docker inspect are inside the RootlessKit network namespace and are not reachable from the host without nsenter.

Host network mode ( docker run --net=host) is not supported.

Rootless Practice

Practice Environment

Experiments are performed on a CentOS 7.5 virtual machine.

$ cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)

Create a Normal User

$ useradd rootless
$ echo 123456 | passwd rootless --stdin

Install Dependencies

Rootless mode requires newuidmap and newgidmap. Install them with:

cat <<EOF | sudo sh -x
curl -o /etc/yum.repos.d/vbatts-shadow-utils-newxidmap-epel-7.repo https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/repo/epel-7/vbatts-shadow-utils-newxidmap-epel-7.repo
yum install -y shadow-utils46-newxidmap
cat <<EOT > /etc/sysctl.conf
user.max_user_namespaces = 28633
EOT
sysctl --system
EOF

Notes

CentOS 7

Add user.max_user_namespaces=28633 to /etc/sysctl.conf (or a file under /etc/sysctl.d) and run sudo sysctl --system. systemctl --user does not work by default; dockerd-rootless.sh runs without systemd.

CentOS 8 / Fedora

Install fuse‑overlayfs with sudo dnf install -y fuse‑overlayfs.

Install iptables if needed.

If SELinux is enabled, you may see “can’t open lock file /run/xtables.lock: Permission denied”. Fix it with

sudo dnf install -y policycoreutils-python-utils && sudo semanage permissive -a iptables_t

.

Known to work on CentOS 8 and Fedora 33.

Ubuntu

No preparation needed; overlay2 is enabled by default.

Supported on Ubuntu 16.04, 18.04, 20.04.

UID/GID Mapping Configuration

Mappings are controlled by /etc/subuid and /etc/subgid. Create 65 536 subordinate IDs for the user rootless:

echo "rootless:100000:65536" | tee /etc/subuid
echo "rootless:100000:65536" | tee /etc/subgid

This maps host IDs 100000‑165535 to container IDs 0‑65535, so even if a process runs as root inside the container it only has the privileges of the non‑root rootless user on the host.

Install Rootless Docker

Switch to the rootless user: su - rootless Install Rootless Docker with the official script: curl -fsSL https://get.docker.com/rootless | sh Installation output (truncated):

# Installing stable version 24.0.5
# Executing docker rootless install script, commit: b9139c0
... (download progress) ...
+ PATH=/home/rootless/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/rootless/.local/bin:/home/rootless/bin
+ /home/rootless/bin/dockerd-rootless-setuptool.sh install
[INFO] systemd not detected, dockerd-rootless.sh needs to be started manually:
PATH=/home/rootless/bin:/sbin:/usr/sbin:$PATH dockerd-rootless.sh
[INFO] Creating CLI context "rootless"
Successfully created context "rootless"
[INFO] Using CLI context "rootless"
Current context is now "rootless"
[INFO] Make sure the following environment variable(s) are set (or add them to ~/.bashrc):
export XDG_RUNTIME_DIR=/home/rootless/.docker/run
export PATH=/home/rootless/bin:$PATH
[INFO] Some applications may require the following environment variable too:
export DOCKER_HOST=unix:///home/rootless/.docker/run/docker.sock

Add the exported variables to ~/.bashrc and source the file:

export XDG_RUNTIME_DIR=/home/rootless/.docker/run
export PATH=/home/rootless/bin:$PATH
export DOCKER_HOST=unix:///home/rootless/.docker/run/docker.sock

Start Docker Daemon

Method 1: With systemd (recommended)

The systemd unit is located at ~/.config/systemd/user/docker.service. Manage it with: systemctl --user start docker Enable it at boot:

systemctl --user enable docker
sudo loginctl enable-linger $(whoami)

Important directories:

Socket default path: $XDG_RUNTIME_DIR/docker.sock (usually /run/user/$UID).

Data directory: ~/.local/share/docker (should not be on NFS).

Daemon config directory: ~/.config/docker (different from ~/.docker used by the client).

The command sudo loginctl enable-linger $(whoami) enables the user’s linger setting so that the user’s services continue running after logout.

Method 2: Without systemd

dockerd-rootless.sh

Required environment variables: $HOME – user’s home directory. $XDG_RUNTIME_DIR – a private runtime directory (e.g., ~/.docker/run), which should be removed on shutdown and not placed under /tmp to avoid TOCTOU attacks.

Client

You must explicitly specify the socket path or CLI context.

Set the socket path:

export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker run -d -p 8080:80 nginx

Or set the CLI context:

docker context use rootless
rootless
Current context is now "rootless"
docker run -d -p 8080:80 nginx

Run a Container

Start an Nginx container and map port 80 to host port 8080: docker run -d -p 8080:80 nginx List containers:

[rootless@demo ~]$ docker ps
CONTAINER ID   IMAGE   COMMAND               CREATED          STATUS          PORTS                     NAMES
f3b204c97a84   nginx   "/docker-entrypoint.…"   9 minutes ago   Up 9 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   bold_stonebraker

Access the service:

$ curl http://localhost:8080
# Returns the default Nginx welcome page
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and working.</p>
</body>
</html>

Rootless Debugging Tips

Enter the dockerd namespace with:

nsenter -U --preserve-credentials -n -m -t $(cat $XDG_RUNTIME_DIR/docker.pid)

Rootless Uninstall

Remove the systemd service:

dockerd-rootless-setuptool.sh uninstall
+ systemctl --user stop docker.service
+ systemctl --user disable docker.service
Removed /home/testuser/.config/systemd/user/default.target.wants/docker.service.
[INFO] Uninstalled docker.service
[INFO] This uninstallation tool does NOT remove Docker binaries and data.
[INFO] To remove data, run: `/usr/bin/rootlesskit rm -rf /home/testuser/.local/share/docker`

Delete the data directory: rootlesskit rm -rf ~/.local/share/docker Remove binaries (if installed via the script):

cd ~/bin
rm -f containerd containerd-shim containerd-shim-runc-v2 ctr docker docker-init docker-proxy dockerd dockerd-rootless-setuptool.sh dockerd-rootless.sh rootlesskit rootlesskit-docker-proxy runc vpnkit

Rootless Docker vs Podman

Red Hat’s Podman also runs containers rootlessly and offers comparable functionality and performance. Podman supports docker run --net=host, which Rootless Docker does not.

Summary

Docker Rootless mode is an official security solution that runs the Docker daemon as an ordinary user, preventing containers from gaining host‑level root privileges. However, operating Docker without root also imposes functional limitations, which are documented in the official Docker documentation.

(© Original author, all rights reserved)

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

LinuxSecurityInstallationContainersRootlessUser Namespaces
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.