Tags: Docker
While the MacPorts and Homebrew projects bring many Linux apps to the Mac environment, they don't support every app we'd want to run. Since the X11 environment is not native to macOS it's not a simple recompile, since you have to rewrite the GUI system. Thankfully there is an X11 display server for Mac OS X that can be used to run an application in a Linux environment and display it on the macOS desktop. In this article we'll look at one way to get this all connected up and running.
We need to start with a little bit of background.
Since the 1980's the Unix/Linux/FreeBSD/etc ecosystem has used the X11 windowing system to display GUI applications. The architecture is different from both Windows and Mac OS X GUI environments. The X11 architecture allows an application to run on a distant computer, and be displayed on another computer, in addition to the typical case where the application is displayed on its local computer. In other words, the X11 GUI architecture generalized the connection between application and graphics display, allowing both to run on any computer connected to the Internet, and for the graphics interaction to run across the Internet.
In this case the desire is to run an X11 application on Linux - one which isn't supported by either MacPorts or Homebrew.
We can set up a Docker container with that application. Since the Docker container is a Linux environment the GUI subsystem is X11. Because X11 allows an X11 application to display on any X11 display server, all that's needed is
- A docker container with the desired Linux GUI application installed
- An X11 display server on a macOS computer
- Suitable authentication and permissions suitable for the Linux app that's running inside the Docker container
It is also possible to implement this using Canonical's Multipass environment which makes it easy to run Ubuntu virtual machines on Mac OS X or on Windows. See: Use Canonical's Multipass to display Linux GUI applications on macOS desktop
A Docker image for a Linux GUI application
Since Docker builds container images using regular Linux distributions, it's possible to install any application. The package manager will automatically ensure all needed packages are installed. While we normally think of Docker to run server applications with no GUI, there's no reason that GUI applications cannot be installed.
Consider this Docker image:
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get -y install firefox
CMD ["firefox"]
That's Ubuntu - we really should update that to 18.04 or something but what the heck. The point is that apt-get install firefox
will install all dependencies required to run Firefox, including the X11 libraries and everything else. The last step, to launch the firefox
binary, causes Firefox to run and attempt to connect with an X11 display server.
XQuartz - an X11 display server for Mac OS X
The phrase "X11 display server" is how the X11 team describes an X11 display. The GUI application is seen to be a client application, displaying its GUI on an X11 display which is the server. Hence "X11 display server" is the correct phrase.
The XQuartz project describes itself as:
The XQuartz project is an open-source effort to develop a version of the X.Org X Window System that runs on OS X. Together with supporting libraries and applications, it forms the X11.app that Apple shipped with OS X versions 10.5 through 10.7.
Apple used to ship an X11 display server with Mac OS X, but at some point stopped. This team keeps that software working.
XQuartz is available through Homebrew and MacPorts if you prefer. But you can download a DMG directly from https://www.xquartz.org/
Simply download the application and install it.
It's recommended to reboot your computer before doing the rest.
Run the XQuartz application, then in it's application menu you'll find a Preferences choice. In the Preferences make this setting:
Namely - Allow connections from network clients. This opens the display so it accepts X11 connections from any non-local computer. The Docker container we're about to create counts - as would a Linux machine in your office.
An X11 display server for Windows
In case you're a Windows user, the VcXsrv Windows X Server is available at this address https://sourceforge.net/projects/vcxsrv/
Running the Docker container, passing along X11 authentication
Since it would be extremely insecure for X11 displays to allow any application access to the display - there is security built in to the X11 protocol. We don't have space to go into all the details of configuring X11 security. Let's just take the happy path and document what works.
The first thing is determining your IP address, which you can do like:
$ ifconfig -a
...
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
ether a8:20:66:38:a9:16
inet6 fe80::10df:8cae:6021:5491%en0 prefixlen 64 secured scopeid 0x8
inet6 2600:1700:a3a0:6e40:1853:7294:6085:e00a prefixlen 64 autoconf secured
inet6 2600:1700:a3a0:6e40:1ce5:80ee:164:c033 prefixlen 64 autoconf temporary
inet 192.168.1.89 netmask 0xffffff00 broadcast 192.168.1.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (1000baseT <full-duplex,flow-control,energy-efficient-ethernet>)
status: active
...
Notice the line reading inet
has my IPv4 address, and the lines reading inet6
have my IPv6 address. We'll use the IPv4 address.
This magic shell command gets the IP address into an environment variable:
export IP=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}')
$ echo $IP
192.168.1.89
With XQuartz running ... execute these commands:
$ xhost +$IP
$ docker run --rm --name firefox -e DISPLAY=$IP:0 -e XAUTHORITY=/.Xauthority --net host -v /tmp/.X11-unix:/tmp/.X11-unix -v ~/.Xauthority:/.Xauthority jess/firefox
The xhost
command configures XQuartz to allow connections from the given IP address. I had to install xhost
using MacPorts.
This command imports various security settings and files into the container. The XQuartz application creates the .X11-unix
directory and the .Xauthority
file. By importing them into the Docker container we've injected the security bits contained in both. That simplifies setting up the security authentication required.
The DISPLAY variable instructs X11 applications which display server to connect to. This instructs the Firefox application to connect with the XQuartz display on the Mac OS X machine.
The XAuthority file is used in authentication. The .X11-unix
thing contains a Unix-domain socket:
$ ls -l /tmp/.X11-unix/X0
srwxrwxrwx 1 david wheel 0 Dec 10 11:27 /tmp/.X11-unix/X0
If you get an error: XDG_RUNTIME_DIR not set in the environment .. add this to the Docker command line: -e XDG_RUNTIME_DIR=/tmp/xdgr
So long as you get the message cannot open display: 192.168.1.89:0 then something is configured incorrectly with the authentication. The above authentication bits worked for me. An additional command that may be helpful is:
$ xhost +local:docker
Running the above command we get this - Firefox for Ubuntu displaying on a Mac OS X desktop.