I have been playing heavily with docker in the last couple of weeks and the idea of encapsulating applications including all of their dependencies and cruft they bring into a kind of ‘sub-system’ that only has well defined shared resources with the host did not only speak to me when thinking about servers and development environments. I have seen a trend with modern, closed source applications: They all start to provide their own repository for your package manager instead of bothering with the official ones. Adding a third party repository to your package manager simply to install spotify or slack is a question of trust - the list of third party repositories should be minimal.
Dockerize it
Since in Linux everything is a file and docker can mount files to containers the thought of putting applications into containers is not very far fetched: It’s as easy as mounting the correct set of sockets to the container and the containerized application is able to talk to the system resources.
X11
In order for graphical output to work there are 3 things that need to be done:
- The host must allow remote connections to X11 (since the container is seen as remote from the point of X11). This can be done by using
xhost local:root
- The X11 socket (Located under
/tmp/.X11-unix
) needs to be mounted to the container - The
$DISPLAY
environment variable needs to be passed down to the container
To test if the connection to X11 is working correctly the following can be executed to setup a simple container containing the xeyes
application:
#!/bin/bash docker build -t 'thej6s/xeyes' - << __EOF__ FROM debian RUN apt-get update && apt-get install -y x11-apps ENV DISPLAY $DISPLAY CMD xeyes __EOF__ XSOCK=/tmp/.X11-unix xhost local:root docker run -v $XSOCK --net host 'thej6s/xeyes'
Sound: Alsa
The next big hardware device that a desktop application might want to use is sound input and output. The simplest way is to let the guest handle all of the audio related tasks using alsa acessing the audio device directly. This would work similar to the X11 socket above - but with the /dev/snd
device.
This works - but has a major drawback: It places all of the control over audio into the containers. Imagine having to ssh into multiple containers to regulate your volume.
Sound: Pulseaudio
Most distributions and most users are using pulseaudio in order to configure and manager their sound environment. A dockerized application should play into the global pulse instance instead of acessing the audio device directly. This way all dockerized applications are still managable by using a tool such as pavucontrol
on the host.
This however presents a couple of difficulties: - Pulseaudio is started as a user service and is bound to the current machine and user
In order to overcome these hurdles a couple of steps need to be taken: 1. Create an environment that is accepted by pulseaudio IPC - Create a user in the container with the same uid as the user on the host system - Mount /etc/machine-id
into the container 2. Mount the pulse audio socket (/run/user/${UID}/pulse
) into the container
The following starts firefox in a container with support for pulseaudio for sound:
XSOCK=/tmp/.X11-unix UID=$(id -u) docker build -t 'j6s/firefox' - << __EOF__ FROM debian RUN apt-get update && apt-get install -y firefox-esr ENV HOME /home/user RUN useradd -u ${UID} \ --create-home --home-dir \ /home/user user && \ usermod -a -G audio user && \ chown -R user:user /home/user USER user WORKDIR /home/user CMD firefox-esr __EOF__ docker run --rm \ -v $XSOCK:$XSOCK \ -v /etc/machine-id:/etc/machine-id \ -v /run/user/${UID}/pulse:/run/user/${UID}/pulse \ -e "DISPLAY=${DISPLAY}" \ --name firefox \ 'j6s/firefox' \
Spotify
Let’s revisit how I started this article: The idea of encapsulating third party closed source applications appealed to me - that was the point of all of this. Spotify is the easiest example, as all that it needs is X11 and sound output.
#!/bin/bash XSOCK=/tmp/.X11-unix UID=$(id -u) DIR=$(pwd) function run { echo -e "$ $@" eval $@ } run mkdir -p data/config data/cache run chown -R ${UID} data/ run chmod -R 755 data/ run docker build -t 'j6s/spotify' - << __EOF__ FROM debian RUN apt-get update && apt-get install -y gpg RUN apt-key adv \ --keyserver hkp://keyserver.ubuntu.com:80 \ --recv-keys 931FF8E79F0876134EDDBDCCA87FF9DF48BF1C90 && \ echo 'deb http://repository.spotify.com stable non-free' > /etc/apt/sources.list.d/spotify.list && \ apt-get update &&\ apt-get install -y -q --no-install-recommends spotify-client RUN apt-get install -y -q --no-install-recommends \ pulseaudio \ libgl1-mesa-dri \ libgl1-mesa-glx ENV HOME /home/user RUN useradd -u ${UID} --create-home --home-dir /home/user user && \ usermod -a -G audio user && \ chown -R user:user /home/user USER user WORKDIR /home/user CMD spotify __EOF__ run docker run --rm \ -v $XSOCK:$XSOCK \ -v /etc/machine-id:/etc/machine-id \ -v /run/user/${UID}/pulse:/run/user/${UID}/pulse \ -v ${DIR}/data/config:/home/user/.config \ -v ${DIR}/data/cache:/home/user/.cache \ -e "DISPLAY=${DISPLAY}" \ --name spotify \ 'j6s/spotify' \
Further reading
The following blogpost (and especially the github repository by the author) is very interesting when it comes to desktop applications running inside of containers: https://blog.jessfraz.com/post/docker-containers-on-the-desktop/
I wrote a follow-up article: Encapsulating nonfree applications using docker
Top comments (3)
Would love to see a video of this
I am actually currently preparing a talk for a local meetup that I am planning to record.
What is going to be about?