|
| 1 | +--- |
| 2 | +description: Tips for the Rootless mode |
| 3 | +keywords: security, namespaces, rootless |
| 4 | +title: Tips |
| 5 | +weight: 20 |
| 6 | +--- |
| 7 | + |
| 8 | +## Advanced usage |
| 9 | + |
| 10 | +### Daemon |
| 11 | + |
| 12 | +{{< tabs >}} |
| 13 | +{{< tab name="With systemd (Highly recommended)" >}} |
| 14 | + |
| 15 | +The systemd unit file is installed as `~/.config/systemd/user/docker.service`. |
| 16 | + |
| 17 | +Use `systemctl --user` to manage the lifecycle of the daemon: |
| 18 | + |
| 19 | +```console |
| 20 | +$ systemctl --user start docker |
| 21 | +``` |
| 22 | + |
| 23 | +To launch the daemon on system startup, enable the systemd service and lingering: |
| 24 | + |
| 25 | +```console |
| 26 | +$ systemctl --user enable docker |
| 27 | +$ sudo loginctl enable-linger $(whoami) |
| 28 | +``` |
| 29 | + |
| 30 | +Starting Rootless Docker as a systemd-wide service (`/etc/systemd/system/docker.service`) |
| 31 | +is not supported, even with the `User=` directive. |
| 32 | + |
| 33 | +{{< /tab >}} |
| 34 | +{{< tab name="Without systemd" >}} |
| 35 | + |
| 36 | +To run the daemon directly without systemd, you need to run `dockerd-rootless.sh` instead of `dockerd`. |
| 37 | + |
| 38 | +The following environment variables must be set: |
| 39 | +- `$HOME`: the home directory |
| 40 | +- `$XDG_RUNTIME_DIR`: an ephemeral directory that is only accessible by the expected user, e,g, `~/.docker/run`. |
| 41 | + The directory should be removed on every host shutdown. |
| 42 | + The directory can be on tmpfs, however, should not be under `/tmp`. |
| 43 | + Locating this directory under `/tmp` might be vulnerable to TOCTOU attack. |
| 44 | + |
| 45 | +{{< /tab >}} |
| 46 | +{{< /tabs >}} |
| 47 | + |
| 48 | +It's important to note that with directory paths: |
| 49 | + |
| 50 | +- The socket path is set to `$XDG_RUNTIME_DIR/docker.sock` by default. |
| 51 | + `$XDG_RUNTIME_DIR` is typically set to `/run/user/$UID`. |
| 52 | +- The data dir is set to `~/.local/share/docker` by default. |
| 53 | + The data dir should not be on NFS. |
| 54 | +- The daemon config dir is set to `~/.config/docker` by default. |
| 55 | + This directory is different from `~/.docker` that is used by the client. |
| 56 | + |
| 57 | +### Client |
| 58 | + |
| 59 | +Since Docker Engine v23.0, `dockerd-rootless-setuptool.sh install` automatically configures |
| 60 | +the `docker` CLI to use the `rootless` context. |
| 61 | + |
| 62 | +Prior to Docker Engine v23.0, a user had to specify either the socket path or the CLI context explicitly. |
| 63 | + |
| 64 | +To specify the socket path using `$DOCKER_HOST`: |
| 65 | + |
| 66 | +```console |
| 67 | +$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock |
| 68 | +$ docker run -d -p 8080:80 nginx |
| 69 | +``` |
| 70 | + |
| 71 | +To specify the CLI context using `docker context`: |
| 72 | + |
| 73 | +```console |
| 74 | +$ docker context use rootless |
| 75 | +rootless |
| 76 | +Current context is now "rootless" |
| 77 | +$ docker run -d -p 8080:80 nginx |
| 78 | +``` |
| 79 | + |
| 80 | +## Best practices |
| 81 | + |
| 82 | +### Rootless Docker in Docker |
| 83 | + |
| 84 | +To run Rootless Docker inside "rootful" Docker, use the `docker:<version>-dind-rootless` |
| 85 | +image instead of `docker:<version>-dind`. |
| 86 | + |
| 87 | +```console |
| 88 | +$ docker run -d --name dind-rootless --privileged docker:25.0-dind-rootless |
| 89 | +``` |
| 90 | + |
| 91 | +The `docker:<version>-dind-rootless` image runs as a non-root user (UID 1000). |
| 92 | +However, `--privileged` is required for disabling seccomp, AppArmor, and mount |
| 93 | +masks. |
| 94 | + |
| 95 | +### Expose Docker API socket through TCP |
| 96 | + |
| 97 | +To expose the Docker API socket through TCP, you need to launch `dockerd-rootless.sh` |
| 98 | +with `DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp"`. |
| 99 | + |
| 100 | +```console |
| 101 | +$ DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="-p 0.0.0.0:2376:2376/tcp" \ |
| 102 | + dockerd-rootless.sh \ |
| 103 | + -H tcp://0.0.0.0:2376 \ |
| 104 | + --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem |
| 105 | +``` |
| 106 | + |
| 107 | +### Expose Docker API socket through SSH |
| 108 | + |
| 109 | +To expose the Docker API socket through SSH, you need to make sure `$DOCKER_HOST` |
| 110 | +is set on the remote host. |
| 111 | + |
| 112 | +```console |
| 113 | +$ ssh -l <REMOTEUSER> <REMOTEHOST> 'echo $DOCKER_HOST' |
| 114 | +unix:///run/user/1001/docker.sock |
| 115 | +$ docker -H ssh://<REMOTEUSER>@<REMOTEHOST> run ... |
| 116 | +``` |
| 117 | + |
| 118 | +### Routing ping packets |
| 119 | + |
| 120 | +On some distributions, `ping` does not work by default. |
| 121 | + |
| 122 | +Add `net.ipv4.ping_group_range = 0 2147483647` to `/etc/sysctl.conf` (or |
| 123 | +`/etc/sysctl.d`) and run `sudo sysctl --system` to allow using `ping`. |
| 124 | + |
| 125 | +### Exposing privileged ports |
| 126 | + |
| 127 | +To expose privileged ports (< 1024), set `CAP_NET_BIND_SERVICE` on `rootlesskit` binary and restart the daemon. |
| 128 | + |
| 129 | +```console |
| 130 | +$ sudo setcap cap_net_bind_service=ep $(which rootlesskit) |
| 131 | +$ systemctl --user restart docker |
| 132 | +``` |
| 133 | + |
| 134 | +Or add `net.ipv4.ip_unprivileged_port_start=0` to `/etc/sysctl.conf` (or |
| 135 | +`/etc/sysctl.d`) and run `sudo sysctl --system`. |
| 136 | + |
| 137 | +### Limiting resources |
| 138 | + |
| 139 | +Limiting resources with cgroup-related `docker run` flags such as `--cpus`, `--memory`, `--pids-limit` |
| 140 | +is supported only when running with cgroup v2 and systemd. |
| 141 | +See [Changing cgroup version](/manuals/engine/containers/runmetrics.md) to enable cgroup v2. |
| 142 | + |
| 143 | +If `docker info` shows `none` as `Cgroup Driver`, the conditions are not satisfied. |
| 144 | +When these conditions are not satisfied, rootless mode ignores the cgroup-related `docker run` flags. |
| 145 | +See [Limiting resources without cgroup](#limiting-resources-without-cgroup) for workarounds. |
| 146 | + |
| 147 | +If `docker info` shows `systemd` as `Cgroup Driver`, the conditions are satisfied. |
| 148 | +However, typically, only `memory` and `pids` controllers are delegated to non-root users by default. |
| 149 | + |
| 150 | +```console |
| 151 | +$ cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers |
| 152 | +memory pids |
| 153 | +``` |
| 154 | + |
| 155 | +To allow delegation of all controllers, you need to change the systemd configuration as follows: |
| 156 | + |
| 157 | +```console |
| 158 | +# mkdir -p /etc/systemd/system/[email protected] |
| 159 | +# cat > /etc/systemd/system/[email protected]/delegate.conf << EOF |
| 160 | +[Service] |
| 161 | +Delegate=cpu cpuset io memory pids |
| 162 | +EOF |
| 163 | +# systemctl daemon-reload |
| 164 | +``` |
| 165 | + |
| 166 | +> [!NOTE] |
| 167 | +> |
| 168 | +> Delegating `cpuset` requires systemd 244 or later. |
| 169 | +
|
| 170 | +#### Limiting resources without cgroup |
| 171 | + |
| 172 | +Even when cgroup is not available, you can still use the traditional `ulimit` and [`cpulimit`](https://github.com/opsengine/cpulimit), |
| 173 | +though they work in process-granularity rather than in container-granularity, |
| 174 | +and can be arbitrarily disabled by the container process. |
| 175 | + |
| 176 | +For example: |
| 177 | + |
| 178 | +- To limit CPU usage to 0.5 cores (similar to `docker run --cpus 0.5`): |
| 179 | + `docker run <IMAGE> cpulimit --limit=50 --include-children <COMMAND>` |
| 180 | +- To limit max VSZ to 64MiB (similar to `docker run --memory 64m`): |
| 181 | + `docker run <IMAGE> sh -c "ulimit -v 65536; <COMMAND>"` |
| 182 | + |
| 183 | +- To limit max number of processes to 100 per namespaced UID 2000 |
| 184 | + (similar to `docker run --pids-limit=100`): |
| 185 | + `docker run --user 2000 --ulimit nproc=100 <IMAGE> <COMMAND>` |
0 commit comments