Introduction
WSGI is the standard for running Python web applications. Gunicorn and uWSGI are great options for running WSGI apps, but they only have limited support in Windows or no support at all. Waitress is another option for running WSGI applications and it really shines in Windows because it is written purely in Python with no other dependencies. I will show you how to use Waitress to serve web application using Python WSGI.
Selling points for Waitress:
- Production quality
- Well documented
- Easy to install
- Portable, with no dependencies except Python standard library so it runs in Windows
- Part of the trusted Pylons project
- Licensed for Freedom with Zope Public License
Setup
You will need Python installed. I recommend using a virtual environment. If you don't know how to create and use Python virtual environments, check out my tutorial Python Virtual Environments Tutorial.
You will need to install the Waitress package for Python which is easily done with pip
:
python -m pip install waitress
If you want to install manually, get the source code from https://github.com/Pylons/waitress and run setup.py
from the root directory:
python setup.py install
Running
You have two primary options for running Waitress. You can use the command-line tool waitress-serve
or you can import Waitress in to a Python script and run it from there. They are essentially the
same and you can pick the option that works best for your project.
Full usage information can be found in the official documentation but I will demonstrate the most common example from my experience.
Waitress supports using a UNIX sockets, although the examples here use TCP.
Command line
After installing the Waitress package, it will create a command-line sript named waitress-serve
.
The full options for running can be reviewed if you run waitress-serve
with no options.
# Print usage
waitress-serve
Waitress is packaged well and also has a __main__.py
in the source code, so you
can also execute the same CLI tool using python3 -m waitress
like this:
# Equivalent to `waitress-serve`
python3 -m waitress
# Or from a specific virtual environment
/path/to/venv/bin/python -m waitress
In this next example, it pretends
there is a file named my_wsgi_project.py
with an object named app
like
a typical Flask web application. The URL prefix is optional, but is useful
when you are serving the application on a sub-directory like behind a reverse-proxy.
It will listen on localhost port 8001.
waitress-serve --url-prefix=/my-app --listen=127.0.0.1:8001 my_wsgi_project:app
In Python
This example will change the current working directory to the directory
that the Python script resides in. In this example, it pretends
there is a file named my_wsgi_project.py
with an object named app
like
a typical Flask web application.
It will serve on IPv4 for localhost only on port 8001.
# run.py
import os
from waitress import serve
from my_wsgi_project import app # Import your app
# Run from the same directory as this script
this_files_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(this_files_dir)
# `url_prefix` is optional, but useful if you are serving app on a sub-dir
# behind a reverse-proxy.
serve(app, host='127.0.0.1', port=8001, url_prefix='/my-app')
As a Service
If you want to run your application so it runs when the computer restarts and always stays up, you need to create a service.
Linux Service
For Linux users, check out my tutorial Creating Systemd Service Files.
Here is a simple example:
# /etc/systemd/system/my_app.service
[Unit]
Description=My WSGI app
After=network.target
[Service]
Type=simple
User=nanodano
WorkingDirectory=/home/nanodano
ExecStart=/path/to/venv/bin/waitress-serve --listen=127.0.0.1:8001 app.wsgi:application
Restart=always
[Install]
WantedBy=multi-user.target
Windows Service
For Windows users, check out my tutorial Run Python Script as Windows Service.
Here is a simple example of registering your app as a Windows service using nssm.
nssm.exe install "MyCustomService" "C:\opt\venv\Scripts\python.exe" "C:\opt\run.py"
Behind Nginx reverse-proxy
Frequently you need to serve multiple web applications on a single server using the same port. The solution is to use a reverse-proxy like Nginx. This allows you to create virtual hosts, serve static files, and serve multiple WSGI applications at once.
To learn how to setup Nginx as a reverse-proxy to serve WSGI applications and handle other things like redirection, SSL, authentication, and more, check out my Nginx Tutorial.
Here is a simple example of a reverse proxy with Nginx using SSL:
http {
server {
listen 443 ssl;
ssl_certificate /path/to/cert.pem
ssl_certificate_key /path/to/private-key.pem
ssl_ciphers HIGH:!aNULL:!MD5;
server_name example.com;
location /static/ {
alias /var/www/static-content/;
}
location / {
proxy_pass http://localhost:9999/;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
Conclusion
After reading this you should understand how to run a Python WSGI application using Waitress and when Waitress would be a good option over Gunicorn or uWSGI. You should understand how to do it from within Python and from the command-line.