Python zipapp: Build Executable Apps

Published on: September 1, 2026
Reading time: 5 minutes
Python application packaged as an executable zipapp archive

The zipapp module lets you package a Python application as a single executable file, usually with the .pyz extension. The format is based on a regular ZIP archive containing a __main__.py entry point. It is useful for command-line tools, internal utilities, educational projects, and controlled environments where Python is already installed.

Because zipapp belongs to the standard library, you do not need an external packager for the basic workflow. It is not a complete replacement for wheels, containers, or tools that bundle the interpreter, but it offers an elegant middle ground when the project is mostly pure Python and easy distribution matters.

How a pyz file works

A .pyz file is a ZIP archive that Python can execute directly. The interpreter opens the archive, finds __main__.py, and runs it as the application entry point. The archive can contain packages, modules, and resource files that are compatible with ZIP imports.

my_app/
├── __main__.py
├── cli.py
└── utils.py

Keep __main__.py small. It should usually import a real main function and delegate execution to it. This design improves testability and keeps command-line concerns separate from business logic.

from cli import main

if __name__ == "__main__":
    raise SystemExit(main())

Create your first zipapp

Once the source directory is ready, run:

python -m zipapp my_app -o my_app.pyz

The -o option defines the output file. You can execute the result with:

python my_app.pyz

On Unix-like systems, add an interpreter line and mark the file as executable:

python -m zipapp my_app -o my_app.pyz -p "/usr/bin/env python3"
chmod +x my_app.pyz
./my_app.pyz

The -p argument writes a shebang. On Windows, execution generally remains python app.pyz unless file associations are configured.

Choose an entry point

If the source directory does not already contain __main__.py, use -m with a module:function target:

python -m zipapp my_app -m "cli:main" -o tool.pyz

The selected function must be importable and should not require positional arguments. The generated entry point calls it automatically.

Use the Python API

For build scripts and continuous integration, use zipapp.create_archive():

from zipapp import create_archive

create_archive(
    "my_app",
    target="dist/my_app.pyz",
    interpreter="/usr/bin/env python3",
    main="cli:main",
    compressed=True,
)

The API makes it easier to generate reproducible artifacts, place them in a distribution directory, and combine packaging with validation steps.

Handle dependencies

Pure Python dependencies can be installed into the staging directory before archive creation. A common approach uses pip --target:

python -m pip install -r requirements.txt --target build/app
cp -r src/* build/app/
python -m zipapp build/app -o dist/app.pyz -m "cli:main"

This technique does not work reliably for every package with native extensions. Shared libraries such as .so or .pyd files may need a real filesystem location and platform-specific installation. For those projects, choose a wheel, container, or another packaging method.

Python version compatibility

A zipapp does not include Python itself. The target machine must provide a compatible interpreter and any system-level requirements. If your source uses new syntax or standard-library features, publish the minimum supported version clearly.

Portable Python code can still depend on operating-system behavior. Paths, permissions, external commands, encodings, and process signals should be tested on every supported platform.

Package resource files correctly

Do not assume that every internal resource has a normal filesystem path. Prefer importlib.resources to read package data:

from importlib.resources import files

template = files("my_package").joinpath("template.txt").read_text(
    encoding="utf-8"
)

This approach works better for modules loaded from ZIP archives. Directly using Path(__file__).parent may fail when a library expects to open a physical path.

Keep configuration outside

Do not embed passwords, tokens, or environment-specific secrets in the archive. A .pyz file is easy to inspect because it is still a ZIP file. Use environment variables, command-line options, secret stores, or external configuration files with appropriate permissions.

Compression choices

Compression can reduce artifact size, especially when the application contains many text files. However, startup may require additional decompression work. For small command-line tools the difference is usually minor. Measure both size and startup time when they matter.

Integrity and distribution

A single artifact simplifies deployment, rollback, and checksum verification. Generate a SHA-256 digest and publish it through a trusted channel:

python -c "import hashlib, pathlib; p=pathlib.Path('app.pyz'); print(hashlib.sha256(p.read_bytes()).hexdigest())"

For higher-assurance distribution, sign the artifact and verify the signature before deployment.

Test the final archive

Unit tests against the source tree are not enough. Build the archive in CI and execute real smoke tests. Resource access, dynamic imports, package metadata, and libraries that depend on physical files can behave differently inside a ZIP.

Test help output, invalid options, exit codes, supported Python versions, and representative operating systems. A simple build-and-run matrix can prevent broken releases.

Use cases

  • Internal command-line utilities.
  • Administrative scripts with several modules.
  • Pure Python applications for controlled servers.
  • Educational tools distributed as one download.
  • Build artifacts used in automation pipelines.

When to use another format

Choose wheels when users should install the package into an environment. Choose containers when operating-system dependencies and services must be controlled. Choose a standalone executable builder when users do not have Python. Zipapp is strongest when the interpreter already exists and the application remains mostly pure Python.

Reproducible builds

Pin dependency versions, build in a clean directory, exclude caches and temporary files, and document the command that generated the archive. Reproducibility makes debugging and rollback much easier.

Security considerations

Packaging does not make code secret or trusted. Treat downloaded archives like any executable code. Verify origin, checksums, signatures, and dependency sources. Do not run untrusted zipapps with elevated privileges.

Maintainable project structure

Keep business logic in importable modules, use a small CLI layer, validate input, return meaningful exit codes, and log failures without exposing secrets. This structure works well both before and after packaging.

For related Academify guides, read Python importlib.resources, Python virtual environments, Python argparse, and Python subprocess.

Conclusion

zipapp provides a lightweight way to turn a Python application into one executable archive. It works best for pure Python projects and environments with a compatible interpreter. With a clear entry point, correct resource handling, pinned dependencies, external configuration, integrity checks, and tests against the final artifact, a zipapp can become a simple and dependable distribution format.

External sources

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Python code used to compose functions with functools.Placeholder
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    functools.Placeholder: Positional Gaps in partial

    Learn Python functools.Placeholder to leave positional gaps in partial functions and build clearer reusable functional APIs.

    Ler mais

    Tempo de leitura: 5 minutos
    31/08/2026
    Developer coding and analyzing data with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    itertools.pairwise: Compare Neighboring Values

    Learn Python itertools.pairwise to compare neighboring values, detect transitions, calculate deltas, and build clear lazy pipelines.

    Ler mais

    Tempo de leitura: 4 minutos
    31/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python types.new_class: Dynamic Classes

    Learn Python types.new_class for dynamic classes with metaclasses, prepared namespaces, inheritance, metadata, and safe factories.

    Ler mais

    Tempo de leitura: 3 minutos
    30/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    partialmethod: Build Specialized Methods in Python

    Learn Python partialmethod for specialized methods with correct binding, fewer wrappers, clear domain names, and safe introspection.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    inspect.getmembers_static: Inspect Attributes Safely

    Use Python inspect.getmembers_static to list attributes without executing properties, descriptors, or unwanted dynamic lookup.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026
    Detailed close-up of yellow and white albino python scales, capturing texture and pattern.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    DynamicClassAttribute: Descriptors and Dynamic Access

    Understand Python DynamicClassAttribute, descriptors, class versus instance access, metaclasses, Enum behavior, and safe introspection.

    Ler mais

    Tempo de leitura: 2 minutos
    30/08/2026