django-binary-builder
Build Django projects as installable desktop applications for Windows.
django-binary-builder turns any Django project into a standalone
Setup.exe in one command. The packaged application ships a
portable Python runtime with your project and every dependency
installed by pip — no code is frozen, so any library that works in
your development virtual environment (including native/C-extension
packages) works in the packaged application. Size is traded for
compatibility on purpose.
When the user starts the installed app, Django is served by waitress on a loopback port in the background and the application opens in a native desktop window (via pywebview); closing the window shuts the server down and exits the process. If pywebview is unavailable, the default browser is used as a fallback.
Quick start
1. Check the prerequisites
- Python 3.10+ — any full CPython build (the python.org
installer,
uv, ...). The Microsoft Store Python cannot be packaged. - Django 5.0 or newer project with
WSGI_APPLICATIONconfigured (thestartprojectdefault). - Windows 10 or 11 — installers must be built on a Windows host.
- Inno Setup 7 — only
needed for the final
Setup.exe; skip it with--skip-installerwhile iterating. - Internet access on the build machine the first time dependencies are installed into the runtime (pip uses its normal cache afterwards).
Always build from the virtual environment you develop in: it defines both the Python runtime that gets copied and the fallback dependency list.
2. Install and register the app
Use whichever workflow you already use for the project — plain pip
or uv. Both install the library into the project's virtual
environment.
With pip (run inside your project's virtual environment):
pip install django-binary-builder
With uv:
uv add django-binary-builder
Then register the app:
# settings.py
INSTALLED_APPS = [
# ... your apps
"django_binary_builder",
]
3. (Optional) add the five-key setting
Every key is optional — with no setting at all, the project folder name becomes the application name:
# settings.py
from pathlib import Path
DJANGO_BINARY_BUILDER = {
"NAME": "Example Project",
"VERSION": "0.1.1",
"PUBLISHER": "Example Company",
"EXECUTABLE_NAME": "example-project",
"ICON": BASE_DIR / "assets" / "icon.ico",
}
4. Build
Run the build from the same (activated) virtual environment you develop in.
With pip (virtual environment activated):
python manage.py binary windows
With uv:
uv run python manage.py binary windows
The installer is written to:
release/windows/<executable-name>-<version>-Setup.exe
Users install it per-user (no admin rights needed), get a desktop and start-menu shortcut, and your app runs completely offline.
CLI
python manage.py binary PLATFORM [OPTIONS]
PLATFORM accepts windows (implemented), or linux / macos
(reserved, not implemented yet). uv users prefix the command with
uv run (for example uv run python manage.py binary windows) so
it executes inside the project environment.
| Option | Behaviour |
|---|---|
--check |
Run preflight checks only; create no files. |
--skip-installer |
Build the runnable application bundle without requiring Inno Setup. |
--output PATH |
Override the output directory (default:release/). |
The complete configuration
DJANGO_BINARY_BUILDER is the entire configuration surface — five
optional keys:
| Key | Meaning | Default when omitted |
|---|---|---|
NAME |
Display name (window title, installer name, start menu) | the project folder name |
VERSION |
Application version | "0.1.0" |
PUBLISHER |
Publisher name (installer metadata, data directory) | NAME |
EXECUTABLE_NAME |
Safe name for the.exe file |
NAME, converted to a safe file name |
ICON |
Application icon, must be an.ico file |
no custom icon |
Unknown keys are ignored with a warning, so typos surface without blocking builds.
How the build works
Read the 5-key DJANGO_BINARY_BUILDER setting
→ Bundle .env as-is (the process environment wins at runtime)
→ Resolve dependencies: requirements.txt → pyproject.toml → pip freeze
→ Resolve the Python pin (.python-version)
→ Copy the current CPython installation into bundle/runtime
→ pip install every dependency into the portable runtime
→ Copy the project + the library, collect static files, generate the launcher
→ Build a dependency-free entry executable (PyInstaller stub)
→ Package everything with Inno Setup into a Setup.exe
The entry executable is an intentionally dependency-free PyInstaller
stub: its only job is to start runtime\pythonw.exe app\launcher.py
and forward the exit code. Your Django code is never frozen — that
is what makes any pip-installable library work out of the box.
Dependency resolution
- If
requirements.txtexists in the project root, it is used as-is. - Otherwise, if
pyproject.tomlexists, its[project] dependenciesare used. - Otherwise
requirements.txtis generated from the current environment (the equivalent ofpip freeze, withdjango-binary-builder,pip,setuptoolsandwheelexcluded) and written into the project root.
The launcher's runtime dependencies (waitress, pywebview,
python-dotenv) are appended automatically when missing.
.python-version
The build generates .python-version in the project root
containing the current interpreter's version (for example 3.14).
If the file already exists it is used as-is, and the build fails
when the current interpreter does not match it — activate the right
virtual environment to build reproducible bundles.
Bundle layout
<install dir> %LOCALAPPDATA%\Programs\Example Project
├── example-project.exe entry stub (starts the runtime)
├── _internal/ PyInstaller support files for the stub
├── runtime/ complete portable CPython + site-packages
└── app/ your project
├── manage.py, myproject/, ...
├── django_binary_builder/ the library itself (bundled import)
├── staticfiles/ collected by the build
├── .env bundled when present
├── db.sqlite3 bundled when present (first-run seed)
└── launcher.py generated entry point
Runtime behaviour
- Server: waitress serves the WSGI application on
127.0.0.1(first free port from 8765) in a background thread. - Window: a 1200×800 pywebview window titled
NAME; the default browser is the fallback. Closing the window exits the app. - Environment: the bundled
.envis applied at startup withpython-dotenv; variables already present in the process environment always win, so deployments can override anything — includingDJANGO_SETTINGS_MODULE. - Database:
-
SQLite databases are relocated on first start to a writable per-user data directory, seeded from the bundled copy and never overwritten on upgrades:
%LOCALAPPDATA%/<Publisher>/<Name>/ (names sanitized: spaces → dashes) ├── data/db.sqlite3 ├── logs/application.log ├── logs/startup.log └── media/
Override the root with the
DJANGO_BINARY_DATA_DIRenvironment variable. Deletedb.sqlite3from the project before building to ship an empty database. -
External databases (PostgreSQL, MySQL, ...) keep the project's
DATABASESsettings — install the driver in your build environment (for examplepip install "psycopg[binary]"oruv add "psycopg[binary]") so it lands in the packaged runtime.
-
- Migrations run automatically on every startup.
- Initial admin: when
django.contrib.authis installed and no superuser exists, one is created with usernameadminand the password fromDJANGO_BINARY_ADMIN_PASSWORD(defaultadmin1234;DJANGO_BINARY_ADMIN_USERNAMEoverrides the username). Change it immediately after the first login. - Static files:
/static/is served from the bundledstaticfiles/directory — no web server or whitenoise configuration needed. - Media files: served at the
MEDIA_URLprefix when it is a real sub-path (for example/media/). Django 6.1 defaultsMEDIA_URLto/, which is deliberately not mounted because it would shadow every route — setMEDIA_URL = "/media/"to enable media serving. 127.0.0.1andlocalhostare appended toALLOWED_HOSTSautomatically.
Security notes
- A bundled
.envor SQLite database can be extracted by anyone with file access — never package real secrets. Provide sensitive values through the environment on the deployment machine instead. - The initial administrator uses a publicly documented default password; it is only a convenience for first-run setups.
Troubleshooting
pins Python 3.x but the current interpreter is ...— the.python-versionfile records the build Python; activate a matching virtual environment (or update the file) and rebuild.pip install failed inside the portable runtime— check internet access and the dependency specifiers; pip's output is printed during the build.The Microsoft Store Python cannot be packaged— install Python from python.org or viauvand rebuild from its virtual environment.Inno Setup 7 was not found— install Inno Setup 7, addISCC.exetoPATH, set theDJANGO_BINARY_INNO_COMPILERenvironment variable, or build with--skip-installer.- The installed app does not start — check
%LOCALAPPDATA%\<Publisher>\<Name>\logs\application.logandstartup.log; all output goes there because the packaged app has no console. - Port 8765 is busy — the launcher automatically picks the next available port.
Known limitations
- Windows only (
linux/macostargets are reserved but not implemented); no cross-compilation. - WSGI only — no ASGI, Django Channels or WebSocket support.
- No Celery worker/beat, no bundled or auto-installed database servers.
- No Windows Service support, no automatic updater, no code signing.
- The bundle is large (hundreds of MB) by design: the complete Python runtime is shipped inside the installer.
Example
See the examples directory for a complete project
demonstrating the five-key configuration, a custom icon, a bundled
SQLite database and a requirements.txt.
Release files for django-binary-builder 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_binary_builder-0.3.3.tar.gz | 25.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_binary_builder-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:59.9 kB
Release files / django_binary_builder-0.3.3.tar.gz
| Download URL | django_binary_builder-0.3.3.tar.gz |
|---|---|
| Size | 25.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8bf66415240e28ccc9052e5e80f27a69feca0673d0cb2babc098c545384f8b1b
|
|
BLAKE2b-256 checksum How to use checksums |
df28004a16cccefe6df8d1842324ee653f9fd0bfa752ee5aa6806a76b0f6d48d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / django_binary_builder-0.3.3-py3-none-any.whl
| Download URL | django_binary_builder-0.3.3-py3-none-any.whl |
|---|---|
| Size | 34.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
00109b28423a40190b279323692e0c3aca233b3ff70734c842d1580e34c9a32b
|
|
BLAKE2b-256 checksum How to use checksums |
be167f3d9c0f3dc03f0629469708562d8c9ee747aedf1e43c06fa92e22b4c205
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|