# PyInstaller: packaging a Python program into a stand-alone executable

> PyInstaller bundles a Python application and its dependencies into a single folder or a single file, so the target machine needs no interpreter. It is not a cross-compiler, and that constraint shapes every workflow built on it.

**pyinstaller/pyinstaller** — Freeze (package) Python programs into stand-alone executables

- Repository: https://github.com/pyinstaller/pyinstaller
- Website: http://www.pyinstaller.org
- Stars: 13,115 · Forks: 2,030
- Language: Python
- License: NOASSERTION
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/pyinstaller-pyinstaller

## The distribution problem PyInstaller solves

Python programs normally assume a Python interpreter exists on the machine that runs them. That assumption breaks the moment you hand a script to someone who does not develop in Python: an end user on Windows, an operator on a locked-down server, a colleague who only wants to double-click an icon. Telling them to install Python and then pip install a list of modules is not a distribution story, it is a support burden.

PyInstaller targets that gap. The README describes it as bundling a Python application and all its dependencies into a single package, so the user can run the packaged app without installing a Python interpreter or any modules. The audience is developers and system administrators who ship Python tooling to machines they do not administer. The project classifies itself as Development Status 6, Mature, and it is tested against Windows, macOS and GNU/Linux, with contributed support for AIX, Solaris, FreeBSD and OpenBSD that is not part of continuous integration.

## How the freeze actually works

The mechanism is static analysis followed by collection, not compilation. PyInstaller reads your entry script, analyzes the code to discover every other module and library the script needs in order to execute, then collects copies of all those files, including the active Python interpreter, and places them alongside your script in a single folder, or optionally in a single executable file. Nothing is translated into machine code. Your Python source is still Python source inside the bundle, executed by the bundled interpreter.

That design has a direct consequence for dynamic imports. Because discovery is analysis-driven, code that imports modules by computed name at runtime is not visible to the analyzer in the same way a literal import statement is. The repository layout reflects how much of this is handled outside the core: there is a separate pyinstaller-hooks-contrib dependency, listed in pyproject.toml with a minimum of 2026.7, which carries the per-package integration logic for third-party libraries. The README claims out-of-the-box bundling for numpy, PyQt5, PySide2, PyQt6, PySide6, wxPython and matplotlib, and states that the required tricks for external packages are already integrated. Read that as a statement about the hook ecosystem rather than about the analyzer being omniscient; a package with no hook and unusual loading behaviour is where builds go wrong.

The bootloader is the other half. It is a compiled component, kept in its own top-level bootloader/ directory, and on contributed platforms you must build it yourself. The README says this happens automatically during pip install provided you have a C compiler such as gcc or clang and zlib's development headers already present.

## Installing PyInstaller and producing a first executable

PyInstaller is distributed on PyPI. The README gives a single install command:

```bash
pip install pyinstaller
```

After that, basic usage is one command against your main script. The README's example takes a path:

```bash
pyinstaller /path/to/yourscript.py
```

Run it from the directory containing your entry point and PyInstaller writes its output into a build/ directory plus a dist/ directory. The default is the folder form: dist/yourscript/ holding the executable together with the collected modules and libraries. The single-file form is the option people search for most often, and the README refers to it as putting your script optionally in a single executable file. On Windows, suppressing the console window for a GUI application is a separate concern from the packaging mode, so a typical GUI build combines a windowed mode with the single-file output. Expect to rebuild more than once. The first run tells you which modules were discovered; the second, after you add hooks or data files, tells you whether the bundle actually starts on a clean machine.

## Not a cross-compiler, and what that costs you

This is the constraint that decides whether PyInstaller fits your release process. The README states it without hedging: PyInstaller is not a cross-compiler. To make a Windows app you run PyInstaller in Windows; to make a GNU/Linux app you run it in GNU/Linux, and so on. If your CI is a single Linux runner and your users are on Windows, PyInstaller cannot help you from that runner. You need a Windows build machine or a Windows job in your pipeline.

There are two more boundaries worth stating plainly. The supported interpreter range is Python 3.8 to 3.15, and the README notes that Python 3.10.0 contains a bug making it unsupportable, and that beta releases of Python 3.16 will not work. Building with an interpreter outside that window is not a supported configuration. On Linux, PyInstaller expects ldd, objdump and objcopy to be present, typically from the glibc or libc-bin and binutils packages. A minimal container image without binutils will fail before it packages anything.

Finally, consider what freezing does not do. It does not make your program faster, and it does not protect your source. The interpreter ships inside the bundle, and the modules are collected as files. If your reason for looking at a freezer is obfuscation or performance, you are looking at the wrong category of tool.

## Nuitka and the compile-to-native alternative

The comparison that comes up most often is Nuitka, and the difference is architectural rather than a matter of flags. PyInstaller collects an interpreter and your bytecode into an archive and runs them; Nuitka translates Python modules into C and compiles them ahead of time, producing a native binary that does not carry a general-purpose interpreter in the same way.

That changes the trade-offs in both directions. A compiler can remove interpreter overhead, which is the reason people reach for it, but it also has to model Python's dynamic semantics precisely, and build times and toolchain requirements grow accordingly. PyInstaller's approach keeps semantics identical to running the script under the same interpreter, which is why it can claim compatibility with a large set of GUI and scientific packages through hooks. If your goal is a smaller, faster artifact and you can accept a C toolchain in your build, Nuitka is the more direct answer. If your goal is to reproduce the behaviour of an existing script on a machine without Python, PyInstaller's model is closer to what you want.

## Licence, maintenance and upgrade cost

The licence is not a standard SPDX identifier in this repository. pyproject.toml declares it as GPLv2-or-later with a special exception which allows you to use PyInstaller to build and distribute non-free programs, including commercial ones, and the classifiers list GPLv2. The practical reading is that the exception exists precisely so that the output of a freeze is not forced under the GPL, but the exception text is the thing that governs, not a summary of it. If your organisation has a policy gate on copyleft dependencies, route the actual licence file through it rather than relying on the classifier.

On maintenance: the repository is not archived, and the last push was on 2026-09-20, one day before this writing, with v6.22.3 released on 2026-09-12. That is a fast release cadence. The upgrade cost that matters is not PyInstaller itself but the hook package: pyinstaller-hooks-contrib is a separate dependency with its own version floor, so a third-party library that changes its package layout can break a build even when you have not touched your own code. Pin both, and rebuild on a schedule rather than only when you ship.

## Conclusion

Adopt PyInstaller if you ship a Python tool to machines you do not control, or if you need a single executable for a desktop app on Windows, macOS or Linux. Do not adopt it if your real requirement is compiling Python to native code for speed, or if you need to build a Windows binary from a Linux CI runner: the README states plainly that PyInstaller is not a cross-compiler. Before committing, verify the interpreter version you build with, since the supported range is Python 3.8 to 3.15 and the README calls out 3.10.0 as unsupportable, and confirm that every third-party package in your dependency tree is covered by pyinstaller-hooks-contrib for your target platform.

## FAQ

### What is PyInstaller used for?

It bundles a Python application and all its dependencies into a single package, so the user can run the packaged app without installing a Python interpreter or any modules. The output is a folder, or optionally a single executable file.

### Why is PyInstaller not recognized?

The README's installation step is pip install pyinstaller, and the command is only available after that install completes in the environment you are using. If the interpreter or virtual environment you are building with is not the one where the package was installed, the command will not resolve.

### Is there an alternative to PyInstaller?

Nuitka is the alternative that comes up most often, and the difference is in approach: Nuitka compiles Python to C ahead of time, while PyInstaller collects the interpreter and the modules into an archive. The repository topics also list py2app and py2exe, which are platform-specific.

### How do I install PyInstaller?

PyInstaller is available on PyPI and installs with pip install pyinstaller, as shown in the README's Installation section. On contributed platforms the bootloader is built during that install, which requires a C compiler and zlib's development headers.

### How do I use PyInstaller to make an exe?

Run pyinstaller against your main script, for example pyinstaller /path/to/yourscript.py, and the packaged output appears in the dist directory. There is no separate exe command; the platform you run the build on determines the executable format.

## Sources

- [Issues](https://github.com/pyinstaller/pyinstaller/issues)
- [Project website](http://www.pyinstaller.org)
- [pyinstaller/pyinstaller on GitHub](https://github.com/pyinstaller/pyinstaller)
- [README](https://github.com/pyinstaller/pyinstaller/blob/develop/README.md)
- [Releases](https://github.com/pyinstaller/pyinstaller/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pyinstaller-pyinstaller
