A comprehensive guide covering multiple methods and tools for converting Python scripts into standalone executable files, including PyInstaller, cx_Freeze, Nuitka, and auto-py-to-exe.
- Quick Start Guide
- Tool Comparison
- Method 1: PyInstaller (Recommended for Beginners)
- Method 2: auto-py-to-exe (GUI Approach)
- Method 3: cx_Freeze (Fast Startup)
- Method 4: Nuitka (Performance & Security)
- Troubleshooting
- Best Practices
- Advanced Configuration
TL;DR: For most users, PyInstaller is the easiest option:
pip install pyinstaller
pyinstaller --onefile your_script.py| Tool | Best For | Pros | Cons | Difficulty |
|---|---|---|---|---|
| PyInstaller | General use, beginners | Easy to use, great compatibility | Larger files, slower startup | โญ Easy |
| auto-py-to-exe | GUI lovers | Visual interface, PyInstaller backend | Same as PyInstaller | โญ Easy |
| cx_Freeze | GUI apps, fast startup | Faster load times, native installers | Larger files, more setup | โญโญ Medium |
| Nuitka | Commercial, performance | Fastest execution, better security | Complex setup, long compile time | โญโญโญ Hard |
PyInstaller is the most popular and user-friendly tool for converting Python scripts to executables.
# Create project directory
mkdir my_python_project
cd my_python_project
# Copy your Python script here
# your_script.py# Windows
python -m venv venv
venv\Scripts\activate
# macOS/Linux
python -m venv venv
source venv/bin/activate# Install your project dependencies
pip install -r requirements.txt
# Install PyInstaller
pip install pyinstallerpyinstaller your_script.pypyinstaller --onefile your_script.pypyinstaller --onefile --windowed your_script.pypyinstaller --onefile --windowed --icon=app.ico your_script.py| Option | Description | Example |
|---|---|---|
--onefile |
Create single executable | pyinstaller --onefile script.py |
--onedir |
Create directory with files (default) | pyinstaller --onedir script.py |
--windowed / -w |
Hide console window | pyinstaller -w script.py |
--icon=file.ico |
Add custom icon | pyinstaller --icon=app.ico script.py |
--add-data "src;dest" |
Include data files | pyinstaller --add-data "data.txt;." script.py |
--hidden-import module |
Include hidden imports | pyinstaller --hidden-import requests script.py |
--log-level=DEBUG |
Verbose output for debugging | pyinstaller --log-level=DEBUG script.py |
A GUI wrapper for PyInstaller that makes the process visual and beginner-friendly.
pip install auto-py-to-exe
auto-py-to-exe- Script Location: Browse and select your
.pyfile - Onefile: Choose "One File" for single executable
- Console Window: Select "Window Based (hide the console)" for GUI apps
- Icon: Optional - add a custom
.icofile - Additional Files: Add any data files your script needs
- Advanced: Configure hidden imports if needed
- Convert: Click "CONVERT .PY TO .EXE"
Best for applications requiring fast startup times and native installers.
pip install cx_freezeCreate a setup.py file:
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it's better to be explicit
build_options = {
'packages': [],
'excludes': [],
'include_files': [] # Add data files here
}
executables = [
Executable('your_script.py', base='Win32GUI') # Use 'Win32GUI' for GUI apps
]
setup(
name='YourApp',
version='1.0',
description='Your application description',
options={'build_exe': build_options},
executables=executables
)# Build executable
python setup.py build
# Create Windows installer
python setup.py bdist_msi
# Create Mac disk image
python setup.py bdist_dmgFor maximum performance and security. Compiles Python to C++ then to native machine code.
Windows: Install MinGW64 or Visual Studio macOS: Install Xcode command line tools Linux: Install GCC
# Install Nuitka
pip install nuitka# Compile to executable
python -m nuitka --onefile your_script.py
# Standalone with all dependencies
python -m nuitka --standalone your_script.py
# GUI application (no console)
python -m nuitka --onefile --windows-disable-console your_script.py
# With icon
python -m nuitka --onefile --windows-icon-from-ico=app.ico your_script.py# Enable all optimizations
python -m nuitka --onefile --enable-plugin=anti-bloat your_script.py
# For NumPy/SciPy applications
python -m nuitka --onefile --enable-plugin=numpy your_script.py# Add hidden imports
pyinstaller --hidden-import=module_name your_script.py
# Or create a hook file for complex cases# PyInstaller
pyinstaller --add-data "data_folder;data_folder" your_script.py
# In your Python code, use this to find bundled files:
import sys
import os
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)# Use UPX compression (optional)
pip install upx-ucl # or download UPX manually
pyinstaller --onefile --upx-dir=/path/to/upx your_script.py- Add exclusions to your antivirus
- Use code signing certificates for distribution
- Consider using Nuitka for better security reputation
- Test in --onedir mode first for easier debugging
- Run executable from command line to see error messages
- Use --log-level=DEBUG for verbose output
- Check the build/warn-*.txt files for warnings
- Use virtual environments to avoid bloated executables
- Test on clean systems without Python installed
- Sign your executables for professional distribution
- Include version information in your builds
-
Choose the right tool based on your needs:
- PyInstaller: General use, easiest
- cx_Freeze: Fast startup, native installers
- Nuitka: Performance, security, commercial apps
-
Minimize dependencies to reduce file size
-
Use --onedir for development, --onefile for distribution
-
Profile your application to identify bottlenecks
# Example professional PyInstaller command
pyinstaller --onefile \
--windowed \
--icon=app.ico \
--add-data "assets;assets" \
--version-file=version.txt \
--name="MyApplication" \
your_script.pyFor complex projects, use spec files for reproducible builds:
# myapp.spec
a = Analysis(['your_script.py'],
pathex=[],
binaries=[],
datas=[('assets', 'assets')],
hiddenimports=['hidden_module'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='MyApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
icon='app.ico')Build with: pyinstaller myapp.spec
- Windows .exe โ Build on Windows
- macOS .app โ Build on macOS
- Linux binary โ Build on Linux
Consider using CI/CD services like GitHub Actions for automated multi-platform builds.
| Metric | PyInstaller | cx_Freeze | Nuitka |
|---|---|---|---|
| Build Time | โก Fast | ๐ Slower | ๐๐ Slowest |
| Startup Time | ๐ Slow (onefile) | โก Fast | โกโก Fastest |
| File Size | ๐ฆ Small | ๐ฆ๐ฆ Larger | ๐ฆ๐ฆ๐ฆ Largest |
| Security | ๐ Low | ๐ Low | ๐๐๐ High |
| Ease of Use | โญโญโญ Easy | โญโญ Medium | โญ Hard |
- โ You're new to Python packaging
- โ You need quick results
- โ Your app doesn't need maximum performance
- โ You want the most community support
- โ Fast startup time is critical
- โ You need native installers (.msi, .dmg)
- โ You're comfortable with setup.py scripts
- โ You're building GUI applications
- โ Performance is critical
- โ You need source code protection
- โ You're building commercial software
- โ You can handle complex build processes
- โ You prefer GUI tools over command line
- โ You want PyInstaller's power with visual interface
- โ You're learning and want to see all options
# hello.py
import requests
import json
def main():
response = requests.get('https://api.github.com/users/octocat')
data = response.json()
print(f"Hello {data['name']}!")
if __name__ == "__main__":
main()Build commands:
# PyInstaller
pyinstaller --onefile hello.py
# cx_Freeze (create setup.py first)
python setup.py build
# Nuitka
python -m nuitka --onefile hello.py# gui_app.py
import tkinter as tk
from tkinter import messagebox
class App:
def __init__(self, root):
self.root = root
self.root.title("My App")
button = tk.Button(root, text="Click Me!", command=self.show_message)
button.pack(pady=20)
def show_message(self):
messagebox.showinfo("Hello", "Hello World!")
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.mainloop()Build commands:
# PyInstaller (GUI - no console)
pyinstaller --onefile --windowed --icon=app.ico gui_app.py
# Nuitka (GUI)
python -m nuitka --onefile --windows-disable-console --windows-icon-from-ico=app.ico gui_app.py# โ Wrong - hardcoded paths won't work
with open('data.txt', 'r') as f:
content = f.read()
# โ
Correct - use resource_path function
import sys
import os
def resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller"""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# Use it like this:
with open(resource_path('data.txt'), 'r') as f:
content = f.read()# If you get "ModuleNotFoundError" in the executable:
# Method 1: Hidden imports
pyinstaller --hidden-import=missing_module your_script.py
# Method 2: Create a hook file
# Create: PyInstaller/hooks/hook-mymodule.py
hiddenimports = ['missing_module1', 'missing_module2']# Reduce size with these techniques:
# 1. Use virtual environment (most important!)
python -m venv clean_env
clean_env\Scripts\activate
pip install only_needed_packages
# 2. Exclude unnecessary modules
pyinstaller --exclude-module matplotlib --exclude-module numpy your_script.py
# 3. Use UPX compression
pyinstaller --onefile --upx-dir=C:\upx your_script.py# .github/workflows/build.yml
name: Build Executables
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build executable
run: |
pyinstaller --onefile --name myapp-${{ matrix.os }} your_script.py
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: executables
path: dist/- UPX: Executable compression
- Resource Hacker: Windows executable editing
- PyInstaller Extractor: Reverse engineering tool
- Dependency Walker: Analyze DLL dependencies
A: No, you need to build on each target platform. Use CI/CD or virtual machines.
A: It includes the Python interpreter and all dependencies. Use virtual environments and exclude unnecessary modules.
A: Use --add-data flag and the resource_path() function shown above.
A: PyInstaller/cx_Freeze offer minimal protection. Use Nuitka for better security, or consider code obfuscation tools.
A: This is common with packed executables. Submit to antivirus vendors, use code signing, or try Nuitka.
This project is licensed under the MIT License - see the LICENSE file for details.
- PyInstaller Team - For the most popular Python packaging tool
- cx_Freeze Developers - For fast startup and native installers
- Nuitka Project - For true Python compilation
- auto-py-to-exe - For making PyInstaller accessible to everyone
- CodersLegacy - For educational content and tutorials
- Python Packaging Authority - For packaging standards and tools
Found an issue or want to improve this guide?
- Fork the repository
- Create a feature branch
- Make your improvements
- Submit a pull request
Areas where contributions are welcome:
- Additional troubleshooting scenarios
- More example projects
- Platform-specific tips
- Performance optimization techniques
- Security best practices
โญ If this guide helped you, please star the repository!