The primary reason to change the data directory for docker to the home directory for user
is to have persistence across AppVM restarts. The secondary reason is to work around permission issues.
Here we provide "the Qubes way" of solving for both that does not leave "bind-dirs" nor "docker" in a broken state.
Step 1: TemplateVM - Perform installation of docker via your package manager, and ensure services are disabled
sudo apt install -y docker.io
sudo systemctl disable containerd
sudo systemctl disable docker
According to docker documentation, by adding user
to a docker
group there will not be a need to sudo docker
to access docker engine via unix socket. This, in turn, will eliminate the situation where files created by docker cli have their ownership set to root:root.
This is ideal.
Most packaged versions of docker will create the docker
group for you, but the below bash snippet will check for the group and create it if missing, then add user
to the docker
group:
[[ $(getent group docker) == '' ]] && sudo groupadd docker;
sudo usermod -aG docker user;
If you do not want this change on the TemplateVM for security reasons, you could add the above script to your AppVM-specific /rw/config/rc.local
instead for the same effect, but anyone that has access to user
has access to root
and can do "all the things" anyway.
While it's possible to move the docker working directory via config into /home/user/ it's not necessary. It's a facility that exists for subsystems which do not allow rootfs modification. "The Qubes Way" is to configure "bind-dirs", this technique applies not only to docker but any and all services, tools, etc which normally operate off the rootfs for their configuration.
The following bash snippet will create the necessary "bind-dirs" configuration, all referenced directories should already exist and be populated with relevant files, but there are checks to create them anyway for anyone that has performed a "binary install" rather than a "package install":
# ensure bind-dirs exist with expected privs
[ ! -d /etc/docker ] && sudo mkdir -p /etc/docker && sudo chmod 755 /etc/docker;
[ ! -d /var/lib/docker ] && sudo mkdir -p /var/lib/docker && sudo chmod 710 /var/lib/docker;
# do bind-dirs setup
sudo mkdir -p /etc/qubes-bind-dirs.d
sudo touch /etc/qubes-bind-dirs.d/30_docker.conf
sudo chmod 644 /etc/qubes-bind-dirs.d/30_docker.conf
echo "binds+=( '/var/lib/docker' )" | sudo tee --append /etc/qubes-bind-dirs.d/30_docker.conf
echo "binds+=( '/etc/docker' )" | sudo tee --append /etc/qubes-bind-dirs.d/30_docker.conf
Rather than run docker
as user
via rc.local
consider using the systemd units and scripts which already exist for the purpose of running docker as a service. Personally, I do this from the "Services" tab of "Qube Manager" for the target Qube, but, here is the CLI equivalent to be run from a dom0 shell. Remember to change $QUBENAME
to be the name of your target AppVM/Qube:
qvm-service --enable $QUBENAME docker
Exit the dom0 shell. Shutdown your TemplateVM, saving all changes. Launch a terminal in your AppVM to perform verifications.
In your AppVM, the following commands should execute without any errors as evidence you have set everything up correctly, systemctl status commands should show each service is "active / running" (you may get a journalctl warning because you are not running the command as root, which is fine.) Once you are in busybox you can uname -a
for confirmation or just exit
.
systemctl status containerd
systemctl status docker
docker run --rm -it busybox
After exiting busybox, you can verify "bind-dirs" is working as intended by restarting the AppVM (sudo reboot
), then launching a new AppVM terminal verify the busybox image persisted between restarts (optionally removing the image):
docker images
docker rmi busybox
- It preserves the privilege model and run time environment intended by docker devs and/or package authors.
- It leverages package-supplied units for service start-up and configuration, which may change over time.
- It is DispVM friendly.
Generally, this eliminates any configuration steps from being "inside the AppVM", any time we have to modify /rw/
directly from "inside" an AppVM we're taking an "advanced approach" to administration of our Qubes meant for solving problems that can't be solved by any regular means. To illustrate: Why bootstrap docker from the TemplateVM? Why not bootstrap everything from rc.local
? Because it is inconvenient, and not necessary, unless you are trying to keep everything outside of the TemplateVM and only need docker in a single AppVM.
These means and reasons apply to more than just docker.