This article was published over 2 years ago. Some information may be outdated.
Gunicorn is one of the most popular WSGI HTTP servers for Python web applications.
This post covers how to deploy a Python web application using Gunicorn, Supervisor as a process manager, and NGINX as a reverse proxy.
Prerequisites
You need basic knowledge of Python and basic Linux administration -- installing packages and using a text editor like vim or nano.
The examples here use Ubuntu 18.04, but Gunicorn is available on other Ubuntu versions as well.
Gunicorn requires Python version 2.x >= 2.6 or Python 3.x >= 3.4.
Web Server Gateway Interface
Gunicorn is a Python WSGI HTTP Server. It is compatible with most Python web frameworks, and it is straightforward to set up. But to understand Gunicorn, you first need to understand WSGI.
Python is a general-purpose language. You can build web applications, desktop applications, mobile applications, and more. When you build a web application, you need a web server to handle HTTP requests. The web server itself knows nothing about your application -- it only sends and receives HTTP:

This is fundamentally how HTTP works: your application receives an HTTP request and returns an HTTP response.
Most web servers are written in C/C++ and cannot execute Python code directly. An intermediate layer is needed between your application and the web server. This layer is called an interface, and it defines how the Python application communicates with the web server.
The most well-known interface is CGI (Common Gateway Interface). CGI is not a program -- it is a specification that defines how an application interacts with a web server. It supports all programming languages.
The web server must be configured to work with CGI scripts. Nginx, Apache, IIS -- they all have CGI configuration options.
The problem with CGI is performance. CGI spawns a new process for every request and kills it when the request ends. One process per HTTP request is slow.
FastCGI solved this by allowing a single process to serve multiple requests.
Gunicorn is a Python WSGI HTTP Server for UNIX. It sits between your Python application and your web server as that intermediate layer.
Installing and configuring Gunicorn
Install it via pip:
pip3 install gunicorn
If you are using virtualenv, activate it first:
source YOUR_PROJECT_PATH/bin/activate
pip3 install gunicorn
Run Gunicorn:
gunicorn main:app -b 127.0.0.1:8000 --name="MyApp" --workers=3 --reload --user=USER_RUNNING_THIS_PROCESS
This starts Gunicorn on port 8000. But running it manually is not production-ready. You need automatic restarts on failure and background execution. That is where Supervisor comes in.
Supervisor
Supervisor is a reliable process manager. It handles automatic restarts, background execution, and logging.
Install it:
sudo apt install supervisor
Create a configuration file at /etc/supervisor/conf.d/myapp.conf (replace myapp with your application name):
[program:{myapp}]
directory=/var/www/{myapp}
command=/home/{mtapp}/.local/bin/gunicorn main:app -b 127.0.0.1:8000 --name="{MyApp}" --reload --workers=3 --user={myappuser}
user=myappuser
autostart=true
autorestart=true
stderr_logfile=/var/log/myapp.err.log
stdout_logfile=/var/log/myapp.out.log
Replace all curly-brace placeholders with values specific to your application.
Tell Supervisor to load the new configuration:
sudo supervisorctl reread
sudo supervisorctl update
Your Gunicorn process is now managed by Supervisor.

How many workers?
The recommended formula from the Gunicorn docs is 2n + 1, where n is the number of CPU cores.
Replace --workers=3 in your Supervisor config with:
--workers=$(( 2 * `cat /proc/cpuinfo | grep 'core id' | wc -l` + 1 ))
After changing the configuration, reload Supervisor:
sudo supervisorctl reread
sudo supervisorctl update
NGINX
Gunicorn is a WSGI gateway. It is not designed to face the internet directly. A reverse proxy like NGINX gives you load balancing, static file serving, SSL termination, and better overall performance.
Install NGINX:
sudo apt install nginx -y
Create a configuration file at /etc/nginx/conf.d/myapp.conf:
server {
server_name myapp.example.com
listen 80;
location / {
proxy_pass "http://127.0.0.1:8000";
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
This is a minimal configuration. For gzip, static file caching, SSL, and HTTP/2, see this gist.
Summary
- Gunicorn is a WSGI HTTP server that sits between your Python application and the web server, translating HTTP requests into Python calls.
- WSGI exists because web servers (written in C/C++) cannot run Python directly -- an interface layer is required.
- CGI spawns a new process per request (slow); FastCGI and WSGI solve this by reusing processes.
- Supervisor manages the Gunicorn process -- handling automatic restarts, background execution, and logging.
- Worker count follows the
2n + 1formula, wherenis the number of CPU cores on the server. - NGINX as a reverse proxy adds load balancing, SSL termination, and static file serving on top of Gunicorn.