Back to Blog Home

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

Jay Mishra| Django | 7 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
postgres@ubuntu:~$

 

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) $ django-admin.py 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 manage.py runserver 0.0.0.0:8000
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 0.0.0.0:8000/
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 settings.py as follow

DATABASES = {
    '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 settings.py’s allowed hosts

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

 

17. Sync the db

(django_env) $ python manage.py 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 setup.py egg_info for package gunicorn
Installing collected packages: gunicorn
  Running setup.py 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 0.0.0.0:8001

 

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

#!/bin/bash
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
cd $DJANGODIR
source /home/ubuntu/django_env/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
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 \
  --log-file=-

 

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

[program:sample_project]
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:
        #   http://en.wikipedia.org/wiki/X-Forwarded-For
        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;
            break;
        }
    }
    # 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