BackBack

How to deploy Django based projects?

How to deploy Django based projects?
5 Minutes

Introduction

The world is vast. Science, too. It's interesting to know that programming is also widespread. Python, as a programming language, has many orientations. Nowadays, for web development with Python, I think Django is a great and powerful web framework for this purpose. Let's see how to deploy a Django-based project on the server.


We have many tools with different combinations for running Django projects on a production/test server. We use this stack listed below:

  • Ubuntu 18.04
  • Python 3.6
  • Django 2.1.5
  • Nginx 1.14
  • Postgres 10.6
  • Pip 19.0.2
  • Virtualenv 15.1.0
  • Supervisor 3.3.1
  • Gunicorn 19.9.0

Step 1 - Preparing Environment

In the beginning, we need to prepare our deployment environment, so we should update our OS and install the required packages.

1- First, we need to create a new user for our project (I used my project name as username, you can use anything else):

  • adduser eggplant
    

Output

Adding user `eggplant' ...
Adding new group `eggplant' (1002) ...
Adding new user `eggplant' (1002) with group `eggplant' ...
Creating home directory `/home/eggplant' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for eggplant_user
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] y

2- Next, add the user to the sudoers group:

  • adduser eggplant sudo
    

Output

Adding user `eggplant' to group `sudo' ...
Adding user eggplant to group sudo
Done.

3- Switch to the new user shell:

  • su - eggplant
    

4- Now, we need to upgrade our Ubuntu packages:

  • sudo apt update
    
  • sudo apt upgrade
    

5- Next, to run our project, we need to install some packages:

  • sudo apt install nginx postgresql python3-pip virtualenv supervisor
    

Step 2 - Preparing PostgreSQL

In this step, we create a user and database for our project.

1- Switch to Postgres shell:

  • sudo -iu postgres
    

2- Create a user for our database:

  • createuser eggplant_user --pwprompt
    

3- Create a database for our project:

  • createdb --owner eggplant_user eggplant
    

4- Exit from Postgres shell:

  • exit
    

Step 3 - Clone and Setup Project

Retrieve project files from the source control and then set up the environment.

1- Clone project (In this case, I used Github):

  • git clone https://github.com/mrouhi13/eggplant.git
    

2- Create a settings file from the sample:

  • cd eggplant
    
  • cp eggplant/settings.py.sample eggplant/settings.py
    

3- Update settings.py file:

  • nano eggplant/settings.py
    

And fill in the SECRET_KEY value. you can use Djecrety to generate SECRET_KEY for your project and then update the database section with created user and database.

Note In the settings.py file, by default, DEBUG = False; so you must set your server IP or domain (if it exists) in ALLOWED_HOST. You can set DEBUG = True if testing on localhost.

eggplants/settings.py

SECRET_KEY = '<secret_key_generated_by_djecrety>'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'eggplant',
        'USER': 'eggplant_user',
        'PASSWORD': '123456',
        'HOST': 'localhost',
        'PORT': '5432'
    }
}

4- Create python3 virtualenv and activate it:

  • virtualenv .env --python=python3
    
  • source .env/bin/activate
    

At this point, our project directory is like this:

Output

eggplant/
|--- eggplant/
|------ __init__.py
|------ settings.py
|------ settings.py.sample
|------ usrls.py
|------ wsgi.py
|--- .env/
|--- .git/
|--- LICENSE
|--- manage.py
|--- README.md
|--- requirements.txt
|--- .gitignore

5- Install the Django package and other dependencies package from _ requirement.txt_:

  • pip install -r requirements.txt
    

6- Migrate models to the database and collect static files:

  • ./manage.py migrate
    
  • ./manage.py collectstatic
    

7- At last, run the server to test that everything is okay:

  • ./manage.py runserver
    

Output

Performing system checks...
System check identified no issues (0 silenced).
February 02, 2019 - 13:26:69
Django version 2.1.5, using settings 'eggplant.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Hit Ctrl + c to exit.

Step 4 - Preparing Gunicorn

In this step, we must connect gunicorn to the project using wsgi.py.

Note Gunicorn package installed with requirements.txt.

1- Create gunicorn_start file and add configuration:

  • nano .env/bin/gunicorn_start
    

.env/bin/gunicorn_start

#!/bin/bash
NAME="eggplant"
DIR=/home/eggplant/eggplant
USER=eggplant
GROUP=eggplant
WORKERS=3
BIND=unix:/home/eggplant/eggplant/.env/run/gunicorn.sock
DJANGO_SETTINGS_MODULE=eggplant.settings
DJANGO_WSGI_MODULE=eggplant.wsgi
LOG_LEVEL=error

cd $DIR
source .env/bin/activate

export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DIR:$PYTHONPATH

exec .env/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $WORKERS \
--user=$USER \
--group=$GROUP \
--bind=$BIND \
--log-level=$LOG_LEVEL \
--log-file=-

Hit Ctrl + x and save the file.

2- Add execute permission to gunicorn_start:

  • chmod u+x .env/bin/gunicorn_start
    

3- Make a run directory for the Unix socket file:

  • mkdir .env/run
    

4- Make a logs directory and gunicorn log file:

  • mkdir .env/logs
    
  • touch .env/logs/gunicorn-error.log
    

Step 5 - Preparing Supervisor

We use the supervisor to ensure our project always is running.

1- The first start and enable supervisor service:

  • sudo systemctl enable supervisor
    
  • sudo systemctl start supervisor
    

2- Create eggplant.conf config file for our project:

  • sudo nano /etc/supervisor/conf.d/eggplant.conf
    

/etc/supervisor/conf.d/eggplant.conf

[program:eggplant]
command=/home/eggplant/eggplant/.env/bin/gunicorn_start
user= eggplant
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/eggplant/eggplant/.env/logs/gunicorn-error.log

Hit Ctrl + x and save the file.

3- Update supervisor service to read the new configuration file:

  • sudo supervisorctl reread
    
  • sudo supervisorctl update
    

4- Check our project status:

  • sudo supervisorctl status eggplant
    

Output

eggplant RUNNING pid 30668, uptime 00:00:69

Step 6 - Preparing Nginx

Now we config a web server to publish our project to the internet.

1- First remove the default config symlink and then enable and start nginx service:

  • sudo rm /etc/nginx/sites-enabled/default
    
  • sudo systemctl start nginx
    
  • sudo systemctl enable nginx
    

2- Create a configuration file for our project and add the config to it:

  • sudo nano /etc/nginx/sites-available/eggplant
    

/etc/nginx/sites-available/eggplant

upstream eggplant_server {
    server unix:/home/eggplant/eggplant/.env/run/gunicorn.sock fail_timeout=0;
}

server {
    listen 80;
    listen [::]:80;

    server_name www.example.com;

    access_log /home/eggplant/eggplant/.env/logs/nginx-access.log;
    error_log /home/eggplant/eggplant/.env/logs/nginx-error.log;

    location /static/ {
        alias /home/eggplant/eggplant/static/;
    }

    location /media/ {
        alias /home/eggplant/eggplant/media/;
    }

    location / {
        try_files $uri @proxy_to_app;
    }

    location @proxy_to_app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://eggplant_server;
    }
}

Hit Ctrl + x and save the file.

3- Create a symlink for the configuration file:

  • sudo ln -s /etc/nginx/sites-available/eggplant /etc/nginx/sites-enabled/eggplant
    

4- Restart nginx service:

  • sudo systemctl restart nginx
    

Finally

We reboot the server to check everything is okay and that all configuration correctly work:

  • sudo reboot
    

After all these things, check your domain (localhost), and you must see something like this:

Final Result!

How to update our project?

For updating our project files using git, we need to take a few steps:

1- First, go to the project directory and activate virtualenv:

  • cd eggplant
    
  • source .env/bin/activate
    

2- Next, pull new changes from the repository:

  • git pull origin master
    

3- And then, migrate and collect static files updates:

  • ./manage.py migrate
    
  • ./manage.py collectstatic
    

4- In the end, restart project and exit:

  • sudo supervisorctl restart eggplant
    
  • exit
    

Conclusion

I tried to show a basic tutorial in this post. Of course, most of the steps have more details, and as a developer, you should look for hardening, security, performance, SSL certification, etc. This post is very simplified and general to be usable for everyone. You can search for the tools we used in this post and find the best solution for your project. I suggest some links below to start:

→ Gunicorn Official Documentation
→ Supervisor Official Documentation
→ Nginx SSL Configuration