Jason Miller / Nuitka on Windows: Cross-Platform Python Compilation Guide

Created Tue, 18 Aug 2026 02:00:00 -0700 Modified Tue, 18 Aug 2026 22:01:35 +0000

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 .pyd files)

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:

  1. Use MSVC for applications with compiled C extension dependencies; MinGW64 for pure-Python applications or CI without VS licenses.
  2. Set version metadata and icon for professional distribution.
  3. Sign with an EV certificate to avoid SmartScreen warnings.
  4. Use GitHub Actions matrix builds to automate multi-platform binary production.
  5. For user-facing software, wrap the .exe in 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