Skip to content

fix(linux): make AppImage work on Mesa 25+ / GLib 2.88 distros#1567

Open
wpfleger96 wants to merge 2 commits into
mainfrom
wpfleger96/appimage-linux-compat
Open

fix(linux): make AppImage work on Mesa 25+ / GLib 2.88 distros#1567
wpfleger96 wants to merge 2 commits into
mainfrom
wpfleger96/appimage-linux-compat

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

The 0.3.45 AppImage silently fails to launch on Ubuntu 26.04 — and it should be shipping Mesa 25+ / GLib 2.88 — the window never appears. I debugged it down to three interlocking problems that all stem from the same root cause: vanilla tauri-bundler over-bundles Linux infra libs that conflict with what's on the host.

EGL crash. linuxdeploy-plugin-gtk pulls in libwayland-client.so.0 (version 1.22) alongside the app. On Mesa 25+ that bundled version gets loaded at runtime, and eglGetDisplay returns EGL_BAD_PARAMETER under a Wayland session. WebKitWebProcess treats that as fatal and aborts before the window ever renders. Verified on this machine — "Could not create default EGL display: EGL_BAD_PARAMETER. Aborting..." appears in stderr every time.

GStreamer crash. The bundled libgst*.so* (10 core libs) are present, but AppRun unconditionally sets GST_PLUGIN_SYSTEM_PATH_1_0 to $APPDIR/usr/lib/gstreamer-1.0, a directory the bundler never populates when bundleMediaFramework is false (the default). With no plugins, appsink is missing, WebKit connects signals on a null element, and the render process dies with GLib-GObject-CRITICAL errors. As a side effect the broken run poisons ~/.cache/gstreamer-1.0/registry.x86_64.bin — the registry gets rebuilt as an empty 76-byte stub on the next run.

WebKit helper mismatch (latent). The bundled WebKit 2.44 helpers have RUNPATH=$ORIGIN only, so any launch path that bypasses AppRun's chdir causes the dynamic linker to load system WebKit 2.52 helpers instead, producing IPC mismatches and eventual SIGBUS in WebKitNetworkProcess.

There's no tauri.conf.json knob that fixes this. bundle.linux.appimage only exposes bundleMediaFramework, files, and bundleXdgOpen. files is a copy-only directive — it can't remove or symlink bundled libs — and nothing changed through tauri 2.11.5. Upstream issue: tauri-apps/tauri#15665.

What this PR does. desktop/scripts/fix-appimage.sh takes a built .AppImage, extracts it, removes the conflicting libs (libwayland-*, libglib/gio/gobject/gmodule-2.0, libmount/blkid/selinux/pcre2-8, all libgst*.so*, libzstd/elf/ffi), symlinks /usr/lib/x86_64-linux-gnu/gstreamer-1.0 (or the aarch64 equivalent) into the bundle's usr/lib/, repacks with appimagetool, and re-signs if TAURI_SIGNING_PRIVATE_KEY is set — handling both .AppImage.sig and legacy .AppImage.tar.gz.sig updater artifact formats so the in-app updater keeps working after the repack. The script fails loudly if the libs it expects to strip aren't where the bundler put them, so a future tauri/linuxdeploy upgrade that changes the bundled lib set breaks the release job instead of silently shipping an unfixed artifact. Signing relies on the TAURI_SIGNING_* env vars alone (same as the macOS jobs) — the password never appears on argv.

The release-linux job now runs inside a container: ubuntu:22.04 image (digest-pinned, like the workflow's SHA-pinned actions; Renovate keeps both fresh) rather than directly on the ubuntu-latest runner. Building against the oldest-supported base is the approach recommended in the tauri docs for broad GLIBC compatibility, and the ubuntu-22.04 runner image hits deprecation in September 2026 anyway — using a container image on ubuntu-latest is the cleaner long-term path. The job sets APPIMAGE_EXTRACT_AND_RUN: "1" at the job level because linuxdeploy and appimagetool are themselves AppImages and containers lack FUSE. Everything the job downloads is pinned and integrity-checked: appimagetool 1.9.1 by SHA256 (hard fail on any arch without a recorded hash), the GitHub CLI apt keyring by SHA256, and the AppImage type2 runtime from the immutable 20251108 release — without that last pin, appimagetool fetches the runtime (the first code end users execute) from its mutable continuous tag at repack time.

The tradeoff worth calling out: removing these libs means the AppImage now relies on the host's Wayland, GStreamer, and graphics stack. In practice that means Ubuntu 22.04+ (GLib ≥ 2.72) — which was already the practical floor, since older distros don't ship webkit2gtk-4.1 at all. RELEASING.md documents this host floor and the post-processing step, and corrects a stale note that claimed Linux users don't receive auto-updates (the updater manifest covers all four platform keys).

Release CI only runs on block/buzz, so this was validated two ways. First, by post-processing the released 0.3.45 artifact directly on Ubuntu 26.04: the broken AppImage reproduces the EGL_BAD_PARAMETER abort every time, and the fixed copy starts cleanly with both WebKitNetworkProcess and WebKitWebProcess staying alive past 20 seconds and no EGL or GStreamer errors in stderr. Second, by exercising the full pipeline inside an ubuntu:22.04 container against the same released artifact: pinned tool + runtime downloads verify, extract → strip → symlink → repack succeeds, the repacked bundle contains none of the denylisted libs, and the layout guard fails loudly when pointed at an already-stripped AppImage.

@wpfleger96 wpfleger96 force-pushed the wpfleger96/appimage-linux-compat branch from 19eb8af to 54a8a5e Compare July 7, 2026 16:11
Vanilla tauri-bundler (no bundle.linux config) ships libwayland-client,
libglib/gio/gobject, all libgst*.so*, and other infra libs into the
AppImage. On newer distros (Ubuntu 26.04, Mesa 25+), the bundled versions
clash with the system: libwayland-client 1.22 causes eglGetDisplay to return
EGL_BAD_PARAMETER under Wayland (WebKitWebProcess aborts before rendering),
and the empty bundled gstreamer-1.0 dir disables GStreamer plugin discovery.
No tauri.conf.json knob exists to remove or symlink bundled libs; the
bundle.linux.appimage surface only exposes bundleMediaFramework/files/bundleXdgOpen.

Add desktop/scripts/fix-appimage.sh to post-process the AppImage: extracts
it, removes the conflicting infra libs, symlinks the system GStreamer plugin
dir, repacks with appimagetool, and re-signs if TAURI_SIGNING_PRIVATE_KEY
is present (handling both .AppImage.sig and .AppImage.tar.gz.sig updater
artifact formats). Wire it into release.yml after the tauri build step, and
switch the job to container: ubuntu:22.04 so linuxdeploy links against the
oldest-supported GLIBC (per tauri docs; avoids the ubuntu-22.04 runner
deprecation starting Sept 2026).
Fail loudly instead of silently shipping an unfixed artifact when the
bundler layout changes, and refuse unverified appimagetool binaries on
new arches. Keep the signing password off argv (the signer reads it
from the env). Pin the remaining mutable release-job inputs: container
image digest, gh keyring hash, and the AppImage type2 runtime, which
appimagetool otherwise fetches from its mutable continuous tag at
repack time. Document the AppImage host floor in RELEASING.md and fix
the stale Linux auto-update note.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant