How can you run GUI applications in a Docker container?
Are there any images that set up vncserver
or something so that you can - for example - add an e
With docker data volumes it's very easy to expose xorg's unix domain socket inside the container.
For example, with a Dockerfile like this:
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
You could do the following:
$ docker build -t xeyes - < Dockerfile
$ XSOCK=/tmp/.X11-unix/X0
$ docker run -v $XSOCK:$XSOCK xeyes
This of course is essentially the same as X-forwarding. It grants the container full access to the xserver on the host, so it's only recommended if you trust what's inside.
Note: If you are concerned about security, a better solution would be to confine the app with mandatory- or role-based-access control. Docker achieves pretty good isolation, but it was designed with a different purpose in mind. Use AppArmor, SELinux, or GrSecurity, which were designed to address your concern.
You can allow the Docker user (here: root) to access the X11 display:
XSOCK=/tmp/.X11-unix
xhost +SI:localuser:root
docker run -t -i --rm -v $XSOCK:$XSOCK:ro -e DISPLAY=unix$(DISPLAY) image
xhost -SI:localuser:root
If you want to run a GUI application headless, then read here. What you have to do is to create a virtual monitor with xvfb
or other similar software. This is very helpful if you want to run Selenium tests for example with browsers.
Something not mentioned anywhere is that some software actually themselves use sand-boxing with Linux containers. So for example Chrome will never run normally if you don't use the appropriate flag --privileged
when running the container.
I managed to run a video stream from an USB camera using opencv
in docker
by following these steps:
Let docker access the X server
xhost +local:docker
Create the X11 Unix socket and the X authentication file
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
Add proper permissions
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
Set the Qt rendering speed to "native", so it doesn't bypass the X11 rendering engine
export QT_GRAPHICSSYSTEM=native
Tell Qt to not use MIT-SHM (shared memory) - that way it should be also safer security-wise
export QT_X11_NO_MITSHM=1
Update the docker run command
docker run -it \
-e DISPLAY=$DISPLAY \
-e XAUTHORITY=$XAUTH \
-v $XSOCK:$XSOCK \
-v $XAUTH:$XAUTH \
--runtime=nvidia \
--device=/dev/video0:/dev/video0 \
nvcr.io/nvidia/pytorch:19.10-py3
Note: When you finish the the project, return the access controls at their default value - xhost -local:docker
More details: Using GUI's with Docker
Credit: Real-time and video processing object detection using Tensorflow, OpenCV and Docker
Here's a lightweight solution that avoids having to install any X
server, vnc
server or sshd
daemon on the container. What it gains in simplicity it loses in security and isolation.
It assumes that you connect to the host machine using ssh
with X11
forwarding.
In the sshd
configuration of the host, add the line
X11UseLocalhost no
So that the forwarded X server port on the host is opened on all interfaces (not just lo
) and in particular on the Docker virtual interface, docker0
.
The container, when run, needs access to the .Xauthority
file so that it can connect to the server. In order to do that, we define a read-only volume pointing to the home directory on the host (maybe not a wise idea!) and also set the XAUTHORITY
variable accordingly.
docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority
That is not enough, we also have to pass the DISPLAY variable from the host, but substituting the hostname by the ip:
-e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")
We can define an alias:
alias dockerX11run='docker run -v $HOME:/hosthome:ro -e XAUTHORITY=/hosthome/.Xauthority -e DISPLAY=$(echo $DISPLAY | sed "s/^.*:/$(hostname -i):/")'
And test it like this:
dockerX11run centos xeyes
While the answer by Jürgen Weigert essentially covers this solution, it wasn't clear to me at first what was being described there. So I'll add my take on it, in case anyone else needs clarification.
First off, the relevant documentation is the X security manpage.
Numerous online sources suggest just mounting the X11 unix socket and the ~/.Xauthority
file into the container. These solutions often work by luck, without really understanding why, e.g. the container user ends up with the same UID as the user, so there's no need for magic key authorization.
First off, the Xauthority file has mode 0600, so the container user won't be able to read it unless it has the same UID.
Even if you copy the file into the container, and change the ownership, there's still another problem. If you run xauth list
on the host and container, with the same Xauthority
file, you'll see different entries listed. This is because xauth
filters the entries depending on where it's run.
The X client in the container (i.e. GUI app) will behave the same as xauth
. In other words, it doesn't see the magic cookie for the X session running on the user's desktop. Instead, it sees the entries for all the "remote" X sessions you've opened previously (explained below).
So, what you need to do is add a new entry with the hostname of the container and the same hex key as the host cookie (i.e. the X session running on your desktop), e.g.:
containerhostname/unix:0 MIT-MAGIC-COOKIE-1 <shared hex key>
The catch is that the cookie has to be added with xauth add
inside the container:
touch ~/.Xauthority
xauth add containerhostname/unix:0 . <shared hex key>
Otherwise, xauth
tags it in a way that it's only seen outside the container.
The format for this command is:
xauth add hostname/$DISPLAY protocol hexkey
Where .
represents the MIT-MAGIC-COOKIE-1
protocol.
Note: There's no need to copy or bind-mount .Xauthority
into the container. Just create a blank file, as shown, and add the cookie.
Jürgen Weigert's answer gets around this by using the FamilyWild
connection type to create a new authority file on the host and copy it into the container. Note that it first extracts the hex key for the current X session from ~/.Xauthority
using xauth nlist
.
So the essential steps are:
FamilyWild
connection type). I admit that I don't understand very well how FamilyWild
works, or how xauth
or X clients filter entries from the Xauthority file depending where they're run. Additional information on this is welcome.
If you want to distribute your Docker app, you'll need a start script for running the container that gets the hex key for the user's X session, and imports it into the container in one of the two ways explained previously.
It also helps to understand the mechanics of the authorization process:
$DISPLAY
. /tmp/.X11-unix
directory mounted in the container.Note: The X11 Unix socket still needs to be mounted in the container, or the container will have no route to the X server. Most distributions disable TCP access to the X server by default for security reasons.
For additional information, and to better grasp how the X client/server relationship works, it's also helpful to look at the example case of SSH X forwarding:
$DISPLAY
in the SSH session to point to its own X server. xauth
to create a new cookie for the remote host, and adds it to the Xauthority
files for both the local and remote users.