Skip to content

cx_Freeze

cx_Freeze is a Python application freezer that packages a Python program and its dependencies into a self-contained directory or executable. It targets Windows, macOS, and Linux from the same configuration and ships native commands for producing each platform’s preferred installer format. Recent releases add experimental support for freezing free-threaded CPython builds. Install it with uv tool install cx_Freeze or pipx install cx_Freeze.

When to Use cx_Freeze

cx_Freeze fits projects whose deliverable is an installer rather than a raw binary. Its bdist_msi, bdist_dmg, bdist_appimage, and bdist_deb commands produce platform-native installers in one step, without a second packaging tool like WiX, dmgbuild, or dpkg-deb. Most other freezers stop at a folder or single executable and leave installer generation to the user.

For a single static binary with heavy optimization, Nuitka compiles Python to C and produces smaller, faster executables. For the largest plugin and hook ecosystem covering obscure scientific libraries, PyInstaller has the deepest catalog. cx_Freeze sits between them: fewer hooks than PyInstaller, no C compilation like Nuitka, but the only one of the three that treats installer generation as a first-class feature.

Key Features

  • Native installer commands: bdist_msi on Windows, bdist_dmg and bdist_mac on macOS, bdist_appimage, bdist_deb, and bdist_rpm on Linux
  • Cross-platform from one config: The same [tool.cxfreeze] table in pyproject.toml drives freezes on every supported OS
  • Free-threaded Python support: Experimental support for freezing free-threaded CPython (3.13t) builds
  • Dependency discovery: A static module finder walks the program’s imports and copies the required pure-Python modules, extension modules, and shared libraries into the output
  • Explicit include/exclude lists: includes, include_files, excludes, packages, and zip_include_packages give direct control over what ends up in the frozen build
  • Hooks for common libraries: Ships hooks for PySide6, PyQt6, numpy, scipy, matplotlib, pandas, PyTorch, tkinter, and others that need special handling for data files or dynamic imports
  • Multiple configuration formats: Reads pyproject.toml, setup.py, setup.cfg, or CLI flags; a cxfreeze-quickstart command generates a starter script

How cx_Freeze Works

cx_Freeze runs the project’s setup, walks the import graph starting from each entry-point script, and copies every detected module into a build directory alongside a bootstrap executable. The bootstrap executable embeds a C launcher that initializes CPython using PEP 587 Python Initialization Configuration and then runs the frozen script.

The output is a folder containing the launcher, a shared python3x.dll/libpython3.x.so/Python binary, a zip file of pure-Python modules, and any extension modules or data files the application needs. Running the launcher on a machine without Python installed loads the bundled interpreter and executes the frozen code with no additional install step.

Configuration lives in [tool.cxfreeze] in pyproject.toml:

pyproject.toml
[project]
name = "guifoo"
version = "0.1"

[tool.cxfreeze]
executables = [
    {script = "guifoo.py", base = "gui"}
]

[tool.cxfreeze.build_exe]
excludes = ["tkinter", "unittest"]
zip_include_packages = ["encodings", "PySide6", "shiboken6"]

Running cxfreeze build_exe then produces the frozen folder, and cxfreeze bdist_msi (or bdist_dmg, bdist_appimage, bdist_deb) wraps that folder in a native installer.

Installer Formats

cx_Freeze provides six bdist_* commands that other freezers leave to separate tooling:

Command Output Platform
bdist_msi Windows Installer .msi Windows
bdist_mac macOS .app bundle macOS
bdist_dmg macOS disk image .dmg macOS
bdist_appimage Linux AppImage (single-file executable) Linux
bdist_deb Debian/Ubuntu .deb package Linux
bdist_rpm Red Hat .rpm package Linux

On Python 3.13 and 3.14, bdist_msi depends on the python-msilib backport (msilib was removed from the standard library in 3.13), pulled in automatically as a dependency.

Package Split

Recent releases split the project into two PyPI packages: cx_Freeze (the user-facing CLI, setup-script API, and hooks) and freeze-core (the platform-specific freezing internals). Installing cx_Freeze still pulls in freeze-core automatically. The split let the project add free-threaded Python support without churning the main package.

Pros

  • Only mainstream Python freezer with first-class bdist_msi, bdist_dmg, bdist_appimage, and bdist_deb commands
  • Configuration lives in pyproject.toml under [tool.cxfreeze], matching the standard Python project layout
  • Supports recent CPython releases, with experimental free-threaded (3.13t) support
  • Active maintenance by Marcelo Duarte with regular releases
  • Hooks for GUI and scientific packages (PySide6, PyQt6, numpy, scipy, matplotlib, pandas, PyTorch) are maintained in-tree

Cons

  • Smaller hook ecosystem than PyInstaller; obscure libraries may need manual includes/include_files entries
  • No compilation step: the bundle carries the full Python interpreter and source bytecode, so output size tracks the source plus CPython (Nuitka’s compile-to-C output can be smaller and faster)
  • Antivirus false positives happen to any freezer that bundles a Python interpreter; code signing with a real certificate is the usual fix
  • bdist_dmg needs a macOS host, bdist_msi needs Windows, and bdist_deb/bdist_appimage need Linux: cross-building installers across OSes is not supported
  • Smaller community and fewer Stack Overflow answers than PyInstaller when something goes wrong

Learn More

Last updated on

Please submit corrections and feedback...