Skip to content

Instantly share code, notes, and snippets.

@AubreyHewes
Last active March 14, 2026 20:52
Show Gist options
  • Select an option

  • Save AubreyHewes/2f5442f96f7f0304230db626af4a085d to your computer and use it in GitHub Desktop.

Select an option

Save AubreyHewes/2f5442f96f7f0304230db626af4a085d to your computer and use it in GitHub Desktop.
Docker MacOS ESPHome Host LVGL Examples (Requires XQuartz)

Getting MacOS ESPHome-In-Docker host: LVGL UI prototyping working

So I wanted to be able to quick prototype a UI for the ESPHome LVGL integration (as recompiling and flashing for each change takes some time).

I have never used the host integration with LVGL before and it turns out it isn't that easy toi get running. So i thought I would write it down - and well this is the gist of it.

(if lazy look at the included docker-compose.yaml, Dockerfile - i stripped my config down to the bare necessities the simple bare config runs - esphome-host-lvgl-example.yaml - requires xquartz)

Getting started

I started out with a minimum host: SDL LVGL config (whats in a name):

# esphome-host-lvgl-example.yaml

# Host mode
host:

# Enable logging
logger:
  level: DEBUG

# ============================================================
#
# Define the SDL display for LVGL
#

#
# Define the SDL display (required for LVGL rendering)
#
display:
  - platform: sdl
    id: sdl_display
    dimensions:
      # Configure the display size
      width: 320
      height: 240

#
# Define the SDL virtual touchscreen (required for LVGL interaction)
#
touchscreen:
  - platform: sdl
    id: sdl_touchscreen
    display: sdl_display

# ============================================================
#
# LVGL Minimal Config; Displays a Basic Widget Showcase
#
lvgl:

But ran into issues; as I run ESPHome in docker on MacOS. The perfect storm it turns out.

Getting it working

The first hurdle was that currently the official docker image (v2026.2.4 as of time of writing) does not include the required libs for SDL (though the official esphome docs mention them):

apt install libsdl2-dev libsodium-dev build-essential git

So the first step was to add the required libs to the docker image. The build-essential and git are not required (i checked) so that is why it deviates from what is officially proposed (which is probably due to them making the docker image). So we just need to add the sdl and sodium libs:

# Dockerfile

FROM ghcr.io/esphome/esphome:2026.2.4

RUN apt update && apt install -y \
    libsodium-dev \
    libsdl2-dev \
    && rm -rf /var/lib/apt/lists/*

As I actually already run the original image using docker-compose I just needed to change my config to use the Dockerfile instaead of the original image.

# docker-compose.yaml
services:
  esphome:
    #image: ghcr.io/esphome/esphome:2026.2.4
    build:
      dockerfile: ./Dockerfile
      
    # ...

Ok gets past that error. But now another error:

XDG_RUNTIME_DIR is invalid or not set in the environment

Head scratch; I know this from days gone by; probably need a DISPLAY variable to run on, but what is it on macos?

So, yeah, grabs the favorite LLM and ask it; and after a lot and a lot and a lot or trial and error and repeating (slightly different solutions):

As MacOS does not run X11 (or compat) you dont have a display to pass. And so, so far the only workable way to get it working is to install something called XQuartz (an X11 server for your macos host). So I unhappily installed that (I dont like installing things). Make sure after installing you disable all the wm integrations (as you probably dont want it -- see also troubleshooting).

Ensure XQuartz -> Settings -> Security -> Allow connections from network clients is enabled. Then to actually allow access it is required to add the client host to the X11 server auth.

Run these once:

defaults write org.xquartz.X11 enable_iglx -bool true
# disable some wm stuff that annoys me
defaults write org.xquartz.X11 wm_ffm -bool false
defaults write org.xquartz.X11 wm_active -bool false

So every time you relogin/reboot you need to do this before you can actually use the X11 server (via DISPLAY):

# allows localhost access to the X11 server
xhost +localhost

Now I can pass the DISPLAY.

    environment:
      - "DISPLAY=host.docker.internal:0"

Ok, so this gets much further and actually now the linux env in the container can run GUI applications on your host. But does LVGL work now?

No, but after looking at the docs and a lot of tweaking and LLM bashing I got it working with the following extra env:

# Forces SDL to use X11 instead of trying Wayland
SDL_VIDEODRIVER=x11

# Tells SDL not to expect a hardware GPU (common in Docker)
SDL_RENDER_DRIVER=software

# Prevents SDL from trying to use a physical keyboard/mouse driver
SDL_MOUSE_RELATIVE=0

# Prevents SDL from trying to use direct rendering
LIBGL_ALWAYS_INDIRECT=1

We have a bingo!

I now get the LVGL window and it is interactive!

Now to clean up my configs and get that xhost command hooked to run on docker-compose-up (so i dont need to add it via login/profile or some such - it is a secutiry thing)

Troubleshooting

Error Possible fix
Any "Authorization Required" error X11 has security levels. The Docker container is not allowed access.
Running xhost +localhost (or xhost +[Your IP]) to whitelist the incoming connection.
swrast / libGL errors The container tried to use "Direct Rendering" (hardware GPU access), which doesn't work through an X11 tunnel. It needed "Indirect Rendering."
Setting enable_iglx -bool true in XQuartz and adding LIBGL_ALWAYS_INDIRECT=1 to your Docker environment.
If you ever find the window "freezing" or not opening after a few hours of development Check XQuartz: It sometimes goes to sleep.
Reset Permissions: Just run the xhost command again.
XQuartz interferes with alt tabbing and refocusing mimimized windows Yea hi know it sucks if this happens on your host. The only way to sort of gegt them windows back is via command+tab -tab +option -command -option or via the application's window options if they have them. WIP.

End

This is the gist of a reasonable usable integration: See the full configuration in the files. at yr own risk.

p.s.

# minimal macos docker-compose for ESPHome `host:` LVGL prototyping
services:
esphome:
container_name: esphome
build:
args:
ESPHOME_VERSION: "2026.2.4"
dockerfile: ./esphome.dockerfile
volumes:
- ./config:/config:rw
- /etc/localtime:/etc/localtime:ro
restart: always
#
# set the port passthough to the host (http://0.0.0.0:6052/ -> container:6052)
ports:
- 6052:6052
environment:
# Your TZ for nice readable logs and create timestamp etc (optional)
- "TZ=Europe/Mariehamn"
#
# --------------------------------------------------
# ESPHome config:
#
# the web interface credentials for http://0.0.0.0:6052/
- "USERNAME=esphome"
- "PASSWORD=esphome"
#
# helper to update online/offline of devices in the webui (needed on macos due to limitations)
- "ESPHOME_DASHBOARD_USE_PING=true"
#
# required for macos; this is how build files/libs are copied (else hardlinked which is shtty in docker on macos)
- "UV_LINK_MODE=copy"
#
# --------------------------------------------------
# Extra for `host:` LVGL support:
#
# Set the X11 display
- "DISPLAY=host.docker.internal:0"
#
# Forces SDL to use X11 instead of trying Wayland
- "SDL_VIDEODRIVER=x11"
#
# Tells SDL not to expect a hardware GPU (common in Docker)
- "SDL_RENDER_DRIVER=software"
#
# Prevents SDL from trying to use a physical keyboard/mouse driver
- "SDL_MOUSE_RELATIVE=0"
#
# Prevents SDL from trying to use direct rendering
- "LIBGL_ALWAYS_INDIRECT=1"
# Host mode
host:
# Enable logging
logger:
level: DEBUG
# ============================================================
#
# Define the SDL display for LVGL
#
#
# Define the SDL display (required for LVGL rendering)
#
display:
- platform: sdl
id: sdl_display
dimensions:
# Configure the display size
width: 320
height: 240
#
# Define the SDL virtual touchscreen (required for LVGL interaction)
#
touchscreen:
- platform: sdl
id: sdl_touchscreen
display: sdl_display
# ============================================================
#
# LVGL Minimal Config; Displays a Basic Widget Showcase
#
lvgl:
# Change the build-arg ESPHOME_VERSION to your required version - else it defaults to this
ARG ESPHOME_VERSION=2026.2.4
FROM ghcr.io/esphome/esphome:${ESPHOME_VERSION}
# Add SDL libs for ESPHome `host:` LVGL prototyping:
RUN apt update && apt install -y \
libsodium-dev \
libsdl2-dev \
&& rm -rf /var/lib/apt/lists/*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment