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.
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. AddRUN apt-get update && apt-get install -y --no-install-recommends libgl1 libglib2.0-0to the Dockerfile. - Obsolete package name:
libgl1-mesa-glxis a legacy name that no longer exists on newer distributions (Debian 12+, Ubuntu 24.04+). Uselibgl1. libGLfixed, nowlibglib-2.0.so.0is missing: OpenCV also needs GLib. Installlibglib2.0-0(Debian/Ubuntu) orglib2(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 thelibGLone. Build on the oldest distribution you need to support. - Architecture mismatch: An x86_64 binary cannot load an
aarch64libGL, and a 32-bitlibGL.so.1does not satisfy a 64-bit process. Check withfile ./appanduname -m. - Proprietary GPU drivers: NVIDIA’s driver installs its own
libGLoutside the default search path on some systems (such as/usr/lib/nvidia). Ifldconfig -pdoesn’t list it, add the path to/etc/ld.so.conf.d/and rerunsudo ldconfig. LD_LIBRARY_PATHinterference: The PyInstaller bootloader prepends its extraction directory toLD_LIBRARY_PATH. A stale or incompatiblelibGLplaced 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
libgl1as in the Quick Fix, and for GUI apps ensure WSLg or an X server is available. - Stale build artifacts: Reusing old
build/anddist/directories after changing OpenCV wheels can keep the oldcv2binary. Always rebuild withpyinstaller --clean.