Django. Running a Project with uWSGI and Nginx

People often ask me how to run a Django project in production. I usually send them a few configs from my own projects and explain how to install and start everything. I started writing this article in 2012, but like many of my other articles, it was left unfinished.
It’s time to finish it so I can point people to an explanation of why and how to run Django under uWSGI.
Why uWSGI?
In short, it’s stable, flexible to configure, and pretty fast. At least in most of the benchmarks I looked at back then, it was faster than its competitors. For example, compared with Gunicorn:
Performance test results are pretty easy to find on Google, so I’ll leave the choice of sources up to you. Or you can write your own tests, and we can run them together :-)
Configuring uWSGI
First, let’s install uWSGI in your virtual environment:
cd ~/<project_name>
source ~/<venv_path>/bin/activate
pip install uWSGI
Let’s pin the version in the dependencies file:
pip freeze|grep -i uwsgi >> requirements.txt
Let’s add a uwsgi/production.ini file to the project (the “uwsgi” directory is needed because I usually have several configs, for example “test.conf” for the test environment):
[uwsgi]
chdir=/home/<username>/<project_name>
pidfile=/home/<username>/<project_name>_uwsgi.pid
socket=/home/<username>/<project_name>_uwsgi.sock
chmod-socket=750
virtualenv=/home/<username>/<venv_path>
pythonpath=.
pythonpath=<project_name>
module=<project_name>.wsgi:application
callable=app
master=true
processes=2
harakiri=30
buffer-size=32768
Let’s go through the options:
chdir- path to the project directory;pidfile- path to the PID file;socket- a Unix socket path or TCP address; I recommend a Unix socket (it’s slightly faster);chmod-socket- socket file permissions; later we’ll add nginx to the “” group belonging to the user “”;virtualenv- path to the environment directory;pythonpath=.- add the current directory from “chdir” to “sys.path”;pythonpath=<project_name>- and the directory containing the Django project code too;module- our wsgi module (included with the Django project)callable- the name of the WSGI callable;master- run in master mode: a separate process will supervise the worker processes;processes- number of worker processes that will handle incoming requests;harakiri- number of seconds after which a hung process will be restarted;buffer-size- maximum request buffer size (headers, such as cookies and the query string); does not include the size of the request body.
You can find even more options in the official documentation
Configuring Nginx
If you haven’t installed Nginx yet, do so:
sudo apt-get install nginx
Let’s create a nginx/production.conf file in the project root with the following contents:
server {
listen 80;
server_name example.org;
location /media/ {
root /home/<username>/<project_name>_storage;
}
location /static/ {
root /home/<username>/<project_name>_storage;
}
# Иногда бывает нужно отдавать статику не из хранилища,
# а прямо из каталога проекта. Но тогда престанет работать админ-панель Django,
# поэтому надо будет добавить вот такой локейшен:
# location /static/admin/ {
# root /home/<username>/<project_name>_storage;
# }
location / {
uwsgi_pass unix:///home/<username>/<project_name>_uwsgi.sock;
include uwsgi_params;
}
}
To let Nginx work with the uWSGI socket file, let’s add it to your project’s group. On Ubuntu, you can do this as follows:
sudo usermod -a -G <usergroup> www-data
Configuring Supervisor
If you don’t have it installed yet, you can do that as follows:
sudo apt-get install supervisor
sudo service supervisor start
I’ve previously written in more detail about working with Supervisor.
Although uWSGI can manage its own worker processes, I prefer to use an external process manager to supervise the service and keep process management consistent.
Let’s create this config, supervisor/production.conf:
[program:<project_name>_uwsgi]
environment=PATH="/home/<username>/<venv_path>/bin"
numprocs=1
directory=/home/<username>/<project_name>
command=/home/<username>/<venv_path>/bin/uwsgi uwsgi/production.ini
user=<username>
autostart=true
autorestart=true
redirect_stderr=true
stopwaitsecs=60
stopsignal=INT
stderr_logfile=/home/<username>/logs/%(program_name)s_err.log
stdout_logfile=/home/<username>/logs/%(program_name)s_out.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=30
stdout_capture_maxbytes=1MB
Starting the Project
Let’s create a directory for logs in the user’s directory:
mkdir -p /home/<username>/logs
Let’s create a symlink to our project’s Supervisor config, then tell Supervisor to update; it will start our uWSGI service according to the config:
sudo ln -s /home/<username>/<project_name>/supervisor/production.conf /etc/supervisor/conf.d/<project_name>.conf
sudo supervisorctl update
sudo supervisorctl status
Then let’s create a symlink to the Nginx config and restart Nginx too:
sudo ln -s /home/<username>/<project_name>/nginx/production.conf /etc/nginx/sites-enable/<project_name>.conf
sudo nginx -t
sudo nginx -s reload
It should work now; ask if anything is unclear.
Monitoring and Runtime Statistics for uWSGI Workers
I’ll talk about “uwsgi-top” a little later. This article is a work in progress.
Comments are closed. Read the original discussion in Russian →
