Run A Python GUI App in Docker
Tags: python, gui, docker, tutorial
Published: 2022-12-06 12:07:28

Running a Python GUI app built with [tkinter](https://docs.python.org/3/library/tkinter.html) in Docker containers can be tricky. Tkinter cannot access the X11 socket, which it relies on to display graphical interfaces, of the Docker host from a Docker container without proper configuration. In this post, I want to share my hack for making my tkinter app work in Docker on a Mac machine.

## Table of Contents

- [Development Environment](#development-environment)
- [Step 1: Prepare Directories & Files](#step-1-prepare-directories--files)
- [Step 2: Add Some Code](#step-2-add-some-code)
- [Step 3: Install & Configure XQuartz](#step-3-install--configure-xquartz)
- [Step 4: Find Our IP Address](#step-4-find-our-ip-address)
- [Step 5: Build Image & Run Container](#step-5-build-image--run-container)
- [Conclusion](#conclusion)  

## Development Environment

- macOS Monterey 12.6
- docker 20.10.17
- docker-compose 1.29.2

## Step 1: Prepare Directories & Files

Create a directory for our project and within it, create a `Dockerfile`, a `docker-compose.yml` file and a `src/` directory. Your directory tree should look like this.

```
├── Dockerfile
├── docker-compose.yml
└── src
    └── app.py
```

## Step 2: Add Some Code

Copy & paste the following code into your `Dockerfile`.

```
FROM python:3.10.7

WORKDIR /usr/src/app

COPY . .

CMD [ "python3", "./app.py" ]
```

Create an `.env` file at the root of the project and add the following code to it. 

```
SRC_PATH=./src
```

Then copy & paste the following code into your `docker-compose.yml` file. 

```
version: '3'
services:
  python3:
    build: .
    container_name: 'python3'
    stdin_open: true
    working_dir: '/usr/src/app'
    tty: true
    volumes:
      - ${SRC_PATH}:/usr/src/app
    environment:
      - DISPLAY=YOUR_IP_ADDRESS:0.0
```

Note: we have a `DISPLAY` environment variable set to `YOUR_IP_ADDRESS:0.0` for now, which we'll replace with our real ip address later.

Then, copy & paste the following code into your `src/app.py` file.

```python
from tkinter import *
from tkinter import ttk

root = Tk()

frm = ttk.Frame(root, padding=10)
frm.grid()

ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)

root.mainloop()
```

## Step 3: Install & Configure XQuartz

[XQuartz](https://www.xquartz.org/) is needed for our app to work in Docker on a Mac machine. So go to [https://www.xquartz.org/](https://www.xquartz.org/), download and install it.


![XQuartz](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vw70bmsm9z1sneyilsg.png)



After installation, open `Preferences > Security` and make sure both boxes are checked. Then restart XQuartz.

## Step 4: Find Our IP Address

Remember in [step 2](#step-2-add-some-code), we had the `DISPLAY` environment variable set to a placeholder. Now we need to replace it with our real ip address.

Open `System Preferences > Network`. You can find your ip address here.


![find ip address](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bpxxw6k6skxnmvyi88b.png)



Replace the `OUR_IP_ADDRESS` part in `docker-compose.yml` with the ip address above. (e.g. if your ip address is `100.1.00.11`, the `DISPLAY` environment variable value should be `100.1.00.11:0.0`).

## Step 5: Build Image & Run Container

Before we can build our image and run our container, we need to run the following command in shell to disable X server's access control so that our app can access the host's X11 socket from within the container.

```bash
xhost +
```

Finally, let's build our image and run our container.

```bash
docker-compose up -d --build
```

The tkinter app window should pop up like the following.


![tkinter window](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j4lq6k4wlzm2vwbqjpe8.png)


## Conclusion

When trying to run tkinter apps in Docker on a Mac, you may get various errors like `_tkinter.TclError: no display name` and `no $DISPLAY environment variable or _tkinter.TclError: couldn't connect to display ~`. These are due to incorrect configuration of the `DISPLAY` environment variable or the X window system. 

I found a lot of proposed solutions online but none of them worked for me. I tested multiple tricks and finally managed to get it work so if you were like me, this article may help you out.