Django + Gunicorn + NGINX Deployment

Updated from my previous post

Pain in Django deployment

Django is a python based web framework known as a batteries-included web framework. Development on Django is pretty straight-forward once you understand the MTV style of the web framework design. However, the biggest struggle I experienced was the deployment step. In this blog post, I would like to note down key concepts of Django system, and why Gunicorn and NGINX are used together.

Why use Gunicorn and NGINX together

First, for all Python based web applications, WSGI (Web Server Gateway Interface) is basically used to connect all requests with your python web applications. When you run the Django development server, it is actually based on WSGI server implementation. Gunicorn is one of many other implementation of the WSGI web server. When Gunicorn web server is running, its worker processes handles all the incoming and outgoing requests. If Gunicorn can handle all the requests, why do we even need NGINX?

Before we begin why NGINX is necessary in Django deployment, let’s go through what NGINX is. NGINX is the most well-known robust web server which can effectively handle multiple requests, SSL encryption, load-balancing, reverse-proxy, providing static assets and many more. NGINX can be thought as the first gate-keeper of your server. In Django deployment, NGINX is used for all of the aforementioned features. Since Gunicorn’s sole purpose is to serve an application, static files such as css and javascript files must be served through other medium such as NGINX.

How Django + Gunicorn + NGINX looks like

Like I pointed out earlier, NGINX is the first gate-keeper. NGINX first intercepts all incoming requests. We then port forward all incoming requests to port 80 toward Gunicorn web server by setting a reverse proxy setting. When the forwarded requests get to Gunicorn, worker processes trigger the actual Python logic in our Django application.

When Django application sends a response back to the client, the client browser might ask for static assets from the backend. These static asset requests’ URL would start with /static/ prefix which triggers NGINX to serve the relative static assets from the configured directory.

Django deployment setting

When you are running the Django development server, all static asset serving is done automatically. However, in the deployment stage, you want to configure all static assets to be handled by another web server.

The key concepts in Django deployment is setting up how your static assets’ URL will be formed, and setting up where you would like to collect all the static contents for the deployment stage.

In your,

DEBUG = False

# Static files (CSS, JavaScript, Images)
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static/'),)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticroot/')
  • DEBUG: You would never want leak out stack level debug messages to the clients for security purpose.
  • STATICFILES_DIRS: This is the directory you will be using for static files in the development stage
  • STATIC_URL: This is the URL prefix which will be used for all static contents. NGINX needs to know what this URL would look like, in order to redirect client requests to the static assets directly.
  • STATIC_ROOT: This is the directory where you will tell Django to collect all of the application specific static files in one place. This directory will be configured on NGINX to serve the static files directly from NGINX.

After updating your, run

python collectstatic

This command will populate all your Django application related static assets including the Django admin static files in the directory you provided in STATIC_ROOT.

Now that Django is good to go, how do we set up Gunicorn to be our WSGI web server for Django?

Gunicorn setup

Install Gunicorn through pip. I would recommend you installing it inside your virtual environment.

pip install gunicorn

Locate your file in the Django application directory, and start a Gunicorn server.

gunicorn wsgi:application&

This will automatically run your web server listening at http://localhost:8000. What this command does is, Gunicorn finds our Django application instance inside the file and runs it in detached mode.

At this moment, in your local machine, you can start accessing your Django application at http://localhost.8000. Since it is running in a private ip, no other hosts in the same subnet can access it yet. Also, static assets will not be loading yet.

This is where NGINX comes in.

NGINX setup

When you are configuring NGINX, you need to do two things

  • Port forward all http requests (port 80) to where Gunicorn server is listening to
  • Set all /static/ URL prefix to be directed to static contents directly

You can install NGINX through apt-get.

sudo apt-get install nginx

Go into /etc/nginx/sites-available, modify default file using

sudo vim default

Modify the file.

server {
  listen 80;
  server_name localhost;
  location / {

  location /static/ {
    alias /home/pi/Projects/home_automation_project/home_automation/staticroot/;

What this configuration tells you is that NGINX will be listening to port 80(http request port) and pass all the traffic to which Gunicorn is listening to.

The file directory set in /static/ alias should be the absolute path of the directory you configured in Django’s STATIC_ROOT variable.

After saving, run

sudo service nginx restart


For further robust deployment

This blog post only covered how Django can be served to the client and how its static contents can be served at the deployment stage. SSL encryption would be the next thing to consider to mask the content of the requests/responses in the course of routing from the front-end to the back-end and vice versa. Another thing would be automatic NGINX and Gunicorn process start after a reboot event.

Alarm Clock Project Summary

Why I started this project

Last November in 2015, I started an IoT project which simulates a sunrise event at a scheduled time. I really wanted a practical project which I can use in my daily life to enhance the quality of my life.

While researching for viable solutions, Philips Hue smart LED bulb, LIFX LED bulb, and various choices were found. I did not choose either of these choices for few reasons. To use a Philips smart bulbs, you require a central hub which allows you to connect to it for controlling all the smart bulbs. LIFX has integrated wi-fi module built into the bulb. Both choices were too expensive to start with, and I didn’t want something that is already built out of the box. I wanted more hands on action which would give me a full control of my soon-to-be sunrise alarm.


After few weeks of researching, I decided to use a Raspberry Pi as my web-server which controls a connected RGB LED light strip. Following technology were used for this project

  • Raspberry Pi: ARM based linux computer
  • Adafruit RGB LED strip: controllable via GPIO on Raspberry Pi
  • NGINX: web server
  • Gunicorn: WSGI proxy web server
  • Django: alarm web application
  • MySQL: record keeper
  • Bootstrap: UI design

The github repository for this code is provided here.


This youtube clip shows a sped-up version of the sunrise simulation for the demo purpose.

A simple authentication page is present to limit other users from triggering or scheduling my alarm.


Currently, it shows past scheduling history for a development purpose.


Simple alarm configuration page is displayed.



To be added

Even though this IoT is still usable, I would like to add few more features into the application.

  • For the user to configure multiple alarms.
  • Display only active alarms on the status page.
  • Allow the user to delete/deactivate selected alarm.
  • NGINX and Gunicorn to auto-start at an event of a crash.
  • Web scrap newsfeed and voice it out over a speaker when the alarm triggers.