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?