Creating Wayland Native AppImage’s with QtWebEngine and Hardware Acceleration
This article describes how to create an AppImage with appimage-builder which includes QtWebEngine (Qt6) and runs natively on Wayland with GPU acceleration.
While Flatpak is a good format and offers an excellent framework to ship distro-agnostic Linux apps, in some cases AppImage’s are also interesting. A company may want to ship an app that does not require installation, without sandbox and restrictions. So I decided to have a look at the AppImage format as well, to know how it works. While it was reasonably simple to package an AppImage, I still had some troubles with a Qt app using QtWebEngine.
At the beginning, I created my appimage-builder YAML as I did for previous Qt apps:
version: 1
script:
- mkdir -p AppDir/opt/qt/6.11.0/gcc_64/
- cp -r /opt/qt/6.11.0/gcc_64/plugins AppDir/opt/qt/6.11.0/gcc_64/
- cp -r /opt/qt/6.11.0/gcc_64/qml AppDir/opt/qt/6.11.0/gcc_64/
- cp -r /opt/qt/6.11.0/gcc_64/resources AppDir/opt/qt/6.11.0/gcc_64/
AppDir:
path: /mldonkeyapp/app/build/appimage/AppDir
runtime:
env:
QTWEBENGINE_RESOURCES_PATH: "$APPDIR/opt/qt/6.11.0/gcc_64/resources"
QTWEBENGINEPROCESS_PATH: "$APPDIR/opt/qt/6.11.0/gcc_64/libexec/QtWebEngineProcess"
QTWEBENGINE_LOCALES_PATH: "$APPDIR/opt/qt/6.11.0/gcc_64/translations/qtwebengine_locales"
app_info:
id: luke.mldonkeynext
name: mldonkey-next
icon: icon.png
version: latest
exec: usr/local/bin/mldonkey-next
exec_args: $@
apt:
arch: [ amd64 ]
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-backports main restricted
universe multiverse
- sourceline: deb http://security.ubuntu.com/ubuntu/ noble-security main restricted
- sourceline: deb http://security.ubuntu.com/ubuntu/ noble-security universe
- sourceline: deb http://security.ubuntu.com/ubuntu/ noble-security multiverse
include:
- ca-certificates
- fonts-dejavu-core
- libc-bin
- xkb-data
- libwayland-egl++1
- libwayland-egl1-mesa
- libwayland-egl1
- libwayland-client0
- libgles2
- libglvnd0
- libegl1
- libglapi-mesa
- libxkbcommon-dev
- libxml2
- libicu-dev
- libjbig0
files:
include:
- /opt/qt/6.11.0/gcc_64/libexec/QtWebEngineProcess
- /lib/x86_64-linux-gnu/libLLVM-17.so.1
- /lib/x86_64-linux-gnu/libelf.so.1
- /lib/x86_64-linux-gnu/libfreeblpriv3.so
- /lib/x86_64-linux-gnu/libnssckbi.so
- /lib/x86_64-linux-gnu/libpci.so.3
- /lib/x86_64-linux-gnu/libsensors.so.5
- /lib/x86_64-linux-gnu/libsoftokn3.so
- /lib/x86_64-linux-gnu/libssl.so.3
- /opt/qt/6.11.0/gcc_64/lib/libQt6WebEngineQuick.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WebSockets.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6StateMachine.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6StateMachineQml.so.6
- /opt/qt/6.11.0/gcc_64/libexec/../lib/libQt6WebEngineCore.so.6
- /opt/qt/6.11.0/gcc_64/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so
- /opt/qt/6.11.0/gcc_64/plugins/platforminputcontexts/libibusplatforminputcontextplugin.so
- /opt/qt/6.11.0/gcc_64/plugins/platforminputcontexts/libqtvirtualkeyboardplugin.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqeglfs.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqlinuxfb.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqminimal.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqminimalegl.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqoffscreen.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqvnc.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland-egl.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland-generic.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqxcb.so
- /opt/qt/6.11.0/gcc_64/plugins/platformthemes/libqxdgdesktopportal.so
- /opt/qt/6.11.0/gcc_64/plugins/tls/libqcertonlybackend.so
- /opt/qt/6.11.0/gcc_64/plugins/tls/libqopensslbackend.so
- /opt/qt/6.11.0/gcc_64/plugins/xcbglintegrations/libqxcb-egl-integration.so
- /opt/qt/6.11.0/gcc_64/plugins/xcbglintegrations/libqxcb-glx-integration.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Basic/libqtquickcontrols2basicstyleplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Basic/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Fusion/libqtquickcontrols2fusionstyleplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Fusion/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/impl/libqtquickcontrols2materialstyleimplplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/impl/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/libqtquickcontrols2materialstyleplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/impl/libqtquickcontrols2implplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/impl/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/libqtquickcontrols2plugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Templates/libqtquicktemplates2plugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Templates/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtWebEngine/libqtwebenginequickplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtWebEngine/qmldir
- /opt/qt/6.11.0/gcc_64/resources/icudtl.dat
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_devtools_resources.pak
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_resources.pak
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_resources_100p.pak
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_resources_200p.pak
- /opt/qt/6.11.0/gcc_64/resources/v8_context_snapshot.bin
- /opt/qt/6.11.0/gcc_64/translations/qtwebengine_locales/en-US.pak
- /root/.pki/nssdb/cert9.db
- /root/.pki/nssdb/key4.db
- /root/.pki/nssdb/pkcs11.txt
- /usr/share/icons/default/index.theme
- /usr/share/mime/mime.cache
- /usr/share/mime/types
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
AppImage:
arch: x86_64
comp: zstd
update-information: guessThe app it packages is an application I wrote in Angular which needed a WebView to render the content and some C++ code. Qt is therefore a container for the angular webapp, providing the WebView and all the implementation that cannot fit into it. The complete project can be found here.
This is clearly not very optimized, as I am copying things that are not probably needed. If you have more time to invest, you could select the mandatory elements more accurately.
WebEngineView QML Element Won’t Work
With this setup, WebEngineView won’t work. This is what I got:
[26497:26497:0524/190301.353211:FATAL:ntent/browser/zygote_host/zygote_host_impl_linux.cc:224] Check failed: . : No such file or directory (2)To make the previous setup work, I had to fix a few issues:
- sandbox does not seem to work in the runtime environment created by the AppImage;
- Chromium could not actually use the GPU;
- Qt window could only be created using XWayland, not as a native Wayland client.
Disable Chromium Sandbox Mechanism
Chromium uses a sandbox mechanism to try to contain damage if a webpage exploits a browser vulnerability. While I own the app and I know it does not try to damage the system, it still uses dependencies that I did not write myself, therefore keeping it on would be preferable.
However, for the moment, I decided to simply disable it. This can be done by adding the --no-sandbox param to Chromium flags:
QTWEBENGINE_CHROMIUM_FLAGS: "--no-sandbox"See official docs for more info.
Disabling GPU Acceleration
At this point, the app does not crash anymore, but still the WebView is unable to render the content. The first workaround I found is to disable GPU acceleration, which is the same workaround suggested here:
QTWEBENGINE_CHROMIUM_FLAGS: "--disable-gpu --no-sandbox"
QT_QUICK_BACKEND: "software"This is however a bad idea: GPU acceleration is important.
See official docs for more info.
Ensure GBM is available
By investigating a bit more the GPU issue, I found that the GPU could not be used for the lack of libgbm. GBM is part of Mesa and provides GPU buffer allocation.
I fixed by adding two more libraries:
- libgbm1
- libgl1-mesa-driand environment variable to provide the path to the backend:
GBM_BACKENDS_PATH: "$APPDIR/usr/lib/x86_64-linux-gnu/gbm"This was sufficient to fix the issue with hardware acceleration on my machine.
So do not add the changes from the previous paragraph and just keep the --no-sandbox param, in addition to libgbm.
Proper Wayland Support
With the previous changes, Chromium could be loaded and WebEngineView could render the content properly. However, the app was running as a X11 client through XWayland. You can use xeyes to confirm.
While this worked pretty well, it would be advisable to run without compatibility layers. However, this is what I saw running the app:
[mldonkey-next]: VERB: 23:01:38.236 "/tmp/.mount_mldonkdnHGoO/opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland.so" cannot load: Cannot load library /tmp/.mount_mldonkdnHGoO/opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland.so:
[mldonkey-next]: VERB: 23:01:38.236 QLibraryPrivate::loadPlugin failed on "/tmp/.mount_mldonkdnHGoO/opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland.so" : "Cannot load library /tmp/.mount_mldonkdnHGoO/opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland.so: "
[mldonkey-next]: INFO: 23:01:38.236 Could not load the Qt platform plugin "wayland" in "" even though it was found.By increasing the verbosity of the dynamic linker, it turned out I simply needed some more libs than those added in the previous setup. By inspecting dependencies using readelf I completed the list:
- /opt/qt/6.11.0/gcc_64/lib/libQt6Core.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Gui.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6DBus.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Network.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6OpenGL.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Qml.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6QmlMeta.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6QmlModels.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6QmlWorkerScript.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Quick.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandClient.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositor.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorIviapplication.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorPresentationTime.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorWLShell.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorXdgShell.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandEglCompositorHwIntegration.so.6Of course you may not need all those libs, but you can invest some time to restrict the list as much as possible for your specific use case.
Complete Configuration
After the changes above, this is the complete YAML for appimage-builder. You can start from this and add/remove to adapt to your specific need:
version: 1
script:
- mkdir -p AppDir/opt/qt/6.11.0/gcc_64/
- cp -r /opt/qt/6.11.0/gcc_64/plugins AppDir/opt/qt/6.11.0/gcc_64/
- cp -r /opt/qt/6.11.0/gcc_64/qml AppDir/opt/qt/6.11.0/gcc_64/
- cp -r /opt/qt/6.11.0/gcc_64/resources AppDir/opt/qt/6.11.0/gcc_64/
AppDir:
path: /mldonkeyapp/app/build/appimage/AppDir
runtime:
env:
QTWEBENGINE_RESOURCES_PATH: "$APPDIR/opt/qt/6.11.0/gcc_64/resources"
QTWEBENGINEPROCESS_PATH: "$APPDIR/opt/qt/6.11.0/gcc_64/libexec/QtWebEngineProcess"
QTWEBENGINE_LOCALES_PATH: "$APPDIR/opt/qt/6.11.0/gcc_64/translations/qtwebengine_locales"
QTWEBENGINE_CHROMIUM_FLAGS: "--no-sandbox"
GBM_BACKENDS_PATH: "$APPDIR/usr/lib/x86_64-linux-gnu/gbm"
app_info:
id: luke.mldonkeynext
name: mldonkey-next
icon: icon.png
version: latest
exec: usr/local/bin/mldonkey-next
exec_args: $@
apt:
arch: [ amd64 ]
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ noble-backports main restricted
universe multiverse
- sourceline: deb http://security.ubuntu.com/ubuntu/ noble-security main restricted
- sourceline: deb http://security.ubuntu.com/ubuntu/ noble-security universe
- sourceline: deb http://security.ubuntu.com/ubuntu/ noble-security multiverse
include:
- ca-certificates
- fonts-dejavu-core
- libc-bin
- xkb-data
- libwayland-egl++1
- libwayland-egl1-mesa
- libwayland-egl1
- libwayland-client0
- libgbm1
- libgl1-mesa-dri
- libgles2
- libglvnd0
- libegl1
- libglapi-mesa
- libxkbcommon-dev
- libxml2
- libicu-dev
- libjbig0
files:
include:
- /opt/qt/6.11.0/gcc_64/libexec/QtWebEngineProcess
- /lib/x86_64-linux-gnu/libLLVM-17.so.1
- /lib/x86_64-linux-gnu/libelf.so.1
- /lib/x86_64-linux-gnu/libfreeblpriv3.so
- /lib/x86_64-linux-gnu/libnssckbi.so
- /lib/x86_64-linux-gnu/libpci.so.3
- /lib/x86_64-linux-gnu/libsensors.so.5
- /lib/x86_64-linux-gnu/libsoftokn3.so
- /lib/x86_64-linux-gnu/libssl.so.3
- /opt/qt/6.11.0/gcc_64/lib/libQt6WebEngineQuick.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WebSockets.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6StateMachine.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6StateMachineQml.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WebEngineCore.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Core.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Gui.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6DBus.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Network.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6OpenGL.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Qml.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6QmlMeta.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6QmlModels.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6QmlWorkerScript.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6Quick.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandClient.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositor.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorIviapplication.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorPresentationTime.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorWLShell.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandCompositorXdgShell.so.6
- /opt/qt/6.11.0/gcc_64/lib/libQt6WaylandEglCompositorHwIntegration.so.6
- /opt/qt/6.11.0/gcc_64/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so
- /opt/qt/6.11.0/gcc_64/plugins/platforminputcontexts/libibusplatforminputcontextplugin.so
- /opt/qt/6.11.0/gcc_64/plugins/platforminputcontexts/libqtvirtualkeyboardplugin.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqeglfs.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqlinuxfb.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqminimal.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqminimalegl.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqoffscreen.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqvnc.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland-egl.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqwayland-generic.so
- /opt/qt/6.11.0/gcc_64/plugins/platforms/libqxcb.so
- /opt/qt/6.11.0/gcc_64/plugins/platformthemes/libqxdgdesktopportal.so
- /opt/qt/6.11.0/gcc_64/plugins/tls/libqcertonlybackend.so
- /opt/qt/6.11.0/gcc_64/plugins/tls/libqopensslbackend.so
- /opt/qt/6.11.0/gcc_64/plugins/xcbglintegrations/libqxcb-egl-integration.so
- /opt/qt/6.11.0/gcc_64/plugins/xcbglintegrations/libqxcb-glx-integration.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Basic/libqtquickcontrols2basicstyleplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Basic/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Fusion/libqtquickcontrols2fusionstyleplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Fusion/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/impl/libqtquickcontrols2materialstyleimplplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/impl/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/libqtquickcontrols2materialstyleplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/Material/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/impl/libqtquickcontrols2implplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/impl/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/libqtquickcontrols2plugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Controls/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Templates/libqtquicktemplates2plugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtQuick/Templates/qmldir
- /opt/qt/6.11.0/gcc_64/qml/QtWebEngine/libqtwebenginequickplugin.so
- /opt/qt/6.11.0/gcc_64/qml/QtWebEngine/qmldir
- /opt/qt/6.11.0/gcc_64/resources/icudtl.dat
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_devtools_resources.pak
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_resources.pak
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_resources_100p.pak
- /opt/qt/6.11.0/gcc_64/resources/qtwebengine_resources_200p.pak
- /opt/qt/6.11.0/gcc_64/resources/v8_context_snapshot.bin
- /opt/qt/6.11.0/gcc_64/translations/qtwebengine_locales/en-US.pak
- /root/.pki/nssdb/cert9.db
- /root/.pki/nssdb/key4.db
- /root/.pki/nssdb/pkcs11.txt
- /usr/share/icons/default/index.theme
- /usr/share/mime/mime.cache
- /usr/share/mime/types
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
AppImage:
arch: x86_64
comp: zstd
update-information: guessBuild Environment
You can use whatever environment you prefer to build the AppImage, provided you have the libs available. In my case, I used my docker image, which provides appimagebuilder and Qt libs for several versions. The dev image also contains appimage-builder. Follow this link for more info on the qt-dev image. Here you have the complete list of available images and Qt versions.
Have fun 😉