Historically, Docker Engine or Docker has always required root privileges to run. This is because certain features like namespaces or mount points which forms the basis of Docker filesystems have always required elevated privileges. You may have started running docker daemon or dockerd in context of another user, but that user needs to be made part of Docker Group, which was assigned root privileges during installation time. Rootless mode means running the Docker daemon and even containers as an unprivileged user to protect the root user from future attacks on the host system.
Since Docker Engine is comprised of whole stack of smaller components – runc, containerd, dockerd, etc., running in rootless mode means running the whole stack in rootless mode. Also since dockerd itself is running as a non-root user, the containers launched will also not have any root privileges associated with them. So this mode protects the host system from potential attacks that exploit vulnerabilities in the application code or misconfiguration arising from dockerd or containerd or runc.
Rootless mode was introduced in Docker version 19.03 as an experimental feature and it had some disadvantages. In Particular, it did not supported cgroups and also did not supported OverlayFS, so you could not define resource limitations for rootless containers. However starting with Docker Engine v20.10., some of these limitations have been removed and it is considered ready for general usage.
What Looks like rootless but is not?
docker run --user foo: It allows you to execute process in containers as non-root. Notably you cannot perform privileged activities like package installation etc. runc, containerd, etc still run as root.
usermod -aG docker foo: Allows a non-root user to connect to docker socket. It is equivalent to allow user to run as root.
chmod +s dockerd: Needs no explanation
dockerd --userns-remap: It allows you to run containers as non-root. runc, containerd, etc still run as root.
How does rootless mode works in Docker?
As we understand it, a lot of docker engine features requires root privileges. Rootless mode works around this restriction by taking advantage of something called user namespaces. User namespaces map a range of user IDs so that the root user in the inner namespace maps to an unprivileged range in the parent namespace. A fresh process in user namespace also picks up a full set of process capabilities.
User namespaces has been around since Linux kernel v3.8, so this feature has been present in Docker for a long time with the
--userns-remap flag. The rootless mode works in a similar way, except that it creates a user namespace first and start the daemon already in the remapped namespace. The daemon and the containers will both use the same user namespace that is different from the host one:
Pre-requisites / Dependencies
Rootless mode has a dependency on the
uidmap package that can do the remapping of users. This package provides required binaries for it to work.
Install Docker in Rootless mode
For installing docker engine in rootless mode you do not need root privileges on the host system. You do not need to run any command as
sudo or need access to package managers like apt, dnf, yum, etc. as well. Installation steps are covered in detail at https://docs.docker.com/engine/security/rootless/. This is TL;DR version of the same for Ubuntu 20.04 LTS:
- Install uidmap package with sudo privileges or ask your system admin to do it for you:
apt-get install -y uidmap
cloud_user@d7e5dc06581c:~$ sudo apt-get install -y uidmap
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 26.3 kB of archives.
After this operation, 171 kB of additional disk space will be used.
Get:1 http://ap-southeast-1.ec2.archive.ubuntu.com/ubuntu focal-updates/universe amd64 uidmap amd64 1:4.8.1-1ubuntu5.20.04 [26.3 kB]
Fetched 26.3 kB in 1s (50.4 kB/s)
Selecting previously unselected package uidmap.
(Reading database ... 193042 files and directories currently installed.)
Preparing to unpack .../uidmap_1%3a4.8.1-1ubuntu5.20.04_amd64.deb ...
Unpacking uidmap (1:4.8.1-1ubuntu5.20.04) ...
Setting up uidmap (1:4.8.1-1ubuntu5.20.04) ...
Processing triggers for man-db (2.9.1-1) ...
- Grab installation script from Docker and run it:
curl -fsSL https://get.docker.com/rootless | sh
cloud_user@d7e5dc06581c:~$ curl -fsSL https://get.docker.com/rootless | sh
# Installing stable version 20.10.5
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 65.9M 100 65.9M 0 0 142M 0 --:--:-- --:--:-- --:--:-- 142M
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger cloud_user`
[INFO] Make sure the following environment variables are set (or add them to ~/.bashrc):
- Export the environment variables as mentioned or add them to ~/.bashrc. In our case, we’ll edit ~/.bashrc and then reload profile using
- Start dockerd with systemd:
systemctl --user start docker
Verify Installation and Run Containers
We can now use
docker version to verify docker version installed and run containers using
cloud_user@d7e5dc06581c:~$ docker version
Client: Docker Engine - Community
API version: 1.41
Go version: go1.13.15
Git commit: 55c4c88
Built: Tue Mar 2 20:14:11 2021
Server: Docker Engine - Community
API version: 1.41 (minimum version 1.12)
Go version: go1.13.15
Git commit: 363e9a8
Built: Tue Mar 2 20:18:31 2021
cloud_user@d7e5dc06581c:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
For more examples and ideas, visit:
Limitations of Rootless Mode
Exposing Network Ports
Be aware that port numbers below 1024 are called privileged ports and not available for rootless users. So you will need to use unprivileged ports like 8080, etc. So if you want to run HTTP server, you need to run
docker run -p 8080:80. However, if you really need to expose privileged ports, you can do that by adjusting sysctl
/proc/sys/net/ipv4/ip_unprivileged_port_start or by setting CAP_NET_BIND SERVICE capability on binary
Limiting Resources such as CPU, Memory
Limiting resources with cgroup-related
docker run flags such as
--pids-limit is supported only when running with cgroup v2 and systemd. See changing cgroup version to enable cgroup v2 for commands related to your distro.
docker info shows
Cgroup Driver, the conditions are not satisfied. When these conditions are not satisfied, rootless mode ignores the cgroup-related
docker run flags.
Other not supported features
AppArmor, Checkpoint, Overlay network, Exposing SCTP Ports
Supported Storage Drivers
Only the following storage drivers are supported as of writing of this post:
overlay2(only if running with kernel 5.11 or later, or Ubuntu-flavored kernel, or Debian-flavored kernel)
fuse-overlayfs(only if running with kernel 4.18 or later, and
Rootless Docker vs Podman
Podman from RedHat Inc, is another popular container engine to run and manage containers. It hails running in rootless mode as one of its features over docker engine. With this Docker Inc, has bridged the gap and now they have almost the same features with almost the same performance. They also use lot of shared code between them.
Rootless Docker doesn’t support specifying
docker run --net=host, but on the other hand, Rootless Podman doesn't support creating custom networks with docker network create`. But if you really need to use
docker run --net=host, Podman might be a better choice for you.
Other than this, podman-compose is still a work in progress and cannot be considered a replacement of docker-compose.
3 thoughts on “Going rootless with Docker and Containers”
[…] of the pre-requisites for k3d to work is docker. You can refer official instructions for same or one of our previous posts for installing docker in rootless […]
Have you ever tried as “rootless user” to do a docker login to a repo different to dockerhub? I’m not able to login it always fails.
Thanks, I installed this for two different users on my system (ubuntu). For one it automatically selected the overlay2 for the other the vfs drivers. Not sure why. But now: how can I tell it to use overlay2 for the second rootless user? where is the daemon.json file (or similar)?