Make a Windows installer for a Python app (PyInstaller to Setup.exe)
Freeze a Python program with PyInstaller, then wrap the output in a proper Windows installer with shortcuts and an uninstaller. Why onedir beats onefile for installers, and the settings that matter.
Updated October 31, 2026 · 3 min read
PyInstaller turns a Python program into a Windows .exe that runs without Python installed. What it doesn't do is install that program: no Start Menu entry, no uninstaller, nothing in Windows Settings. For that you wrap PyInstaller's output in an installer. Here is the whole path, from script to Setup.exe.
Step 1: freeze the app with PyInstaller
Install PyInstaller in the project's environment, then build from the folder that holds your entry script:
pip install pyinstaller
pyinstaller --noconsole --onedir --name MyApp --icon app.ico main.py
What the flags do:
--onedir(the default) produces a folderdist/MyApp/withMyApp.exeand an_internal/folder of libraries. Use this for installers. It starts faster than--onefile, because nothing has to be unpacked to a temp folder at every launch, and it draws less attention from antivirus heuristics.--noconsolehides the black console window for GUI apps (Tkinter, PyQt, Kivy and so on). Leave it out for command-line tools.--icon app.icoembeds the icon in the.exe. Keep the same.icofor the installer.--name MyAppnames the executable and the output folder.
Data files your program opens at run time (images, config templates, models) must be added with --add-data "src;dest" or in the .spec file, or they won't be in dist/MyApp/.
Open dist/MyApp/MyApp.exe and check the app runs. Copy the whole dist/MyApp folder to another Windows PC without Python if you can, and run it there too.
Step 2: zip the output folder
Zip the contents of dist/MyApp/: MyApp.exe and _internal/. Don't include build/, the .spec file or your source tree. The zip is what gets installed, byte for byte.
Step 3: build the installer
- Upload the zip to ExeTup. It finds
MyApp.exeas the main program. - Enter the app name, version, publisher and an optional licence.
- Choose all users (Program Files, admin prompt) or current user (no admin rights needed). Tools distributed to colleagues often work best per user.
- Choose the architecture. PyInstaller builds for the Python you ran it with, so a 64-bit Python gives a 64-bit app.
- Upload
app.ico, turn on the shortcuts you want, and build. - Download
Setup.exeand test it on Windows.
Python-specific notes
- Antivirus false positives. PyInstaller bundles are a frequent target of overcautious antivirus heuristics, especially
--onefilebuilds. A onedir build inside a normal installer, placed in Program Files, behaves like any other program and is less likely to be flagged. Code signing helps further. - Size. A Tkinter app is 20 to 40 MB; PyQt, NumPy or PyTorch push that far higher. The installer's LZMA compression typically halves it. Exclude modules you don't use with
--exclude-moduleto keep it lean. - First launch. Because onedir needs no unpacking, the first start is as fast as later ones.
- Updates. Ship a new installer with a higher version number. Installing over the previous version replaces the files in place.
- Console tools. For a command-line program, consider a per-machine install and tell users the install folder; the installer does not add it to
PATH.
Alternatives
Nuitka and cx_Freeze also produce a folder with an .exe; the installer step is identical. If you want to write the installer script yourself, NSIS and Inno Setup both work with PyInstaller output.
FAQ
Should I use --onefile or --onedir for an installer?
--onedir. The installer already gives users a single Setup.exe to download, so there is no reason to pay onefile's slow start-up and temp-folder unpacking. Onedir is also the PyInstaller default.
Do users need Python installed?
No. PyInstaller bundles the Python interpreter and the libraries into the folder, and the installer puts that folder on the user's machine.
Why does Windows warn about my installer?
New, unsigned downloads trigger Microsoft SmartScreen until they gain a reputation. See the SmartScreen guide for what you can do.
Can I include a sample config or data folder?
Yes. Either add it with PyInstaller's --add-data, so it lands inside _internal/, or add it in ExeTup's "Other files" step to place it next to MyApp.exe.
Related guides
- How to make a Setup.exe for your Windows app (no code)What a Setup.exe actually does, the three ways to make one, and how to turn an app folder into a professional installer in minutes without writing an installer script.
- Turn an .exe into a Windows installer (exe to Setup.exe)How to convert a plain .exe, or a folder with an .exe in it, into a real Windows installer with shortcuts and an uninstaller, without writing an installer script.
- "Windows protected your PC": what the SmartScreen warning means and how to get rid of itWhy Windows shows the "unknown publisher" warning on a new installer, what users can click, and how code signing and reputation make it go away.