Servers¶
Note
Deployment arcitectures vary widely depending on the needs and traffic of the site. The setup described below is minimally configured and works well for most instances.
We serve Django on Ubuntu Linux with a PostgreSQL database backend via gunicorn or uWSGI from behind an Nginx frontend proxy. For simplicity, we’ll only be discussing Gunicorn/Nginx here.
Nginx¶
Nginx makes for a great frontend server due to its speed, stability and low resource footprint. The typical Nginx configuration for a site looks like this:
# Gunicorn server
upstream django {
server domain.com:9000;
}
# Redirect all requests on the www subdomain to the root domain
server {
listen 80;
server_name www.domain.com;
rewrite ^/(.*) http://domain.com/$1 permanent;
}
# Serve static files and redirect any other request to Gunicorn
server {
listen 80;
server_name domain.com;
root /var/www/domain.com/;
access_log /var/log/nginx/domain.com.access.log;
error_log /var/log/nginx/domain.com.error.log;
# Check if a file exists at /var/www/domain/ for the incoming request.
# If it doesn't proxy to Gunicorn/Django.
try_files $uri @django;
# Setup named location for Django requests and handle proxy details
location @django {
proxy_pass http://django;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
What Does it Do?
The first block tells Nginx where to find the server hosting our Django site. The second block redirects any request coming in on www.domain.com
to domain.com
so each resource has only one canonical URL. The final section is the one that does all the work. It tells Nginx to check if a file matching the request exists in /var/www/domain.com
. If it does, it serves that file, if it doesn’t, it proxies the request to the Django site.
SSL¶
Another benefit to running a frontend server is SSL termination. Rather than having two Django instances running for SSL and non-SSL access, we can have Nginx act as the gatekeeper redirecting all requests back to a single non-SSL WSGI instance listening on the localhost
. Here’s what that would look like:
server {
listen 67.207.128.83:443; #replace with your own ip address
server_name domain.com;
root /var/www/domain.com/;
access_log /var/log/nginx/domain.com.access.log;
ssl on;
ssl_certificate /etc/nginx/ssl/certs/domain.com.crt;
ssl_certificate_key /etc/nginx/ssl/private/domain.com.key;
ssl_prefer_server_ciphers on;
# Check if a file exists at /var/www/domain/ for the incoming request.
# If it doesn't proxy to Gunicorn/Django.
try_files $uri @django;
# Setup named location for Django requests and handle proxy details
location @django {
proxy_pass http://django;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protocol ssl;
}
}
You can include this code at the bottom of your non-SSL configuration file.
Gunicorn¶
Gunicorn is a lightweight WSGI server that can scale from small deploys to high-traffic sites. You can install it via pip install gunicorn
. Since Nginx will be listening for HTTP(S) requests, you’ll need to bind Gunicorn to a different port. While you’re at it, you can tell it to only respond to the localhost
. A simple gunicorn process might look like this:
$ gunicorn --workers=4 --bind=127.0.0.1:9000 my_project.wsgi:application
This spawns a gunicorn process with 4 workers listening on http://127.0.0.1:9000. If your project doesn’t already have a wsgi.py
file, you’ll want to add one. See the Django WSGI docs or django-layout for an example.
Process Management¶
You want to be sure that gunicorn is always running and that it starts up automatically after a server reboot. If you are deploying to Ubuntu, upstart
is probably the easiest way to get started. Here is a sample config:
# logs to /var/log/upstart/my_project.log
description "my_project"
start on startup
stop on shutdown
respawn
# start from virtualenv path
exec /opt/webapps/my_project/bin/gunicorn -w 4 -b 127.0.0.1:9000 my_project.wsgi:application
setuid www-data
Save this file to /etc/init/gunicorn.conf
and run sudo start gunicorn
. For troubleshooting, your logs will be visible at /var/log/upstart/gunicorn.log
.
Note
Supervisor is a pure Python option if you don’t have access to upstart
.