How to Run Django as a Windows Service with Waitress and PyWin32

Setting up a Django project to run as a Windows service can help ensure that your application stays online and automatically restarts after system reboots. This guide walks you through setting up Django as a Windows service using Waitress (a production-ready WSGI server) and PyWin32 for managing the service. We'll also cover common problems, like making sure the service starts and stops correctly.

django

The Plan#

We'll be doing the following:

  1. Set up Django to run as a Windows service using PyWin32.
  2. Use Waitress to serve the Django application.
  3. Handle service start/stop gracefully.
  4. Troubleshoot common issues that can pop up.

Step 1: Install What You Need#

You'll need to install Django, Waitress, and PyWin32. Run these commands to install the necessary packages:

pip install django waitress pywin32

After installing PyWin32, run the following command to finish the installation:

python -m pywin32_postinstall

This step ensures the necessary Windows files for PyWin32 are in place.


Step 2: Write the Python Service Script#

To create the Windows service, we’ll write a Python script that sets up the service and runs the Django app through Waitress.

Create a file named django_service.py in your Django project directory (where manage.py is located), and paste the following code:

import os
import sys
import win32service
import win32serviceutil
import win32event
from waitress import serve
from django.core.wsgi import get_wsgi_application
import logging
# Set up logging for debugging
logging.basicConfig(
filename='C:\\path\\to\\logs\\django_service.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
class DjangoService(win32serviceutil.ServiceFramework):
_svc_name_ = "DjangoWebService"
_svc_display_name_ = "Django Web Service"
_svc_description_ = "A Windows service running a Django web server using Waitress."
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.running = True
logging.info("Initializing Django service...")
try:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project_name.settings')
self.application = get_wsgi_application()
except Exception as e:
logging.error(f"Error initializing WSGI application: {e}")
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
self.running = False
logging.info("Stopping Django service...")
def SvcDoRun(self):
logging.info("Service is running...")
serve(self.application, host='0.0.0.0', port=8000)
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(DjangoService)
What’s Happening in the Script:#
  • Logging: We set up logging to help debug issues. All logs go to django_service.log.
  • WSGI Application: Django’s get_wsgi_application() is used to initialize the app.
  • Waitress: We serve Django using Waitress, which is a good production-ready server.
  • Service Methods:
    • SvcStop(): Handles stopping the service gracefully.
    • SvcDoRun(): Runs the Waitress server.

Step 3: Install the Service#

Once the script is ready, you need to install it as a Windows service. Run this command in the directory where your django_service.py is located:

python django_service.py install

This registers your Django application as a Windows service.

Note:#

Make sure to run this command as an administrator. Services need elevated privileges to install properly.


Step 4: Start the Service#

Now that the service is installed, you can start it by running:

python django_service.py start

Alternatively, you can go to the Windows Services panel (services.msc), find "Django Web Service," and start it from there.


Step 5: Troubleshooting Common Errors#

Error 1066: Service-Specific Error#

This error usually happens when something crashes during the service startup. To fix it:

  • Check Logs: Look at django_service.log for any errors.
  • Check Django Config: Make sure that DJANGO_SETTINGS_MODULE is set correctly.
Error 1053: Service Did Not Respond in Time#

This happens when the service takes too long to start. You can try:

  • Optimizing Django Startup: Check if your app takes too long to start (e.g., database connections).
  • Check Waitress Config: Ensure that the server is set up properly.
Logs Not Generated#

If logs aren’t showing up:

  • Ensure the directory C:\\path\\to\\logs exists.
  • Make sure the service has permission to write to that directory.
  • Double-check that logging is set up before the service starts.

Step 6: Stopping the Service Gracefully#

Stopping services cleanly is essential to avoid crashes or stuck processes. In the SvcStop method, we signal the server to stop by setting self.running = False.

If this still doesn’t stop the service cleanly, you can add os._exit(0) to force an exit, but this should be a last resort. Try to allow the application to shut down properly if possible.


Step 7: Configuring Allowed Hosts in Django#

Before you go live, ensure that the ALLOWED_HOSTS setting in settings.py is configured properly. It should include the domain or IP of your server:

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'your-domain.com']

This ensures Django only accepts requests from specified hosts, which is crucial for security.


Step 8: Package it with PyInstaller (Optional)#

If you want to package everything into a single executable, you can use PyInstaller. Here’s how:

First, install PyInstaller:

pip install pyinstaller

Then, create the executable:

pyinstaller --onefile django_service.py

This will create a standalone executable in the dist folder that you can deploy to other Windows machines.


Conclusion#

By following this guide, you’ve successfully set up Django to run as a Windows service using Waitress and PyWin32. You’ve also learned how to:

  1. Install and run the service.
  2. Debug common service-related errors.
  3. Ensure a clean shutdown for the service.
  4. Configure Django’s ALLOWED_HOSTS for production.

With this setup, your Django app will run efficiently as a background service, ensuring it stays available even after reboots.

For more information on the topics covered in this blog, check out these resources: