Run Django in Production on Windows With Waitress
Run Django on a Windows server with Waitress: install it in a virtualenv, serve the WSGI app on a port, run it as a Windows service with NSSM, put IIS or Caddy in front for HTTPS and static files, and tune threads and logging.
Problem statement
Your Django app has to run on Windows Server, perhaps next to other internal software or because the organization has no Linux servers. Gunicorn and uWSGI do not run on Windows, and runserver is not for production. Waitress is a pure-Python WSGI server that runs on Windows and Linux alike. Combined with a Windows service wrapper and a reverse proxy for TLS, it gives a supportable setup.
- Create a virtualenv and
pip install waitressalongside your requirements. - Serve the app:
waitress-serve --listen=127.0.0.1:8000 --threads=8 config.wsgi:application. - Run it as a Windows service with NSSM (or WinSW) so it starts at boot and restarts on failure.
- Put IIS (with URL Rewrite and ARR) or Caddy in front for HTTPS; serve static files with WhiteNoise or the proxy.
- Set
SECURE_PROXY_SSL_HEADER,ALLOWED_HOSTS, and pass--trusted-proxyoptions so Django sees the real scheme.
Step-by-step solution
1. Install
In PowerShell:
cd C:\apps\shop
py -3.12 -m venv .venv
.\.venv\Scripts\pip install -r requirements.txt waitress
.\.venv\Scripts\python manage.py collectstatic --noinput
.\.venv\Scripts\python manage.py migrate
Packages such as psycopg and Pillow ship Windows wheels, so installation usually needs no compiler.
2. Start Waitress
.\.venv\Scripts\waitress-serve --listen=127.0.0.1:8000 --threads=8 `
--trusted-proxy=127.0.0.1 --trusted-proxy-headers="x-forwarded-for x-forwarded-proto" `
config.wsgi:application
Waitress uses a single process with a thread pool, not multiple worker processes. Eight threads handle typical I/O-bound Django traffic; for CPU-heavy apps, run several Waitress services on different ports behind the proxy.
3. Run as a Windows service
NSSM (Non-Sucking Service Manager) wraps any executable as a service:
nssm install ShopDjango C:\apps\shop\.venv\Scripts\waitress-serve.exe
nssm set ShopDjango AppParameters "--listen=127.0.0.1:8000 --threads=8 config.wsgi:application"
nssm set ShopDjango AppDirectory C:\apps\shop
nssm set ShopDjango AppEnvironmentExtra DJANGO_SETTINGS_MODULE=config.settings.production
nssm set ShopDjango AppStdout C:\apps\shop\logs\waitress.log
nssm set ShopDjango AppStderr C:\apps\shop\logs\waitress.log
nssm set ShopDjango AppRotateFiles 1
nssm start ShopDjango
Run the service under a dedicated low-privilege account rather than LocalSystem, and give that account read access to the app and write access only to logs and media.
4. Put a proxy in front
With IIS, install URL Rewrite and Application Request Routing, enable proxying in ARR, and add a reverse proxy rule to http://127.0.0.1:8000. Bind the site to HTTPS with your certificate. With Caddy for Windows, a two-line Caddyfile does the same with automatic certificates:
shop.example.com {
reverse_proxy 127.0.0.1:8000
}
In Django:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
ALLOWED_HOSTS = ["shop.example.com"]
5. Static files and logging
WhiteNoise serves collected static files from Django with proper caching headers, which avoids configuring IIS static handlers. Log Django to stdout so NSSM captures it into the rotating log file, or use the Windows Event Log handler if your operations team prefers it.
Explanation
Waitress implements WSGI with an asynchronous I/O loop for connections and a thread pool for application code, so it buffers slow clients itself and works well behind a proxy. Unlike Gunicorn, it does not fork processes, which is why it runs on Windows. One process means the Global Interpreter Lock limits CPU-bound throughput; scaling out is done with more processes on different ports or more servers. NSSM fills the role of systemd, and IIS or Caddy fills the role of Nginx.
Edge cases / notes
- Celery on Windows. Celery's prefork pool does not support Windows; use the
soloorthreadspool, or run workers on Linux. - Line endings and paths. Use
pathlibin settings; hard-coded forward-slash Unix paths fail on Windows. - wfastcgi. The older IIS FastCGI approach with wfastcgi is unmaintained; a reverse proxy to Waitress is easier to support.
Next steps
- If IIS must host the app directly, see Deploy Django on Windows IIS.
- Understand why not to use runserver in Why Not Use runserver in Production.
- Serve static files with Serve Static Files With WhiteNoise.
FAQ
Can Gunicorn run on Windows?
No. Gunicorn relies on Unix features such as fork. Use Waitress on Windows.
How many threads should Waitress use?
Start with 4 to 8 for typical Django apps and adjust based on response times and CPU usage.
How do I run Waitress as a service?
Wrap waitress-serve.exe with NSSM or WinSW and configure automatic start and restart.
Can Celery run on Windows?
Only with the solo or threads pool. For production workers, Linux is more reliable.