Running Linux GUI Apps in Docker on Mac

Why would you want to run Linux apps in Docker on a Mac? Maybe there's a Linux program you love or you want the added security of sandboxing an app. Or maybe, as was the case with me, you're developing a C++ program targeting Linux and don't want to cross compile your dependencies.

The following steps get the CLion IDE running in a Docker container created during the project's build process. It uses XQuartz, a version of the X Window system that runs on macOS, along with socat.

  1. Download the Linux version of CLion
  2. Each time dependencies change, run to create a Docker image and container. The script assumes ~/dev is your code directory. If you use something different, edit the script or run it as CODE_DIR=<your_dir> ./
  3. Run to start the container and exec bash inside of it
  4. From inside the container, run /home/dev/clion-XXX/bin/ to start CLion. If this fails, ensure your container contains a JRE by either manually running apt install default-jre or adding it to the Dockerfile.
  5. Either select "Evaluate for free" or enter your activation code
  6. Open your project
  7. Build it and enjoy code completion, etc.!
  8. If you make changes to settings they'll be lost each time you create a new container. To save them, run docker cp my_project:/root/.CLionXXX/ ~/dev and import them next time you start CLion in a fresh container.

EXISTING_IMAGE_ID=$(docker ps --all --filter "name=$NAME" --format "{{.Image}}")
if [[ -n "$EXISTING_IMAGE_ID" ]]; then
read -p "$NAME already exists. Delete it? " answer
while true
case $answer in
[yY]* ) docker stop $NAME > /dev/null 2>&1
docker rm $NAME > /dev/null 2>&1
* ) echo "exiting"
echo "$NAME does not exist, creating..."
# Build your docker container
IP_ADDRESS=$(ifconfig en0 | grep "inet " | cut -d " " -f 2)
IMAGE_ID=$(docker images --format "{{.ID}}" build/local/my-project:latest)
echo "Creating container using image ID $IMAGE_ID, mapping IP address $IP_ADDRESS, and mounting $CODE_DIR..."
docker create -it -e DISPLAY=$IP_ADDRESS:0 -v $CODE_DIR:/home/dev/ $IMAGE_ID bash
GENERATED_NAME=$(docker ps --all --filter "ancestor=$IMAGE_ID" --format "{{.Names}}")
echo "Renaming $GENERATED_NAME to $NAME..."
docker rename $GENERATED_NAME $NAME
echo "\nContainer created, run ./ to exec bash inside of it."

brew ls --versions $APP > /dev/null 2>&1 || brew install $APP
brew cask ls --versions $APP > /dev/null 2>&1 || brew cask install $APP
if [ "$(lsof -nP -i4TCP:6000 | grep -c LISTEN)" -eq 0 ]; then
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
open -a Xquartz
docker start $NAME > /dev/null 2>&1
docker exec -it $NAME bash

Stay up to date

Get notified when I publish. Unsubscribe at any time.