Docker & DevOps

Run Docker in rootless mode on Ubuntu

Set up Docker rootless mode on Ubuntu so the daemon and containers run as a normal user. Covers the setup tool, the user systemd service, ports below 1024, storage and the trade-offs.

AdvancedLinux server5 min read

Normally the Docker daemon runs as root, and anyone in the docker group effectively has root on the server. Rootless mode runs the daemon and every container under an ordinary user account instead. If a container is compromised, the attacker lands in that user's account, not root's. This guide sets it up on Ubuntu and explains what you give up in return.

Before you start

  • A Linux VPS running Ubuntu 22.04 or 24.04, with a sudo user.
  • Docker Engine installed from Docker's official apt repository, including the docker-ce-rootless-extras package, which the standard install pulls in. See Install Docker and Docker Compose.
  • A dedicated non-root user that will own the rootless daemon, for example deploy. Create one with Create a sudo user on Linux if needed, but rootless Docker itself does not need sudo rights.

Warning: The steps below stop the system-wide Docker daemon. Containers running under it will stop. Plan a maintenance window, or test on a fresh server first.

Install the prerequisites

Rootless mode maps a range of user IDs into containers, which needs the uidmap tools:

Bash
sudo apt update
sudo apt install -y uidmap dbus-user-session

Check that your user has a subordinate ID range:

Bash
grep deploy /etc/subuid /etc/subgid

Each file should have a line such as deploy:100000:65536. Ubuntu adds one when the user is created. If it is missing, add it with sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 deploy.

Stop the root daemon

If you do not need the root daemon any more, disable it:

Bash
sudo systemctl disable --now docker.service docker.socket
sudo rm -f /var/run/docker.sock

You can keep both running side by side, but it is easy to talk to the wrong one, so most people disable the root daemon.

Run the setup tool

Log in directly as the rootless user over SSH. Do not use sudo su - deploy, because the user's systemd session and XDG_RUNTIME_DIR would not be set up.

Bash
ssh deploy@203.0.113.10
dockerd-rootless-setuptool.sh install

The tool checks requirements, creates a user-level systemd service called docker.service and starts it. If a requirement is missing, it prints the exact command to fix it. On Ubuntu 24.04, it may ask you to add an AppArmor profile for rootlesskit, because that release restricts unprivileged user namespaces. Follow the commands it prints, then run the setup again.

At the end it prints two environment lines. Add them to ~/.bashrc so every shell uses the rootless daemon:

Bash
echo 'export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock' >> ~/.bashrc
source ~/.bashrc

Start on boot

User services normally stop when you log out. Enable lingering so the daemon starts at boot and keeps running:

Bash
systemctl --user enable docker
sudo loginctl enable-linger deploy

Run the loginctl command from an account with sudo rights if deploy has none.

Allow ports below 1024

Unprivileged processes cannot bind ports below 1024, so publishing -p 80:80 fails. You have three options:

  • Publish high ports (8080, 8443) and let a reverse proxy or firewall rule forward 80 and 443.
  • Allow unprivileged users to bind from port 80 upwards, system-wide:
Bash
echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-rootless.conf
sudo sysctl --system
  • Give the rootlesskit binary the capability to bind low ports. The Docker documentation describes this with setcap; check it for the current command.

The sysctl change lets any local user open ports 80 and up, so use it only on a server where you trust every account.

Storage and other differences

  • Images, containers and volumes live under ~/.local/share/docker, not /var/lib/docker. Make sure the home partition has room.
  • The root daemon's images are not visible. Pull or build them again as the rootless user.
  • Resource limits such as --memory and --cpus need cgroup v2 with delegation, which Ubuntu 22.04 and 24.04 use by default.
  • Rootless networking is slower than the root bridge for heavy traffic, and the source IP seen by containers can differ. Check the Docker rootless documentation for the port driver options if you need the client's real IP.
  • Some features need real root. For example, --network host gives a container the network of the rootless sandbox, not the real host network, and AppArmor profiles and some storage drivers are not available.

Rootless mode versus the docker group

Adding a user to the docker group is simpler, but that user can start a container that mounts / and so has full root access. Rootless mode removes that risk at the cost of the limitations above. For a single-purpose VPS where only you log in, the docker group with other hardening is often enough. For shared servers or untrusted images, rootless is the stronger choice.

Check it works

Bash
docker info --format '{{.SecurityOptions}}'
docker run --rm hello-world

The first line should include name=rootless. Then confirm containers run as your user on the host:

Bash
docker run -d --name sleeper alpine sleep 300
ps -o user,cmd -C sleep
docker rm -f sleeper

The sleep process should belong to deploy, not root.

Troubleshooting

  • Cannot connect to the Docker daemon at unix:///var/run/docker.sock: DOCKER_HOST is not set in this shell. Source ~/.bashrc or export it again.
  • systemctl --user fails with Failed to connect to bus: you switched user with su or sudo. Log in directly over SSH as that user.
  • Containers stop when you log out: lingering is not enabled. Run sudo loginctl enable-linger deploy.
  • newuidmap errors: uidmap is not installed or /etc/subuid has no entry for the user.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.