Qt Deployment and Packaging
Everything a Qt application needs at runtime beyond the executable: plugins, qt.conf, the deploy tools, platform installers, and why the platform-plugin error is the most common Qt shipping failure.
Overview
Where Qt projects fail publicly
The build is green, the app runs on every developer machine, and the first clean install dies with a dialog naming plugins it found rather than paths it looked in. Almost every Qt team has shipped this bug once; explaining it precisely signals you have operated a Qt product, not just built one.
Qt is a plugin architecture at runtime
Windowing integration, image decoders, SQL drivers, TLS backends, icon engines and styles are separate shared objects loaded by name on first need. In development they are found via paths compiled into the Qt libraries; on a clean machine those paths do not exist.
- Platform plugin — Fatal, immediately at startup
- Everything else — Deferred to first use: a missing qjpeg does not fail to link or start, and surfaces weeks later as blank images
> [!WARNING] Deferred failure is far worse than a crash at launch, because it reaches users as a feature quietly not working.
The tooling
- windeployqt, macdeployqt — Long-standing workhorses: collect Qt libraries, plugins, QML modules and runtime dependencies
- qt_generate_deploy_app_script, qt_deploy_runtime_dependencies (Qt 6) — Deployment in CMake: drive the platform tools on Windows and macOS; on Linux use CMake's runtime-dependency resolution and also deploy non-Qt project libraries
One cross-platform description of deployment replaces three divergent shell scripts.
Packaging is separate, per platform
- Windows — MSI, NSIS or Inno Setup, plus Authenticode signing
- macOS — A .app bundle, Developer ID signing of every nested framework and plugin, hardened runtime, notarization
- Linux — AppImage, Flatpak, or native .deb and .rpm, each with a different dependency philosophy
- All three — Qt Installer Framework: online and offline installers plus a maintenance tool to add, remove and update components
The escape hatch: static Qt
A static build imports required plugins at link time and genuinely deletes the whole failure class. It also means building Qt from source and, under LGPLv3, an obligation to let recipients relink.
> [!NOTE] That is why the static-build conversation and the commercial-license conversation are the same one.
Key concepts
- QPA platform plugin
- The plugin implementing the Qt Platform Abstraction layer, required by every Qt GUI application: qwindows.dll on Windows, libqcocoa.dylib on macOS, libqxcb.so or a Wayland plugin on Linux. It must live in a platforms subdirectory under the distribution directory. Its absence produces the "no Qt platform plugin could be initialized" failure before the application draws anything.
- Deferred plugin failures
- Plugins outside platforms are loaded on first use, not at startup. A missing qjpeg gives blank JPEGs, a missing sqldrivers entry gives a database open failure, a missing TLS backend gives failing HTTPS requests. None of these fail the build or the launch, which is why packaging must be validated by exercising features, not by checking that the application starts.
- qt.conf
- An INI file that overrides the paths compiled into the Qt library. Searched for as :/qt/etc/qt.conf in the resource system, then the macOS bundle Resources directory, then the executable directory. Its [Paths] section sets Prefix, Plugins, QmlImports, Libraries, Binaries, Translations and more; relative values resolve against Prefix.
- windeployqt
- The Windows deployment tool. Inspects an executable and collects Qt libraries, plugins, QML modules, compiler runtime, and identifiable third-party dependencies into a runnable folder. Key flags include --dir, --plugindir, --qmldir for QML import scanning, --dry-run, --no-compiler-runtime, and plugin filters. It does not reliably find all application-specific third-party libraries.
- macdeployqt
- The macOS tool that embeds Qt libraries into the bundle as private frameworks under Contents/Frameworks and plugins under Contents/PlugIns, rewrites install names, and optionally produces a disk image with -dmg. Supports -codesign=, -no-codesign, and -hardened-runtime. Deploys platform, imageformats, sqldrivers, accessibility, style, and print plugins unless -no-plugins is given.
- qt_generate_deploy_app_script
- Qt 6 CMake command producing an install-time script that calls qt_deploy_runtime_dependencies. On Windows and macOS it drives windeployqt or macdeployqt; on Linux it uses file(GET_RUNTIME_DEPENDENCIES) and also deploys non-Qt project libraries, excluding system directories by default. Arguments include GENERATE_QT_CONF, NO_PLUGINS, INCLUDE_PLUGIN_TYPES, EXCLUDE_PLUGIN_TYPES, and DEPLOY_TOOL_OPTIONS.
- Qt Installer Framework
- A toolset for creating custom online and offline installers for Linux, Windows, and macOS. Driven by a config.xml plus a packages directory describing components. Produces an installer and a maintenance tool that lets end users add, remove, update, and reconfigure components after installation, which is how in-place updates are delivered.
- QT_DEBUG_PLUGINS
- Environment variable that, set to 1, makes Qt print every plugin path it searches and every load attempt with the reason for failure. It converts the uninformative platform-plugin error into an exact list of directories that were checked, and is the first thing to reach for on any deployment failure.
When to use it
- Shipping a Qt desktop application to end users on Windows, macOS, or Linux rather than running it from a development tree
- Debugging a startup failure that reproduces only on machines without Qt installed — the definition of a deployment problem rather than a build problem
- Moving from hand-written per-platform deployment shell scripts to the Qt 6 CMake deployment API
- Preparing a macOS release that must pass Gatekeeper, which requires signing every embedded framework and plugin, hardened runtime, and notarisation
- Deciding between AppImage, Flatpak, and native packages for Linux, where the bundling philosophy determines your support burden
Interview questions
- Diagnose "This application failed to start because no Qt platform plugin could be initialized" from first principles.
- Set up cross-platform deployment for a Qt 6 CMake project.
- Walk through packaging a Qt application for macOS so it passes Gatekeeper.
- Compare the Linux packaging options for a Qt application and pick one.
- How do you validate a Qt deployment in CI so packaging bugs cannot reach users?
Sign up free to read the full topic
More DevOps topics
Start free on Cariara · Coding problems · System design studio