Back to Blog Home

How to Deploy Django App To Linux using Nginx, Gunicorn, Supervisor, PostgreSQL

Jay Mishra| Django | 9 months, 1 week


1. Update the existing packages on the server 

$ sudo apt-get update
$ sudo apt-get upgrade


2. Install PostgreSQL and required dependencies 

$ sudo apt-get install postgresql postgresql-contrib


3. Now lets create a database for our Django app and a database user. We need to login as a postgres user to do so.

sudo su - postgres


4. You are logged in as Postgres user and now we will create a database user and assign him required privileges

postgres@ubuntu:~$ createuser --interactive -P
Enter name of role to add: db_user
Enter password for new role:
Enter it again:
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n


5. Now we will create a database with the name django_db with user as db_user

postgres@ubuntu:~$ createdb --owner db_user django_db


6. Let’s logout as postgres user now

postgres@ubuntu:~$ logout


7. Lets install the virtualenv package

$ sudo apt-get install python-virtualenv


8. Create a virtual environment with python installed in it

$ virtualenv -p python3 django_env
New python executable in django_env/bin/python
Installing distribute..............done.
Installing pip.....................done.


9. Activate the virtual environment

$ source django_env/bin/activate
(django_env) $


10. Install django in the virtual environment

(django_env) $ pip install django
Downloading/unpacking django
Installing collected packages: django
Successfully installed django
Cleaning up...


11. Create a sample project by the name sample_project

(django_env) $ startproject sample_project


12. Cd into sample_project and run the server to see if everything is working fine

(django_env) $ cd sample_project 


(django_env) $ python runserver
Validating models...
0 errors found
Django version 1.10.1, using settings 'hello.settings'
Jan 09, 2017 - 06:12:00
Development server is running at
Quit the server with CONTROL-C.


13. We need a psycopg2 database adapter which simply helps our postgres database to talk to our django app. Install dependencies of psychopg2

(django_env) $ sudo apt-get install libpq-dev python3-dev


14. Install psychopg2

(django_env) $ pip install psycopg2


15. Configure your Django app’s as follow

    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'django_db',
        'USER': 'db_user',
        'PASSWORD': '<password you entered when creating db_user>',
        'HOST': 'localhost',
        'PORT': '',                      # Set to empty string for default.


16. Add server’s IP or domain name in’s allowed hosts

ALLOWED_HOSTS = ['<your server's IP or domain name>']


17. Sync the db

(django_env) $ python migrate


18. Now we’ll install Gunicorn which replaces django’s single threaded development server

(django_env) $ pip install gunicorn
Downloading/unpacking gunicorn
  Downloading gunicorn-0.17.4.tar.gz (372Kb): 372Kb downloaded
  Running egg_info for package gunicorn
Installing collected packages: gunicorn
  Running install for gunicorn
    Installing gunicorn_paster script to /webapps/hello_django/bin
    Installing gunicorn script to /webapps/hello_django/bin
Successfully installed gunicorn
    Installing gunicorn_django script to /webapps/hello_django/bin
Cleaning up...


19. Test if gunicorn is running

(django_env) $ gunicorn sample_project.wsgi:application --bind


20. Currently gunicorn is running from server’s IP at port 8001. We need to configure it properly for production. Let’s create a bash script

(django_env) $ vim gunicorn_start.bash


21. Add following stuff in the bash file

NAME="django_app"                                   # Name of the application
DJANGODIR=/home/ubuntu/sample_project               # Django project directory
SOCKFILE=/home/ubuntu/django_env/run/gunicorn.sock  # we will communicte using this unix socket
USER=ubuntu                                         # the user to run as
GROUP=ubuntu                                        # the group to run as
NUM_WORKERS=3                                       # how many worker processes should Gunicorn spawn
DJANGO_SETTINGS_MODULE=sample_project.settings      # which settings file should Django use
DJANGO_WSGI_MODULE=sample_project.wsgi              # WSGI module name
echo "Starting $NAME as `whoami`"
# Activate the virtual environment
source /home/ubuntu/django_env/bin/activate
# Create the run directory if it doesn't exist
test -d $RUNDIR || mkdir -p $RUNDIR
# Start your Django Unicorn
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE \
  --log-level=debug \


22. Make the script executable 

$ sudo chmod u+x gunicorn_start.bash


23. Test it

(django_env) $ ./gunicorn_start.bash
Starting hello_app as hello
2013-06-09 14:21:45 [10724] [INFO] Starting gunicorn 18.0
2013-06-09 14:21:45 [10724] [DEBUG] Arbiter booted
2013-06-09 14:21:45 [10724] [INFO] Listening at: unix:/webapps/hello_django/run/gunicorn.sock (10724)
2013-06-09 14:21:45 [10724] [INFO] Using worker: sync
2013-06-09 14:21:45 [10735] [INFO] Booting worker with pid: 10735
2013-06-09 14:21:45 [10736] [INFO] Booting worker with pid: 10736
2013-06-09 14:21:45 [10737] [INFO] Booting worker with pid: 10737
^C (CONTROL-C to kill Gunicorn) 
2013-06-09 14:21:48 [10736] [INFO] Worker exiting (pid: 10736)
2013-06-09 14:21:48 [10735] [INFO] Worker exiting (pid: 10735)
2013-06-09 14:21:48 [10724] [INFO] Handling signal: int
2013-06-09 14:21:48 [10737] [INFO] Worker exiting (pid: 10737)
2013-06-09 14:21:48 [10724] [INFO] Shutting down: Master
$ exit


24. Install Supervisor which is used to reboot our application if it closes down unexpectedly 

$ sudo apt-get install supervisor


25. We’ll need a supervisor config file. Create it 

$ sudo vim /etc/supervisor/conf.d/sample_project.conf


26. Add following config settings to it

command = /home/ubuntu/gunicorn_start.bash                  ; Command to start app
user = ubuntu                                                ; User to run as
stdout_logfile = /home/ubuntu/logs/gunicorn_supervisor.log   ; Where to write log messages
redirect_stderr = true                                       ; Save stderr in the same log
environment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8              ; Set UTF-8 as default encoding


27. Make log file to store logs

(django_env) $ mkdir -p /home/ubuntu/logs/
(django_env) $ touch /home/ubuntu/logs/gunicorn_supervisor.log


28. We will need to ask supervisor to reread configuration files and update it so the our newly configuration file get add

For Ubuntu 14.04:

$ sudo supervisorctl reread
sample_project: available
$ sudo supervisorctl update
sample_project: added process group


Our sample_project configuration file gets added to supervisor process group. Start the app now. For this

$ sudo supervisorctl start sample_project
sample_project: started


For Ubuntu 16.04:

$ sudo systemctl restart supervisor
$ sudo systemctl enable supervisor


To check status:

$ sudo supervisorctl status sample_project
sample_project                   RUNNING  pid 24768, uptime 0:00:10

To stop:

$ sudo supervisorctl stop sample_project
sample_project: stopped


To restart:

$ sudo supervisorctl restart sample_project
sample_project: stopped
sample_project: started


Our application will get automatically start if the system reboots


29 Install nginx 

$ sudo apt-get install nginx


30. Create nginx config file

$ sudo vim /etc/nginx/sites-available/sample_project.conf


31. Add following config settings, add your domain name

upstream sample_project_server {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response (in case the Unicorn master nukes a
  # single worker for timing out).
  server unix:/home/ubuntu/django_env/run/gunicorn.sock fail_timeout=0;
server {
    listen   80;
    server_name <your domain name>;
    client_max_body_size 4G;
    access_log /home/ubuntu/logs/nginx-access.log;
    error_log /home/ubuntu/logs/nginx-error.log;
    location /static/ {
        alias   /home/ubuntu/static/;
    location /media/ {
        alias   /home/ubuntu/media/;
    location / {
        # an HTTP header important enough to have its own Wikipedia entry:
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # enable this if and only if you use HTTPS, this helps Rack
        # set the proper protocol for doing redirects:
        # proxy_set_header X-Forwarded-Proto https;
        # pass the Host: header from the client right along so redirects
        # can be set properly within the Rack application
        proxy_set_header Host $http_host;
        # we don't want nginx trying to do something clever with
        # redirects, we set the Host: header above already.
        proxy_redirect off;
        # set "proxy_buffering off" *only* for Rainbows! when doing
        # Comet/long-poll stuff.  It's also safe to set if you're
        # using only serving fast clients with Unicorn + nginx.
        # Otherwise you _want_ nginx to buffer responses to slow
        # clients, really.
        # proxy_buffering off;
        # Try to serve static files from nginx, no point in making an
        # *application* server like Unicorn/Rainbows! serve static files.
        if (!-f $request_filename) {
            proxy_pass http://sample_project_server;
    # Error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /home/ubuntu/static/;


32. Create symbolic link

$ sudo ln -s /etc/nginx/sites-available/sample_project.conf /etc/nginx/sites-enabled/sample_project.conf


33. Start nginx

$ sudo service nginx start


Now open your server’s IP or domain name. Your production level django app should be up. Congrats :)



Join 1000+ People Who Subscribe to Weekly Blog Updates

Back to Blog Home