Why compile Python at all
If you're building a desktop app in Python and want a normal person to run it, "pip install" isn't an option. They don't have Python installed, they don't want to install it, and they shouldn't have to. You need a real .exe they can double-click. That means compiling — turning your interpreted Python code and its dependencies into something that runs without a Python installation on the target machine.
Why PyInstaller wasn't good enough
PyInstaller was my first attempt, and it works — technically. The problem is what it actually does: it bundles a Python interpreter and all your dependencies into a package, then unpacks them to a temp folder every time the app starts (in --onefile mode) or ships them as a folder full of loose files (in --onedir mode). Either way, you're still running interpreted bytecode through an embedded Python runtime at startup, not compiled machine code. Startup felt sluggish, the temp-extraction step added a visible delay on first launch, and antivirus software treats "unpacks itself to %TEMP% and starts executing" as a textbook heuristic for malware — which caused exactly the false-positive problem I was trying to avoid.
The right approach: Nuitka --standalone
Nuitka is a real Python-to-C compiler. It doesn't bundle an interpreter — it translates your Python code into C, compiles that with a real C compiler, and links it into an actual native executable. The result runs as compiled machine code, not interpreted bytecode inside an embedded runtime. Startup is close to instant, and — critically — it never extracts itself to a temp folder at runtime, which removes the single biggest trigger for antivirus heuristics. The flag that matters most is --standalone: it produces a self-contained folder with your .exe and its native dependencies sitting right next to it, ready to distribute as-is or wrap in an installer.
How to actually do it
The 3 mistakes that cost me hours
⚠️ Forgetting --enable-plugin=tk-inter
If your GUI uses Tkinter or CustomTkinter and you skip this flag, the compiled app doesn't error out loudly — it just fails to load the GUI toolkit and can crash silently or show a blank window. Nuitka doesn't include GUI framework support by default; you have to opt in per-framework.
⚠️ Using --onefile instead of --standalone
--onefile is tempting because it produces a single .exe instead of a folder. Don't use it for anything you plan to distribute publicly. Onefile builds still self-extract to a temp directory at runtime — the same behavior that made PyInstaller trigger false positives — and I've seen it flagged by Windows Defender as Wacatac or Sabsik purely from that extraction pattern, with completely clean code. --standalone avoids this entirely.
⚠️ Resolving asset paths with __file__ instead of sys.executable
In a normal Python script, __file__ points to your source file's location. In a compiled standalone build, that assumption breaks. Use Path(sys.executable).parent to find where your .exe actually lives on the user's machine, and load config files, icons, and bundled assets relative to that — not relative to where your source happened to sit during development.
What this actually gets you
Both ReelNox Studio and BackDrop_ ship as Nuitka --standalone builds, code-signed with a DigiCert EV certificate. Startup is instant, Windows Defender doesn't flag either installer, and neither app has ever needed a bundled Python runtime the user has to think about. The combination that matters is: --standalone (not --onefile) plus proper code signing — together they solve both the performance problem and the trust problem that come with distributing a Python app as a native Windows executable.
About
Patrick Chen — indie developer behind Sublimearts.io. I write about the exact build problems I actually hit shipping ReelNox Studio, BackDrop_ and TimePeek to real Windows users — not generic tutorials.