Quick Links

Docker's normally used to containerise background applications and CLI programs. You can also use it to run graphical programs though! You can either use an existing X Server, where the host machine is already running a graphical environment, or you can run a VNC server within the container.

First it's important to understand what Docker actually does. A Docker "container" is a form of encapsulation which seems to be superficially similar to a virtual machine. Unlike a virtual machine, containers share the same Linux kernel as their host system.

The next component is the X Window System. X Servers such as Xorg provide the fundamental graphical capabilities of Unix systems. GUI applications can't render without an X Server available. (Alternative windowing systems, such as Wayland, are available - we're focusing on X in this article.)

Trying to run an X Server in Docker is theoretically possible but rarely used. You'd need to run Docker in privileged mode (

        --privileged
    

) so it could access your host's hardware. Starting the server would try to claim your video devices, usually resulting in loss of video output as your host's original X server gets its devices yanked away.

A better approach is to mount your host's X Server socket into the Docker container. This allows your container to use the X Server you already have. GUI applications running in the container would then appear on your existing desktop.

Why Run GUI Apps in Docker?

Running a GUI program in Docker can be a useful technique when you're evaluating a new piece of software. You can install the software in a clean container, instead of having to pollute your host with new packages.

This approach also helps you avoid any incompatibilities with other packages in your environment. If you need to temporarily run two versions of a program, you can use Docker to avoid having to remove and reinstall the software on your host.

Forwarding An X Socket to A Docker Container

Providing a Docker container with access to your host's X socket is a straightforward procedure. The X socket can be found in

        /tmp/.X11-unix
    

on your host. The contents of this directory should be mounted into a Docker volume assigned to the container. You'll need to use the

        host
    

networking mode for this to work.

You must also provide the container with a

        DISPLAY
    

environment variable. This instructs X clients - your graphical programs - which X server to connect to. Set

        DISPLAY
    

in the container to the value of

        $DISPLAY
    

on your host.

You can encapsulate all this configuration in one

        docker-compose.yml
    

file:

        version: "3"

services:
  app:
    image: my-app:latest
    build: .
    environment:
      - DISPLAY=${DISPLAY}
    volumes:
      - /tmp/.X11-unix:/tmp/.X11-unix
    network_mode: host

Next, you need to create a Dockerfile for your application. Here's an example that runs the Firefox web browser:

        FROM ubuntu:latest
RUN apt-get update && apt-get install -y firefox
CMD ["/usr/bin/firefox"]

Now build and run the image:

        docker-compose build
docker-compose up

A new Firefox window should appear on your desktop! The Firefox instance will run within the container, independently of any other open Firefox windows. The container will share your host's X socket, so the containerised Firefox still shows up on your desktop.

This approach should only be used when you trust your Docker container. Exposing the host's display server is a security risk if you're not completely sure what lies inside the container.

Handling X Authentication

You might need to authenticate the container to access the X Server. First get an X authentication token from your host machine. Run xauth list and note down one of the listed cookies. You'll need to copy the entire line.

Screenshot showing "xauth list" command output

Inside the Docker container, install the xauth package. Then run xauth add, passing the token you copied in the previous step.

        apt install -y xauth
xauth add <token>

Your container should now successfully authenticate to the X Server.

Another Approach - Running a VNC Server

If you're unable to use X socket forwarding, you could setup a VNC server inside your container. This approach lets you view graphical apps in the container by connecting from a VNC client running on the host.

Add the VNC server software to your container:

        FROM ubuntu:latest
RUN apt-get update && apt-get install -y firefox x11vnc xvfb
RUN echo "exec firefox" > ~/.xinitrc && chmod +x ~/.xinitrc
CMD ["v11vnc", "-create", "-forever"]

When you run this container, a VNC server will be created automatically. You must bind a host port to the container's port 5900 - this is the port the VNC server will be exposed on.

Firefox gets launched on startup as it's added to .xinitrc. This file will be executed when the VNC server launches and initialises a new display.

To connect to the server, you'll need a VNC client on your host. Find the IP address of your container by running docker ps, noting down the container ID and passing it to docker inspect <container>. You'll find the IP address near the bottom of the output, within the Network node.

Screenshot of getting a Docker container's IP address with docker inspect

Use the container's IP address with your VNC client. Connect on port 5900 without authentication. You should now be able to interact with the graphical programs running within your Docker container.

Conclusion

You have the choice of two approaches when running graphical programs within a containerised environment. For general use, sharing the host's X socket usually provides the simplest solution. You may also choose to run a VNC server within the container. This approach can be safer when you didn't create the container image.

Containerised graphical apps are useful when you're evaluating software or need to run two versions of a package. You can use programs on your existing desktop without needing to touch your host's configuration.