Nuitka on Windows: Building Native Python Executables
Nuitka on Windows produces genuine .exe binaries — not CPython bundled in a zip archive. The compilation requires a C compiler (either MSVC or MinGW64), and Windows introduces several platform-specific constraints that don’t exist on Linux or macOS.
This guide covers the complete workflow: compiler setup, platform-specific flags, UPX compression, code signing, and GitHub Actions multi-platform builds that produce Windows, Linux, and macOS artifacts from the same pipeline.
Compiler Options: MSVC vs MinGW64
Nuitka on Windows requires one of two C compilers. Each has trade-offs.
Microsoft Visual C++ (MSVC)
MSVC is the native Windows compiler, part of Visual Studio or the Build Tools package.
Advantages:
- Best runtime compatibility: MSVC-compiled binaries are the standard on Windows
- Best optimization: MSVC produces tighter code for Windows targets
- No additional runtime DLLs: the UCRT (Universal C Runtime) is part of Windows 10+
- Required for some C extensions (many scientific libraries on Windows ship MSVC-compiled
.pydfiles)
Setup:
# Install VS Build Tools (no IDE required)
winget install Microsoft.VisualStudio.2022.BuildTools
# During install, select: "Desktop development with C++"
# This includes cl.exe, nmake, and the Windows SDK
After installation, Nuitka auto-detects MSVC when run from a Developer Command Prompt, or when cl.exe is in PATH.
MinGW64
MinGW64 is the GCC-based compiler for Windows. Nuitka can download and configure it automatically.
Advantages:
- Free, no VS license required
- Can be installed programmatically in CI without a full VS installation
- Better for open-source projects that want reproducible builds without MSVC dependencies
Setup via Nuitka (automatic):
python -m nuitka --mingw64 your_script.py
# Nuitka will prompt to download MinGW64 on first run
Manual setup:
# Using winget
winget install GnuWin32.Wget # or use chocolatey
choco install mingw
# Or download directly from winlibs.com
# Add bin/ to PATH
Which to choose: Use MSVC if your application loads compiled C extensions (numpy, scipy, Pillow) that were built with MSVC — mixing compilers can cause subtle runtime failures. Use MinGW64 for pure-Python applications or when building in CI without a VS license.
Basic Windows Compilation
# Minimum viable Windows executable
python -m nuitka --standalone --onefile app.py
# With MSVC explicitly
python -m nuitka --standalone --onefile --msvc="latest" app.py
# With MinGW64
python -m nuitka --standalone --onefile --mingw64 app.py
The output is app.exe (or app.dist/app.exe without --onefile).
Windows-Specific Nuitka Flags
Hide the Console Window
For GUI applications or services that should not show a terminal:
python -m nuitka --standalone --onefile --windows-console-mode=disable app.py
For applications that should show a console when launched from a terminal but not when double-clicked:
python -m nuitka --standalone --onefile --windows-console-mode=attach app.py
Add an Application Icon
python -m nuitka --standalone --onefile --windows-icon-from-ico=icon.ico app.py
The icon file must be .ico format. Convert PNG to ICO with ImageMagick:
magick icon.png -resize 256x256 icon.ico
Set Version Information
python -m nuitka \
--standalone \
--onefile \
--windows-company-name="Thought Parameters LLC" \
--windows-product-name="MyApplication" \
--windows-file-version="1.0.0.0" \
--windows-product-version="1.0.0" \
--windows-file-description="My Application Description" \
app.py
This metadata appears in Properties → Details in Windows Explorer and is used by enterprise software management tools.
Request UAC Elevation
For applications that need administrator privileges:
python -m nuitka --standalone --onefile --windows-uac-admin app.py
This triggers the UAC prompt when the executable is launched.
UPX Compression
UPX (Ultimate Packer for eXecutables) compresses the binary and decompresses at runtime. It reduces binary size by 40–60% at the cost of a small startup delay.
# Install UPX via winget or chocolatey
winget install upx.upx
# Nuitka with UPX (auto-detected if in PATH)
python -m nuitka --standalone --onefile app.py
# Nuitka automatically uses UPX when available
# Disable UPX if you don't want compression
python -m nuitka --standalone --onefile --noinclude-dlls-mode=none app.py
Size comparison (typical FastAPI application):
| Configuration | Binary Size |
|---|---|
| Standalone directory | ~45 MB |
| Onefile, no UPX | ~38 MB |
| Onefile, with UPX | ~16 MB |
UPX is disabled automatically for --onefile mode on some configurations where it causes extraction issues. Test your specific build.
Code Signing
Unsigned Windows executables trigger SmartScreen warnings (“Windows protected your PC”). For software distributed to end users, code signing is essential.
Obtaining a Code Signing Certificate
- EV (Extended Validation) certificates: required for SmartScreen reputation. Cost: $200–500/year from DigiCert, Sectigo, or GlobalSign.
- OV (Organization Validation) certificates: cheaper but still trigger SmartScreen for new publishers.
- Self-signed certificates: appropriate only for internal tools on managed endpoints.
Signing with signtool.exe
# Sign with a PFX file (local certificate store)
signtool sign \
/fd SHA256 \
/f certificate.pfx \
/p "$env:CERT_PASSWORD" \
/t http://timestamp.digicert.com \
app.exe
# Verify signature
signtool verify /pa app.exe
The /t flag adds a timestamp from a trusted TSA, so the signature remains valid after the certificate expires.
Azure Key Vault Signing (CI/CD)
For CI pipelines, storing a PFX file as a secret is risky. Azure Key Vault stores the private key and performs signing without exposing it:
# Using AzureSignTool
dotnet tool install --global AzureSignTool
AzureSignTool sign \
--azure-key-vault-url "https://your-vault.vault.azure.net/" \
--azure-key-vault-client-id "$env:AZURE_CLIENT_ID" \
--azure-key-vault-client-secret "$env:AZURE_CLIENT_SECRET" \
--azure-key-vault-tenant-id "$env:AZURE_TENANT_ID" \
--azure-key-vault-certificate "CodeSigningCert" \
--timestamp-rfc3161 "http://timestamp.digicert.com" \
--file-digest sha256 \
app.exe
GitHub Actions: Multi-Platform Build
This workflow produces Windows (.exe), Linux, and macOS binaries from the same codebase:
# .github/workflows/release.yml
name: Build Release Binaries
on:
push:
tags: ['v*']
jobs:
build:
strategy:
matrix:
include:
- os: windows-latest
artifact: app.exe
nuitka_flags: --msvc=latest
- os: ubuntu-latest
artifact: app
nuitka_flags: ""
- os: macos-latest
artifact: app
nuitka_flags: ""
runs-on: ${{ matrix.os }}
name: Build on ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Install dependencies
run: pip install -r requirements.txt nuitka
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: sudo apt-get install -y gcc patchelf ccache
- name: Compile with Nuitka
run: |
python -m nuitka \
--standalone \
--onefile \
--include-package=your_package \
${{ matrix.nuitka_flags }} \
--output-filename=${{ matrix.artifact }} \
main.py
- name: Sign Windows binary
if: runner.os == 'Windows'
run: |
# Replace with your signing solution
# signtool sign /fd SHA256 /f cert.pfx /p $env:CERT_PW app.exe
echo "Signing step placeholder"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-binary
path: ${{ matrix.artifact }}
retention-days: 30
release:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Create release
uses: softprops/action-gh-release@v1
with:
files: |
windows-latest-binary/app.exe
ubuntu-latest-binary/app
macos-latest-binary/app
generate_release_notes: true
Common Windows Compilation Errors
ModuleNotFoundError at runtime
Nuitka’s static analysis missed an import that uses a string-based module reference.
# Force-include the missing module
python -m nuitka --include-package=missing_package app.py
DLL load failed for C extensions
A compiled C extension (.pyd file) depends on a DLL that is not present in the distribution.
# Use Dependency Walker or dumpbin to find missing DLLs
# C:\Program Files\Microsoft Visual Studio\...\VC\Tools\MSVC\...\bin\Hostx64\x64\dumpbin.exe
dumpbin /dependents app.exe
# Add the missing DLL to the distribution
python -m nuitka --include-data-files=C:\path\to\missing.dll=missing.dll app.py
Anti-virus false positives
Compiled Python executables are sometimes flagged by AV engines due to their unusual structure (embedded Python interpreter, self-extraction). Code signing significantly reduces this.
For unsigned internal tools, add the binary to Windows Defender exclusions:
Add-MpPreference -ExclusionPath "C:\path\to\app.exe"
Error: C compiler can't be found
# Use the Developer Command Prompt, or run vcvarsall.bat first
& "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64
# Or use --mingw64 to avoid MSVC dependency
python -m nuitka --mingw64 app.py
Windows Installer Generation
A compiled .exe can be wrapped in a proper Windows installer using NSIS or Inno Setup:
; installer.nsi — NSIS installer script
!define APP_NAME "My Application"
!define APP_VERSION "1.0.0"
!define INSTALLER_NAME "MyApp-Setup-${APP_VERSION}.exe"
Name "${APP_NAME}"
OutFile "${INSTALLER_NAME}"
InstallDir "$PROGRAMFILES64\MyApp"
Section "MainSection"
SetOutPath "$INSTDIR"
File "app.exe"
CreateShortcut "$DESKTOP\MyApp.lnk" "$INSTDIR\app.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Uninstall"
Delete "$INSTDIR\app.exe"
Delete "$INSTDIR\Uninstall.exe"
Delete "$DESKTOP\MyApp.lnk"
RMDir "$INSTDIR"
SectionEnd
Compile the installer:
makensis installer.nsi
This integrates with Windows Add/Remove Programs, handles upgrades cleanly, and can be code-signed the same way as the application binary.
Cross-Platform Behavior Differences
Nuitka-compiled applications behave identically on all platforms with one important exception: you must compile on the target platform. Cross-compilation (building a Windows .exe on Linux) is not supported. Use a matrix build in CI to produce platform-specific binaries.
| Behavior | Windows | Linux | macOS |
|---|---|---|---|
| Default output | app.exe |
app |
app |
| C compiler | MSVC or MinGW64 | GCC | Clang |
| Binary format | PE32+ | ELF | Mach-O |
| Cross-compilation | Not supported | Not supported | Not supported |
--onefile extraction dir |
%TEMP%\nuitka-* |
/tmp/nuitka-* |
$TMPDIR/nuitka-* |
| DLL dependencies | .dll files |
.so files |
.dylib files |
Summary
Nuitka on Windows is production-ready when you follow the platform-specific patterns:
- Use MSVC for applications with compiled C extension dependencies; MinGW64 for pure-Python applications or CI without VS licenses.
- Set version metadata and icon for professional distribution.
- Sign with an EV certificate to avoid SmartScreen warnings.
- Use GitHub Actions matrix builds to automate multi-platform binary production.
- For user-facing software, wrap the
.exein an NSIS or Inno Setup installer.
The combination of a properly signed, Nuitka-compiled .exe with a Windows installer is indistinguishable from software compiled in C or Go — and it is the most professional way to distribute a Python application to Windows users.
Related: Nuitka Packaging for Web Frameworks: FastAPI, Flask, and Django | Nuitka vs PyInstaller: Python Packaging Compared | Nuitka + Docker: Containerizing Compiled Python Applications