I've been experimenting with packaging PyQt apps, and on macOS the resulting package with all the necessary dependencies was almost as big as an Electron installer. What kind of file sizes can you expect for standalone installers on Windows and macOS?
This is a common concern when you start distributing PyQt applications. You've spent time building a lean, well-crafted GUI app, and then the packaged installer turns out to be 100MB or more. That can feel surprising, especially if your actual application code is only a few kilobytes. Let's look at why this happens and what you can do about it.
Why are PyQt installers so large?
When you package a PyQt application into a standalone installer, you're bundling much more than just your own code. The installer needs to include:
- The Python interpreter — since your users may not have Python installed, the entire runtime gets bundled in.
- Qt libraries — PyQt is a wrapper around the Qt framework, which is a large, full-featured C++ toolkit. Even a simple "Hello World" window pulls in a significant portion of Qt.
- Python packages — any third-party libraries your app depends on (NumPy, requests, etc.) get included too.
- Platform-specific libraries — shared libraries and frameworks required by your operating system.
On macOS, Python 3 isn't installed by default, so the full interpreter must be included. This alone adds a meaningful amount to the final size. Qt's libraries are also substantial because Qt provides a wide range of functionality — from widgets and graphics to networking and multimedia — and packaging tools sometimes include more of Qt than your app strictly needs.
Typical installer sizes
For a minimal PyQt6 application — a single window with a few widgets and no heavy dependencies — you can expect roughly the following:
| Platform | Approximate Size |
|---|---|
| Windows (.exe via PyInstaller) | 30–80 MB |
| macOS (.app bundle via PyInstaller) | 50–120 MB |
| Linux (AppImage or similar) | 40–90 MB |
These numbers vary depending on which Qt modules your application imports, which version of Python and PyQt you're using, and which packaging tool you choose. If your app uses additional libraries like matplotlib, pandas, or numpy, the size can grow well beyond these ranges.
For comparison, a basic Electron app typically starts around 50–120 MB as well, so the sizes are in a similar ballpark. The comparison with Electron is a reasonable one — both approaches bundle a significant runtime (Python + Qt vs. Chromium + Node.js).
How packaging tools decide what to include
Tools like PyInstaller analyze your Python imports and try to figure out which files are needed. However, they tend to err on the side of caution, including modules and shared libraries that might be needed rather than risk a missing dependency at runtime. This is sensible behavior — a working-but-large installer is better than a small-but-broken one — but it does mean extra files often sneak in.
PyInstaller has two main modes:
- One-folder mode (
--onedir): produces a directory containing the executable and all dependencies. This is the default and generally preferred for distribution. - One-file mode (
--onefile): bundles everything into a single executable. The file size is similar (sometimes slightly larger due to compression overhead), and the app extracts itself to a temporary directory on each launch, which makes startup slower.
Reducing installer size
There are several practical steps you can take to bring the size down.
Use a virtual environment
Always package from a clean virtual environment that contains only the dependencies your app actually needs. If you're packaging from your system Python or a cluttered development environment, PyInstaller will pick up packages you don't need.
python -m venv packaging_env
source packaging_env/bin/activate # On macOS/Linux
# packaging_env\Scripts\activate # On Windows
pip install pyqt6 pyinstaller
# Install only your app's actual dependencies
Exclude unnecessary Qt modules
PyQt6 comes with many Qt modules — QtWebEngine, Qt3D, QtMultimedia, and others — that your app probably doesn't use. You can tell PyInstaller to exclude them using the --exclude-module flag:
pyinstaller --windowed --exclude-module PyQt6.QtWebEngineWidgets \
--exclude-module PyQt6.Qt3D \
--exclude-module PyQt6.QtMultimedia \
your_app.py
You can also configure these exclusions in a PyInstaller .spec file for repeatability:
# your_app.spec
a = Analysis(
['your_app.py'],
excludes=[
'PyQt6.QtWebEngineWidgets',
'PyQt6.QtWebEngineCore',
'PyQt6.Qt3DCore',
'PyQt6.Qt3DRender',
'PyQt6.QtMultimedia',
'PyQt6.QtBluetooth',
'PyQt6.QtNfc',
'PyQt6.QtPositioning',
'PyQt6.QtSensors',
'PyQt6.QtSerialPort',
],
# ... rest of the spec file
)
QtWebEngine is particularly large (it bundles a Chromium-based browser engine), so excluding it — if you don't need it — can save a lot of space.
Use UPX compression
UPX (Ultimate Packer for Executables) can compress the bundled executables and shared libraries. PyInstaller supports UPX automatically if it's available on your system. Install UPX, make sure it's on your PATH, and PyInstaller will use it during the build:
pyinstaller --windowed your_app.py
This can reduce the final size by 20–40%, depending on the contents. Note that UPX compression can occasionally cause issues with certain libraries or trigger false positives in antivirus software on Windows, so test your packaged app thoroughly.
Strip unused translations and plugins
Qt ships with translation files and platform plugins that you may not need. After PyInstaller generates the build folder, you can manually inspect and remove unnecessary files from the output directory, such as:
- Translation files (
.qmfiles) for languages you don't support - Unused image format plugins (if you only use PNG, you don't need the JPEG2000 or TIFF plugins)
- Unused platform plugins
Be careful with this approach — removing the wrong file will break your app. Test on a clean machine after making changes.
Consider alternative packaging tools
Different tools produce different results. It's worth trying more than one:
- PyInstaller — the most popular option, good cross-platform support.
- cx_Freeze — another mature option that sometimes produces smaller output.
- Nuitka — compiles Python to C, which can produce smaller and faster executables in some cases.
- briefcase (part of the BeeWare project) — creates native app packages and can offer good size characteristics.
Each has different trade-offs in terms of ease of use, size, and compatibility.
A realistic perspective
It's worth keeping some perspective on installer sizes. A 50–80 MB installer is modest by modern standards. Many desktop applications users install daily are hundreds of megabytes or more. While it's good practice to keep your installer as lean as possible — especially if users are downloading over slow connections — the size of a PyQt application is generally reasonable and comparable to other cross-platform frameworks.
The real advantage of PyQt over something like Electron is runtime performance and memory usage. A PyQt app will typically use significantly less RAM and feel more responsive than an equivalent Electron app, even if the installer sizes are similar.
Summary
| Strategy | Potential Savings |
|---|---|
| Clean virtual environment | Varies — avoids bundling unneeded packages |
| Exclude unused Qt modules | 10–50 MB+ (especially QtWebEngine) |
| UPX compression | 20–40% of total size |
| Remove unused plugins/translations | A few MB |
| Try alternative packaging tools | Varies |
Start with a clean virtual environment and targeted exclusions — those two steps alone often make the biggest difference. From there, you can fine-tune with compression and manual cleanup to get the installer size to a point you're comfortable with.
Create GUI Applications with Python & Qt6 by Martin Fitzpatrick
(PyQt6 Edition) The hands-on guide to making apps with Python — Over 15,000 copies sold!