PyInstaller Executable Failing on Target Machine with Missing libGL.so.1

Root Cause Diagnosis

PyInstaller bundles Python modules and most shared libraries, but it deliberately excludes libGL.so.1 through its built-in exclude list, because that library is tied to the host’s GPU driver and graphics stack. Any bundled native module that links against it (typically cv2 from opencv-python, or Qt, pyqtgraph, vtk, Open3D, pygame/pyglet extensions) will fail at load time on a target machine that doesn’t have the library installed. Minimal server, container, and WSL images commonly lack it.

Quick Fix

On the target machine, install the library.

# Debian / Ubuntu
sudo apt-get update && sudo apt-get install -y libgl1 libglib2.0-0

# RHEL / Fedora / Rocky / Alma
sudo dnf install -y mesa-libGL glib2

# Arch
sudo pacman -S libglvnd glib2

# openSUSE
sudo zypper install Mesa-libGL1 libglib-2_0-0

If the app uses OpenCV and needs no GUI functions (cv2.imshow, cv2.waitKey), skip the system package and rebuild with the headless wheel (see Step 3).

Step-by-Step Resolution

1. Confirm which bundled module requires libGL

Build with --onedir for diagnosis, since the _internal layout is easy to inspect (PyInstaller 6.x):

pyinstaller --onedir app.py
cd dist/app
find . -name "*.so*" -print0 | xargs -0 ldd 2>/dev/null | grep "not found" | sort -u

For a quick check against a single binary:

ldd ./_internal/cv2/cv2*.so | grep -i gl

To trace the failure at runtime without ldd:

LD_DEBUG=libs ./app 2>&1 | grep -i "libGL"

2. Install the missing runtime libraries on the target

Use the commands from the Quick Fix section. Verify the dynamic linker can see the library:

ldconfig -p | grep libGL.so.1

Expected output is something like:

libGL.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libGL.so.1

If the package is installed but the library is not listed, refresh the cache:

sudo ldconfig

3. (OpenCV only) Replace the GUI wheel with the headless wheel

opencv-python links against libGL. opencv-python-headless does not. Rebuild in a clean virtual environment:

python -m venv .venv && source .venv/bin/activate
pip uninstall -y opencv-python opencv-contrib-python
pip install opencv-python-headless
pyinstaller --clean --onefile app.py

Never have both opencv-python and opencv-python-headless installed in the same environment. They share the cv2 namespace and overwrite each other’s files.

See also  Playwright tracing zip file corrupted when terminating worker process prematurely

4. (Qt apps) Select a platform plugin that doesn’t require GL

For servers with no display, set the offscreen platform:

export QT_QPA_PLATFORM=offscreen
./app

For software rendering on machines without a GPU driver, also install Mesa’s DRI drivers:

sudo apt-get install -y libgl1-mesa-dri libegl1

5. (Last resort) Bundle libGL explicitly

This is generally discouraged, since a bundled libGL can conflict with the host’s GPU driver. Entries passed explicitly via --add-binary are not subject to the exclude list.

pyinstaller --onefile \
  --add-binary "/usr/lib/x86_64-linux-gnu/libGL.so.1:." \
  app.py

In a .spec file:

a = Analysis(
    ['app.py'],
    binaries=[('/usr/lib/x86_64-linux-gnu/libGL.so.1', '.')],
    ...
)

libGL.so.1 itself depends on libglvnd, libGLX, and libGLdispatch. Bundling it alone often just moves the failure to the next missing library.

Why This Fails for Some Users

  • Slim container images: python:*-slim, debian:*-slim, and Alpine-based images omit Mesa/GL libraries. Add RUN apt-get update && apt-get install -y --no-install-recommends libgl1 libglib2.0-0 to the Dockerfile.
  • Obsolete package name: libgl1-mesa-glx is a legacy name that no longer exists on newer distributions (Debian 12+, Ubuntu 24.04+). Use libgl1.
  • libGL fixed, now libglib-2.0.so.0 is missing: OpenCV also needs GLib. Install libglib2.0-0 (Debian/Ubuntu) or glib2 (RHEL family).
  • glibc version mismatch: A binary built on a newer distribution fails on older targets with GLIBC_2.xx not found. This is a separate error from the libGL one. Build on the oldest distribution you need to support.
  • Architecture mismatch: An x86_64 binary cannot load an aarch64 libGL, and a 32-bit libGL.so.1 does not satisfy a 64-bit process. Check with file ./app and uname -m.
  • Proprietary GPU drivers: NVIDIA’s driver installs its own libGL outside the default search path on some systems (such as /usr/lib/nvidia). If ldconfig -p doesn’t list it, add the path to /etc/ld.so.conf.d/ and rerun sudo ldconfig.
  • LD_LIBRARY_PATH interference: The PyInstaller bootloader prepends its extraction directory to LD_LIBRARY_PATH. A stale or incompatible libGL placed there by a previous workaround can shadow the system one. Remove it and rebuild with --clean.
  • WSL and remote sessions: Headless WSL installs have no GL stack by default. Install libgl1 as in the Quick Fix, and for GUI apps ensure WSLg or an X server is available.
  • Stale build artifacts: Reusing old build/ and dist/ directories after changing OpenCV wheels can keep the old cv2 binary. Always rebuild with pyinstaller --clean.

Leave a Comment