How can you run GUI apps in a docker container?

Are there any images that set up vncserver or something so that you can - for example - add an extra speedbump sandbox around say Firefox?

share

13 Answers 13

up vote 143 down vote accepted

You can simply install a vncserver along with firefox :)

I pushed an image vnc/firefox here: docker pull creack/firefox-vnc

The image has been made with this Dockerfile:

# Firefox over VNC
#
# VERSION               0.1
# DOCKER-VERSION        0.2

from    ubuntu:12.04
# make sure the package repository is up to date
run     echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
run     apt-get update

# Install vnc, xvfb in order to create a 'fake' display and firefox
run     apt-get install -y x11vnc xvfb firefox
run     mkdir ~/.vnc
# Setup a password
run     x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way to do it, but it does the trick)
run     bash -c 'echo "firefox" >> /.bashrc'

This will create a docker container running vnc with the password 1234:

For docker version 1.3 or newer:

docker run -p 5900 -e HOME=/ creack/firefox-vnc x11vnc -forever -usepw -create

For docker before version 1.3:

docker run -p 5900 creack/firefox-vnc x11vnc -forever -usepw -create

share
2  
How would I use a VNC client to view this remotely? Typing in the IP + port doesn't seem to be working. – user94154 Jul 30 '13 at 0:42
14  
First, you need to check the port allocated (by doing docker inspect <container id> or simply docker ps, then you connect to your host's ip with the port you just found. – creack Jul 30 '13 at 0:54
4  
the creackfirefox-vnc image fails with error: Enter VNC password: stty: standard input: Inappropriate ioctl for device fgets: No such file or directory stty: standard input: Inappropriate ioctl for device x11vnc -usepw: could not find a password to use. – alfonsodev Nov 11 '14 at 13:35
4  
Use docker well > Running GUI apps with Docker fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker – Dennis C Nov 17 '14 at 6:22
5  
There is no username, the password is clearly indicated in the answer and any vnc client will do. In my case, I like the native osx one. (from finder, press command+K and connect to vnc://<docker ip>:<container exposed port>) – creack Nov 25 '14 at 15:08

Xauthority becomes an issue with newer systems. I can either discard any protection with xhost + before running my docker containers, or I can pass in a well prepared Xauthority file. Typical Xauthority files are hostname specific. With docker, each container can have a different host name (set with docker run -h), but even setting the hostname of the container identical to the host system did not help in my case. xeyes (I like this example) simply would ignore the magic cookie and pass no credentials to the server. Hence we get an error message 'No protocol specified Cannot open display'

The Xauthority file can be written in a way so that the hostname does not matter. We need to set the Authentication Family to 'FamilyWild'. I am not sure, if xauth has a proper command line for this, so here is an example that combines xauth and sed to do that. We need to change the first 16 bits of the nlist output. The value of FamilyWild is 65535 or 0xffff.

docker build -t xeyes - << __EOF__
FROM debian
RUN apt-get update
RUN apt-get install -qqy x11-apps
ENV DISPLAY :0
CMD xeyes
__EOF__
XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth
xauth nlist :0 | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
docker run -ti -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH xeyes
share
17  
Note: This is the latest and most accurate answer to this problem. – James Mills Mar 2 '15 at 9:45
3  
Just a note, -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH can be shortened to -v $XSOCK -v $XAUTH – Piotr Aleksander Chmielowski Apr 13 '16 at 14:37
    
@PiotrAleksanderChmielowski that did not work for me, Docker version 1.12.0, build 8eab29e – tbc0 Aug 6 '16 at 19:02
1  
@tbc0 - interesting. Maybe it is a bug to report? – Piotr Aleksander Chmielowski Aug 23 '16 at 8:12
4  
@Dirk: You might want to replace :0 with $DISPLAY. That means xauth nlist $DISPLAY | ... and docker run -ti -e DISPLAY=$DISPLAY .... Usually the X DISPLAY is :0, but not always (and especially not if you are connecting via ssh -X). – johndodo Dec 14 '16 at 9:15

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.

share
5  
You also need to allow access to the X Server from other hosts using a tool like xhost. To completely open it up use xhost + on the host. – Tully Sep 26 '14 at 21:13
3  
@Tully only xhost +local is necessary. It would be better to make the ~/.Xauthority file available in the container however, so it can authenticate itself. – Aryeh Leib Taurog Oct 1 '14 at 14:01
2  
have you managed to get it working on a Mac (using boot2docker) ? – Karl Forner Nov 3 '14 at 15:39
3  
This was working rather nicely for me on an Ubuntu 14.04 laptop with docker 1.5 earlier; but is now failing for me on Ubuntu 15.04, docker 1.6.2, with the error Can't open display: :0. Any ideas? – cboettig Jun 10 '15 at 4:30
2  
I used xhost +si:localuser:$USER to authorise just the user starting the container. – Nick Breen Jan 9 '16 at 3:46

i just found this blog entry and want to share it here with you because i think it is the best way to do it and it is so easy.

http://fabiorehm.com/blog/2014/09/11/running-gui-apps-with-docker/

PROS:
+ no x server stuff in the docker container
+ no vnc client/server needed
+ no ssh with x forwarding
+ much smaller docker containers

CONS:
- using x on the host (not ment for secure-sandboxing)

in case the link will fail some day i have put the most important part here:
dockerfile:

FROM ubuntu:14.04

RUN apt-get update && apt-get install -y firefox

# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
    mkdir -p /home/developer && \
    echo "developer:x:${uid}:${gid}:Developer,,,:/home/developer:/bin/bash" >> /etc/passwd && \
    echo "developer:x:${uid}:" >> /etc/group && \
    echo "developer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer && \
    chown ${uid}:${gid} -R /home/developer

USER developer
ENV HOME /home/developer
CMD /usr/bin/firefox

build the image:

docker build -t firefox .

and the run command:

docker run -ti --rm \
   -e DISPLAY=$DISPLAY \
   -v /tmp/.X11-unix:/tmp/.X11-unix \
   firefox

of course you can also do this in the run command with sh -c "echo script-here"

HINT: for audio take a look at: http://stackoverflow.com/a/28985715/2835523

share
    
This is a great little recipe, thanks for sharing! Its using the xserver on the host, so its not recommended from a secure-sandboxing perspective. – Will Mar 11 '15 at 6:24
    
i have added this as con - thx – A. Binzxxxxxx Mar 11 '15 at 11:00

You can also use subuser: https://github.com/timthelion/subuser

This allows you to package many gui apps in docker. Firefox and emacs have been tested so far. With firefox, webGL doesn't work though. Chromium doesn't work at all.

EDIT: Sound works!

EDIT2: In the time since I first posted this, subuser has progressed greatly. I now have a website up subuser.org, and a new security model for connecting to X11 via XPRA bridging.

share
3  
Please note that subuser is still very new and relatively untested. If you run into any problems please submit bug reports! – timthelion Feb 11 '14 at 22:52
    
I'd avoid X11 if there's any way you can. Your killer app would be running the tor proxy in docker, and running a full browser with plugins in a child docker such that firewalling etc forces all network out via the tor docker. This would run laps around the current tor browser bundle for web usability because you'd let rich content through. – Will Feb 12 '14 at 6:31
    
Is the trouble for you with X11 security? Or is it that you want this working with windows? Or that you want this to work remotely? All of the above? I think that making this work with vnc is quite possible(though I wouldn't make it the default method because it adds a dependency on vnc). Making subuser work remotely isn't really possible/meaningfull. There is also this: github.com/rogaha/docker-desktop but from the bug reports it seems xpra might be unusable in real life. – timthelion Feb 12 '14 at 10:45

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
share
2  
(This is great for trusted apps. For any kind of sandboxing, though, you want to avoid X-forwarding.) – Will Jun 30 '14 at 22:56
1  
If you'd rather not mount the whole home directory into the container you can just mount the .Xauthority file itself: -v $HOME/.Xauthority:/root/.Xauthority -e XAUTHORITY=/root/.Xauthority. – Robert Haines Dec 24 '15 at 8:50
1  
Instead of changing X11UseLocalhost, you can also use the additional option --net=host for the docker run command (found here). – ingomueller.net Apr 10 at 11:49

OSX

Jürgen Weigert has the best answer that worked for me on Ubuntu, however on OSX, docker runs inside of VirtualBox and so the solution doesn't work without some more work.

I've got it working with these additional ingredients:

  1. Xquartz (OSX no longer ships with X11 server)
  2. socket forwarding with socat (brew install socat)
  3. bash script to launch the container

I'd appreciate user comments to improve this answer for OSX, I'm not sure if socket forwarding for X is secure, but my intended use is for running the docker container locally only.

Also, the script is a bit fragile in that it's not easy to get the IP address of the machine since it's on our local wireless so it's always some random IP.

The BASH script I use to launch the container:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
NIC=en0

# Grab the ip address of this box
IPADDR=$(ifconfig $NIC | grep "inet " | awk '{print $2}')

DISP_NUM=$(jot -r 1 100 200)  # random display number between 100 and 200

PORT_NUM=$((6000 + DISP_NUM)) # so multiple instances of the container won't interfer with eachother

socat TCP-LISTEN:${PORT_NUM},reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" 2>&1 > /dev/null &

XSOCK=/tmp/.X11-unix
XAUTH=/tmp/.docker.xauth.$USER.$$
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/Users/$USER" \
    -v "/Users/$USER:/home/$USER:rw" \
    -v $XSOCK:$XSOCK:rw \
    -v $XAUTH:$XAUTH:rw \
    -e DISPLAY=$IPADDR:$DISP_NUM \
    -e XAUTHORITY=$XAUTH \
    $CONTAINER \
    $COMMAND

rm -f $XAUTH
kill %1       # kill the socat job launched above

I'm able to get xeyes and matplotlib working with this approach.

Windows 7+

It's a bit easier on Windows 7+ with MobaXterm:

  1. Install MobaXterm for windows
  2. Start MobaXterm
  3. Configure X server: Settings -> X11 (tab) -> set X11 Remote Access to full
  4. Use this BASH script to launch the container

run_docker.bash:

#!/usr/bin/env bash

CONTAINER=py3:2016-03-23-rc3
COMMAND=/bin/bash
DISPLAY="$(hostname):0"
USER=$(whoami)

docker run \
    -it \
    --rm \
    --user=$USER \
    --workdir="/home/$USER" \
    -v "/c/Users/$USER:/home/$USER:rw" \
    -e DISPLAY \
    $CONTAINER \
    $COMMAND

xeyes running on PC

share
    
i didnt understand what you meant by the bash script - how do i run it in windows? – deller May 1 at 18:29
    
@deller I do software development on windows using GIT, so I have the GIT-bash shell available to me. – Nick May 4 at 20:45

There is another solution by lord.garbage to run GUI apps in a container without using VNC, SSH and X11 forwarding. It is mentioned here too.

share
1  
This is great if security isn't a concern. If the purposes of dockering something are to isolate it, its best to avoid X11 in-out of the container. – Will Sep 24 '14 at 6:42

Sharing host display :0, as stated in some other answers, has two drawbacks:

  • It breaks container isolation due to some X security leaks. For example, keylogging with xev or xinput is possible.
  • Applications can have rendering glitches and bad RAM access errors due to missing shared memory for X extension MIT-SHM.

To circumvent X security leaks and to avoid MIT-SHM issue, I have published x11docker on github. The main idea is to run a second X server with its own authentication cookies and with MIT-SHM disabled. docker containers get access to the new X server and are segregated from host display :0. There are no X dependencies inside the image as X / Xephyr is provided by host.

Below an example script to run a docker image in Xephyr. It expects some arguments, first a host window manager to run in Xephyr, second a docker image, optionally third an image command to be executed. To run a desktop environment in docker, use ":" instead of a host window manager. On systems without a root password, change variable $Getroot

Xephyr is started using xinit. A custom xinitrc is created to create a cookie, to set keyboard layout, to run window manager, and to run xterm with xtermrc to prompt for password to run docker.

Closing Xephyr window terminates docker container applications. Terminating the dockered applications closes Xephyr window.

Annotations:

  • The MIT-SHM problem also can be avoided with docker option --ipc=host, but this breaks down container isolation and is discouraged.
  • The cookie has to be changed to "familiy wild" as described by @Jürgen Weigert if shared X socket is used. With X over tcp, this is not needed.
  • similar solutions are possible with Xpra, Xorg and Xvnc
  • For X over tcp, find out docker daemon ip (mostly 172.17.42.1), share DISPLAY=172.17.42.1:1, create cookie for it, don't share NewXsocket and don't use X/Xephyr option -nolisten tcp. Using X over tcp, MIT-SHM is disabled automatically.

Examples:

  • x11docker_example "openbox --sm-disable" x11docker/lxde pcmanfm
  • x11docker_example : x11docker/lxde

x11docker_example script:

#! /bin/bash
# x11docker_example : Example script to run docker GUI applications in Xephyr.
#                     Look at x11docker on github: https://github.com/mviereck/x11docker
#
# Usage:
#   x11docker_example WINDOWMANAGER DOCKERIMAGE [IMAGECOMMAND [ARGS]]
#
# WINDOWMANAGER     host window manager for use with single GUI applications.
#                   To run a desktop environment in docker, use ":"
# DOCKERIMAGE       docker image containing GUI applications or a desktop
# IMAGECOMMAND      command to run in image
#
Windowmanager="$1" && shift
Dockerimage="$*"

# command to get root permissions to run docker
Getroot="su -c"
# Getroot="sudo su -c"      # Use this on systems without a root password like Ubuntu or Sparky

# define new display and its X socket.
Newdisplay=:1
# To make sure $Newdisplay is not already in use, check /tmp/.Xn-lock
[ -e "/tmp/.X1-lock" ] && echo "Error: Display :1 is already in use" >&2 && exit 1
NewXsocket=/tmp/.X11-unix/X1


# cache folder and files
Cachefolder=/tmp/x11docker_example
mkdir -p $Cachefolder
Xclientcookie=$Cachefolder/Xcookie.client
Xservercookie=$Cachefolder/Xcookie.server
Xtermrc=$Cachefolder/xtermrc
Xinitrc=$Cachefolder/xinitrc
Dockerpidfile=$Cachefolder/docker.pid
Dockerlogfile=$Cachefolder/docker.log


# command to run docker
# --rm                           created container will be discarded.
# -e DISPLAY=$Newdisplay         set environment variable to new display
# -e XAUTHORITY=/Xcookie         set environment variable XAUTHORITY to provided cookie
# -v $Xclientcookie:/Xcookie:ro  provide cookie file to container
# -v $NewXsocket:$NewXsocket:ro  Share new X socket of Xephyr
Dockercommand="docker run --rm -e DISPLAY=$Newdisplay -e XAUTHORITY=/Xcookie -v $Xclientcookie:/Xcookie:ro -v $NewXsocket:$NewXsocket:ro $Dockerimage"  


# command to run X/Xephyr
# /usr/bin/Xephyr                an absolute path to X server executable must be given
# $Newdisplay                    first argument has to be new display
# -auth $Xservercookie           path to cookie file for X server. Must be different from cookie file of client, not sure why
# -nolisten tcp                  disable tcp connections for security reasons
# -extension MIT-SHM             disable MIT-SHM to avoid rendering glitches and bad RAM access (+ instead of - enables it)
# -retro                         nice retro look
Xcommand="/usr/bin/Xephyr $Newdisplay -auth $Xservercookie -nolisten tcp -extension MIT-SHM -retro"


# create xinitrc
{ echo "#! /bin/bash"

  echo "# set environment variables to new display and new cookie"
  echo "export DISPLAY=$Newdisplay"
  echo "export XAUTHORITY=$Xclientcookie"

  echo "# same keyboard layout as on host"
  echo "echo '$(setxkbmap -display $DISPLAY -print)' | xkbcomp - $Newdisplay"

  echo "# create new XAUTHORITY cookie file" 
  echo ":> $Xclientcookie"
  echo "xauth generate $Newdisplay . untrusted"
  echo "cp $Xclientcookie $Xservercookie"
  echo "# create prepared cookie with localhost identification disabled by ffff,"
  echo "# needed if X socket is shared instead connecting over tcp. ffff means 'familiy wild'"
  echo 'Cookie=$(xauth nlist '"$Newdisplay | sed -e 's/^..../ffff/')" 
  echo 'echo $Cookie | xauth -f '$Xclientcookie' nmerge -'

  echo "# run window manager in Xephyr"
  echo $Windowmanager' & Windowmanagerpid=$!'

  echo "# show docker log"
  echo 'tail --retry -n +1 -F '$Dockerlogfile' 2>/dev/null & Tailpid=$!'

  echo "# prompt for password"
  echo "xterm -title x11docker_example -e '/bin/bash $Xtermrc'"

  echo "# wait for docker to finish"
  echo 'Dockerpid=$(cat '$Dockerpidfile') && {'
  echo '  while [ -n "$(pgrep docker | grep $Dockerpid)" ] ; do'
  echo '    sleep 1'
  echo '  done }'

  [ "$Windowmanager" = ":" ] || echo 'kill $Windowmanagerpid'
  echo 'kill $Tailpid'
} > $Xinitrc


# create xtermrc for a password prompt
{ echo "#! /bin/bash"
  echo "echo 'x11docker_example will start docker on display $Newdisplay with command:'"
  echo "echo $Getroot '$Dockercommand'"
  echo "echo 'Please type in your password to run docker:'"
  echo "$Getroot 'nohup $Dockercommand 2>&1 1>$Dockerlogfile & echo "'$!'" > $Dockerpidfile'"
} > $Xtermrc


xinit  $Xinitrc -- $Xcommand
rm -Rf $Cachefolder
share

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.

share

For OpenGL rendering with the Nvidia driver, use the following image:

https://github.com/thewtex/docker-opengl-nvidia

For other OpenGL implementations, make sure the image has the same implementation as the host.

share

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
share

This is not lightweight but is a nice solution that gives docker feature parity with full desktop virtualization. Both Xfce4 or IceWM for Ubuntu and CentOS work, and the noVNC option makes for an easy access through a browser.

https://github.com/ConSol/docker-headless-vnc-container

It runs noVNC as well as tigerVNC's vncserver. Then it calls startx for given Window Manager. In addition, libnss_wrapper.so is used to emulate password management for the users.

share

protected by Community Sep 28 '14 at 1:07

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.