PyInstaller
PyInstaller is a Python application bundler that freezes a Python program, the CPython interpreter it was built against, and every imported dependency into a single distributable. The output runs on end-user machines without a pre-installed Python interpreter and without a virtual environment. PyInstaller supports CPython on macOS, Linux, and Windows; there is no iOS, Android, or tvOS target. Install it with uv tool install pyinstaller or pipx install pyinstaller.
Key Features
- One-folder bundles via
--onedirand self-extracting single-file executables via--onefile - Hook system that declares hidden imports, data files, and runtime patches for third-party packages
- Community hook repository (
pyinstaller-hooks-contrib) installed alongside PyInstaller, covering packages such as NumPy, SciPy, PyQt, and Matplotlib - Spec file workflow for declarative builds (
pyinstaller my.spec) in addition to CLI flags --exclude-moduleflag for trimming unused transitive dependencies out of a bundle- Optional UPX compression for smaller executables
Build Process
A PyInstaller spec file orchestrates four stages: Analysis walks the entry-point script’s imports to produce a dependency graph and applies hooks for packages that need special handling. PYZ compiles pure-Python modules to bytecode and packs them into a ZlibArchive. EXE concatenates the PYZ, extension modules, shared libraries, and data files (wrapped internally in a PKG CArchive) onto a precompiled native bootloader and writes the executable. COLLECT copies the executable plus any external dependencies into the output folder for one-folder builds; one-file builds skip COLLECT and emit the self-extracting executable directly.
The bootloader is a small C program that, at runtime, extracts or memory-maps the PYZ archive, initializes the embedded Python interpreter, and executes the frozen entry point. PyInstaller ships precompiled bootloaders for supported platforms; the bootloader source is available in the project repository for teams that need to recompile it.
Distribution Modes
The --onedir mode produces a folder containing the executable, the Python interpreter shared library, and every dependency file. Startup is fast because no archive extraction happens at launch.
The --onefile mode concatenates the same contents into a self-extracting archive. At launch, the bootloader extracts the payload into a temporary directory, runs the program, and cleans up on exit. Startup is slower than --onedir and the temporary directory can be inspected by anyone with filesystem access.
Pros
- Extensive third-party hook coverage via
pyinstaller-hooks-contrib, reducing manual work for packages with C extensions or data files - Cross-platform support across macOS, Linux, and Windows from the same spec file
- Active maintenance with regular releases
- Licensed GPLv2-or-later with a documented exception that permits distributing bundled applications under any license, including proprietary ones
- Works with any CPython installation; does not require a specific build backend or project layout
Cons
- Bundle sizes for ML workloads are large. Community reports for PyTorch and TensorFlow
--onefileexecutables range from roughly 500 MB to 1.5 GB before manual--exclude-moduletuning. - No built-in code signing. Windows distributions require
signtoolwith a code-signing certificate; macOS distributions requirecodesignandnotarytoolwith an Apple Developer ID. - Antivirus false positives are a known issue. PyInstaller documents rebuilding the bootloader from source and signing the result as a mitigation, which requires a C toolchain on each target platform.
--onefilestartup is slower than--onedirbecause the payload is extracted on every launch.- No mobile target support. Projects shipping to iOS or Android need a different tool such as Briefcase.
- Bundles the interpreter at build time, so the build machine’s OS and architecture must match the target. Cross-compilation is not supported.
Alternatives
Nuitka compiles Python source to C and produces a native binary instead of freezing bytecode. cx_Freeze provides a lighter-weight freezing workflow for pure-Python programs without heavy third-party dependencies. Briefcase targets native installer formats and app stores on desktop and mobile platforms.
auto-py-to-exe is a third-party GUI wrapper around PyInstaller. It exposes the PyInstaller CLI flags as form inputs and is maintained independently of PyInstaller itself.
Learn More
- PyInstaller documentation
- PyInstaller GitHub repository
- pyinstaller-hooks-contrib provides community-maintained hooks for third-party packages
- auto-py-to-exe is a GUI wrapper around PyInstaller
- Nuitka compiles Python to C instead of freezing bytecode
- cx_Freeze is a freezer with narrower hook coverage
- Briefcase targets native installer formats and mobile platforms
- How do I ship a Python application to end users? compares the freezer options with
uvx, PyApp, and ONNX - What is a Python application?