How to display a gui app in a Docker container in macOS


This tutorial will show you how to create a Docker container for the notorious xeyes app and how to display it in a macOS desktop. I tested it on macOS Catalina with Docker Desktop Community 2.3.0.5.

XQuartz

In order to display any graphical application, Linux uses X11, a windowing subsystem for Unix-like operating systems. X11 is based on a client-server model where an X server controls the output on the screen and every application acts as an X client to the X server. This model allows clients to be displayed in a server located in a different computer in the same network. As you can imagine, this comes in handy for what you are trying to do here because you will be connecting the X client xeyes inside the Docker container to the X server in the macOS host via the internal network generated by the Docker daemon.

macOS doesn't use X11 as its default windowing subsystem but there is an open-source third-party version of X11 developed specifically for mac called XQuartz.

You can either download it from the official website or, if you have homebrew installed, you can type the following in the terminal:

brew cask install xquartz

Once the installation process is complete, you need to open XQuartz, go to Preferences, go to the Security tab and check Allow connections from network clients as shown in the image below.

Preferences, Security tab

Gui app container

Now it's time to build an image containing xeyes. Apparently modern versions of Centos don't have xeyes in their repos so we will use Centos 6. Note that any distro with any gui app shuould work. Create the following Dockerfile:

FROM centos:centos6.6
RUN yum install -y xeyes
CMD ["/usr/bin/xeyes"]

and then build the image with: docker build -t xeyes .

Let there be gui: and there was gui

Every time we lunch XQuartz you need to specify which clients on the network are allowed to connect; since you are connecting from a container in your own mac, you need to add localhost to the whitelist. To do this you can type the following:

xhost +localhost

As I said, this has to be done every time you lunch XQuartz and AFAIK there is no way to automate this inside XQuartz. I guess it's for security reasons because a misuse of xhost could potentially give the opportunity to every X client on the internet to connect to our X server.

Now you can finally run the container and display the gui on macOS by typing the following:

docker run --rm -e DISPLAY=host.docker.internal:0 xeyes

Let's break it down:

  • --rm means that the container (not the image) is destroyed after its execution. You might need to save some files or preferences from the gui application so you decide whether to keep it or not and then re-run the existing stopped container.
  • -e DISPLAY=host.docker.internal:0 sets the environment variable DISPLAY with the address of the mac display. host.docker.internal is evaluated by Docker as the host IP address in the local network between the host and the Docker containers. You can find more on the official documentation.

If everything went according to plan you should see two eyes staring at your mouse pointer.

xeyes app

Conclusion

Of course, just by changing the Dockerfile, you can easily run any application with a graphical user interface on a macOS desktop. This is especially useful for testing apps in a replicable environment or running Linux applications that won't easily compile on the mac (or have weird .sh installation scripts that will copy files all over your file system without much control).

Enjoy your gui applications on macOS!