Introducing : Managed Cloud Hosting Services is on Beta on BatchNepal 🎉🎉
// //

How to Deploy Django to Production

Added by

at August 26, 2024

How to Deploy Django to Production - BatchNepal

Introduction & Prerequisites

Django is a python-based web framework and is widely used in industry. Due to its security and performance, it is loved by freelancers and companies. In this guide we will perform necessary steps to host a django website in production.  We will be using Nginx as reverse proxy, Gunicorn workers, Postgres database and Ubuntu. Our goal in this journey will be to deploy a django website and host it on our custom domain. 

Requirements:

You will need a Ubuntu server and a django project ready to deploy. We are using DigitalOcean for cheap and performing servers but you can use WSL2 or virtualbox to create virtual ubuntu server and test on it.  We are assuming you have a fresh Ubuntu 22.04 .You can use ifconfig  to get your Ubuntu server ip. In this tutorial , the commands  written after # are only for examples. You don't need to copy and paste them in terminal. Assuming our project name is demowebsite, we have done configurations. You will have to change it accordingly.

Prepare Project Files for Deployment

First of all, we will need to perform some preparations to ensure smoth deployment. Follow these steps:

  1. Cd to your project directory and  activate your virtual environment
  2. Install django-extensions package using:
    (zvenv) C:\Users\Mojo\projects\web\demowebsite> pip install django-extensions
  3. In settings.py file, alter these settings:
    DEBUG = False
    ALLOWED_HOSTS = ['your-server-domain.com', 'your-server-ip','127.0.0.1', 'localhost']
    INSTALLED_APPS = [ ... 'django_extensions',...]
    STATIC_URL = 'static/'
    MEDIA_URL = '/media/'
    if not DEBUG:   
         STATIC_ROOT = "/var/www/demowebsite/static/"
         MEDIA_ROOT = "/var/www/demowebsite/media/"
    else:
        STATIC_ROOT = "staticfiles"
        MEDIA_ROOT = "mediafiles"
      Note: This is example and real production code needs more customization. So change as per your requirements.
  4. Run pip freeze > requirements.txt  to generate  requirements file. We will need this file to install required dependencies.
  5. For our example the project directory we are using is like:
    zvenv                 # virtual environment folder
    demowebsite        #  main project folder.
       - .git
      - manage.py
      - requirements.txt
      - .gitignore
      - .env
      -  firstapp
      -  secondapp
      -  demowebsite
          - settings.py
          -urls.py
  6. Now we can use git push and push our code to github. After this step, we will be doing server and database setup.

Server and Database Setup

Ubuntu Setup

We need to perform some configurations to increase security and smoothness of our server.  

1. Login as root. Root is the default administrative user in linux opertating systems. Use this command and accept the warning about host authenticity if it appears. You may have to enter its password if you have added password authentication in it. 

$ ssh root@your_server_ip   #eg: ssh [email protected]

2. We will need to create normal user because sometimes we can accidently break our server as root if any mistake happens.

$ adduser chetraj22   #hit enter , choose password and reconfirm password and click enter

3. Sometimes we need to perform tasks that require administrative priviliges or root users, that time we will need to use sudo command-to-execute . So we will now add our new user to sudo group.

$ usermod -aG sudo chetraj22        # members of the sudo group are allowed to use the sudo command.

4. Let's set up basic firewalls for our server. Firewalls are used to secure server from attacks and limit what services are allowed to run.

$ufw app list    #list firewallsOutput
Output:
Available applications:  OpenSSH

5. Lets make sure our server allows ssh connections when we login next time.

$ ufw allow OpenSSH           #allows OpenSSH 
$ ufw allow 80/tcp             #allows http connections
$ ufw allow 443/tcp            #allows https connections
$ ufw enable                  #enables allowed firewalls
$ ufw reload                  # reload the firewall to apply changes

6. We need to verify if OpenSSH is allowed or not. We can see that firewall is blocking all connects except ssh for now. You can customize as per your needs.

$ sudo ufw status

7. We can logout as root and login as a normal user now. You may need to enter password if you have configured user to use password.

$exit    # logout from server
$ ssh [email protected]       #login as normal user.

8. Update Ubuntu and Install necessary dependencies like python3, pip, postgresql database, git and nginx. We will later start working with these packages once we install and configure required packages.

$ sudo apt-get update
$ sudo apt install python3-virtualenv
$ sudo apt-get install python3-pip nginx libpq-dev postgresql python3-dev postgresql-contrib git
$ sudo service nginx status  #check if nginx is running or not 

Git and Github Setup

We mostly host our project on private repositories so we may need to perform extra setups and allow our server to fetch code after we update or git push them. 

  • Open Project on VS Code and create a .gitignore File (If needed)
  • Push your Project folder to Your Github Account as Private Repository using git
  • Make Connection between Remote Server and Github Repository via SSH Key
  • Generate ssh keys using 
    $ mkdir ~/.ssh$ sudo ssh-keygen -t ed25519 -C "[email protected]"    #generate ssh key
    $ sudo chown -R chetraj22 .ssh     # in case of permission error
    $ sudo cat ~/.ssh/id_ed25519.pub                       #copy the key
  • Go to Your Github Repo > Settings > Deploy Keys > Deploy keys at sidebar.
  • Click on Add Deploy Key Button and Paste Remote Server's Copied SSH Public Key then Click on Add Key.
  • We can copy your project from private repository to server using git clone command.
    $ sudo mkdir /home/<username>/web           #makes new folder named web or anyname you want 
    $ sudo chown chetraj22:chetraj22 /home/chetraj22/web   # in case of permission error
    $ sudo chmod 755 /home/chetraj22/web                       # in case of permission error
    $ cd /home/<username>/web                         # changes active directory to web
    $ sudo git clone [email protected]:<githubUsername>/<repositoryname>.git  

PostgreSQL Setup

We will need to create a postgres database, user and provide permission to the user in order to allow user modify database data. We will need these credentials later to use in our django settings.py file. Login to postgres session and use:

$ sudo -u postgres psql    #logs in you as postgres user. psql is interactive terminal for postgresql.

postgres=# CREATE DATABASE mydemodb;
postgres=# CREATE USER mydatabaseuser WITH PASSWORD 'mypassword';
postgres=# GRANT ALL PRIVILEGES ON DATABASE mydemodb TO mydatabaseuser;
postgres=# \q         #exit postgres prompt

Gunicorn Setup

Gunicorn will serve as an interface to our django application, and translates client requests in HTTP to Python calls that our application can process. We will then set up Nginx in front of Gunicorn us to take advantage of its high performance connection handling mechanisms and its easy-to-implement security features.

  1. Create a virtual evironment for our project.
    $ cd /home/<username>/web   # eg: /home/chetraj22/web
    $ python3 -m venv zvenv         # creates virtual environment named zvenv inside web directory
    $ source zvenv/bin/activate      # activates virtual environment
    $ cd demowebsite                 # main project directory inside home/username/web/
  2. Install gunicorn and necessary packages in project's requirements.txt file and deactivate virtual environment.
    (zvenv) $ pip install gunicorn(zvenv) 
    $ pip install -r requirements.txt     #to create requirements.txt file use pip freeze > requirements.txt
    (zvenv) $ deactivate
  3. We need to Create System Socket File for Gunicorn.
    $ sudo nano /etc/systemd/system/<demowebsite.com>.gunicorn.socket   # use your website name and remove < >
  4. Paste this code below code using ctrl+shift + v or right click and save file using CTRL + X and then y and hit enter
    [Unit]
    Description=<demowebsite.com>.gunicorn socket
    
    [Socket]
    ListenStream=/run/<demowebsite.com>.gunicorn.sock
    
    [Install]
    WantedBy=sockets.target
  5. Create System Service File for Gunicorn:
    $ sudo nano /etc/systemd/system/<demowebsite.com>.gunicorn.service
  6. Paste this code below. This time extra carefully as it contains project directory and mistake will return Gunicorn error. Replace chet22 with your username and enter your project root directory on WorkingDirectory setting. replace djangoProjectName with your django project name. 
    Example:-
    [Unit]
    Description=<testwebsite.com>.gunicorn
    daemonRequires=<testwebsite.com>.gunicorn.socket
    After=network.target
    
    [Service]
    User=chet22
    Group=www-data
    WorkingDirectory=/home/<chet22>/web/<demowebsite>/
    ExecStart=/home/<chet22>/web/<virtual-env-name>/bin/gunicorn \ 
             --access-logfile - \
              --workers 3 \
              --bind unix:/run/demowebsite.com.gunicorn.sock \
              <djangoProjectName>.wsgi:application
    
    [Install]
    WantedBy=multi-user.target
  7. We will need to start and enable both Gunicorn socket and Gunicorn service. Check the status of both to ensure both are working properly else we will need to configure properly or check for errors.
    $ sudo systemctl start demowebsite.com.gunicorn.socket 
    $ sudo systemctl enable demowebsite.com.gunicorn.socket 
    $ sudo systemctl start demowebsite.com.gunicorn.service
    $ sudo systemctl enable demowebsite.com.gunicorn.service
    $ sudo systemctl status demowebsite.com.gunicorn.socket
    $ sudo systemctl status demowebsite.com.gunicorn.service
  8. Restart Gunicorn. We need need to restart Gunicorn everytime we push new code using git to reflect changes in our production server.
    $ sudo systemctl daemon-reload
    $ sudo systemctl restart demowebsite.com.gunicorn

NGINX Configuration

Now that we have setup Gunicorn, we need to configure Nginx to pass traffic to the gunicorn process. 

1. Creat virtual host file in /var/nginx/sites-available/demowebsite.com using:

$  cd /etc/nginx/sites-enabled/
$  sudo rm -rf default$ sudo nano /etc/nginx/sites-available/demowebsite.com

and paste: 

server{
    listen 80;
    listen [::]:80;
    server_name <demowebsite.com>;
    location = /favicon.ico { access_log off; log_not_found off; }
    location / {
        proxy_set_header Host $http_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-Proto $scheme; 
       proxy_pass http://unix:/run/<demowebsite.com>.gunicorn.sock; 
   }
    location  /static/ {
        root /var/www/<demowebsite>;
    }
    location  /media/ {
        root /var/www/<demowebsite>;
    }
}

2. Now Enable Virtual Host or Create Symbolic Link of Virtual Host File.

$ sudo ln -s /etc/nginx/sites-available/demowebsite.com /etc/nginx/sites-enabled/demowebsite.com

3. We need to check if nginx configuration is correct or not. Also we need to restart nginx to apply changes.

$ sudo nginx -t
$ sudo systemctl restart nginx

4 . Now we need to allow Nginx through firewall

$ sudo ufw delete allow 8000
$ sudo ufw allow 'Nginx Full'

5. Let's create necessary media and static directory inside var/www/ :

$ cd /var/www$ sudo mkdir demowebsite         # must match what we included in nginx conf file and django's settings.py file
$ sudo chown -R <chetraj001>:www-data demowebsite    # change chetraj001 with your username
$ cd demowebsite  
$ sudo mkdir static media     # create both folders with one command

6. Open your project settings.py using nano command and change these lines:

DEBUG = False
STATIC_URL = 'static/'
STATIC_ROOT = "/var/www/demowebsite/static/"
MEDIA_URL = '/media/'
MEDIA_ROOT = "/var/www/demowebsite/media/"

7.  Again restart gunicorn and nginx to load fresh settings.

$ sudo systemctl daemon-reload
$ sudo systemctl restart demowebsite.com.gunicorn
$ sudo systemctl restart nginx

Post Setup Steps  & SSL

Once we complete cloud and server setup, we can follow these steps. For this we will need to be inside demowebsite .Just run:

$ cd /home/<username>/web
$ source zvenv/bin/activate
zvenv) $ cd <demowebsite>                 #cd to your projectname
(zvenv) $ python manage.py clean_pyc   # cleans .pyc compiled files
(zvenv) $ python manage.py clear_cache    # cleans project cache files
(zvenv) $ python manage.py collectstatic  # serves static files
(zvenv) $ python manage.py makemigrations
(zvenv) $ python manage.py migrate
(zvenv)$ python manage.py createsuperuser
(zvenv) $ deactivate

Let's again restart gunicorn and  nginx and serve fresh website.

$ sudo systemctl daemon-reload
$ sudo systemctl restart demowebsite.com.gunicorn
$ sudo nginx -t                                   # test if nginx configuration is ok
$ sudo systemctl restart nginx                  # restart nginx

After that we will need to assign SSL to our server. SSL an encryption security protocol that ensures excrypted link between client and server. To assign SSL Certificate to ubuntu server, we use Certbot. It is a fully-featured, extensible client for the Let's Encrypt CA that can automate the tasks of obtaining ssl certificates and configuring webservers to use them. 

$ sudo apt install certbot python3-certbot-nginx
$ sudo certbot --nginx -d your_domain.com -d www.your_domain.com

You will be provided yes or no questions, read the question and answer yes or no and with few clicks, your server will receive a ssl certificate for free. Let's Encrypt's SSL certificates generally expire after 90 days. Here are bonus commands to check for validity of ssl certificate and to renew ssl.

$ sudo systemctl status certbot.timer        # Check Status of Certbot
$ sudo certbot renew --dry-run               # dry run ssl renewal.

Conclusion

In this tutorial, we have learned how to deploy django website to production server and install SSL certificates on it. By liveraging these steps, we can host a powerful django website online using Gunicorn, Nginx and Ubuntu.  In case you want us to solve your problem on paid basis, then you can always contact us.

if you feel these steps hard then you will like this post:  How to Automate Django Deployment using Github Action. I have discussed on how we can automate deployment using github actions. With this guide, next time you push your code to github, it will trigger actions which will perform deployment tasks automatically.  

Related Topics

How to create SSH Keys  - Complete Tutorial
// //
How to create SSH Keys - Complete Tutorial

In this guide, we will learn how to create ssh keys using PuttyGen and command line.

August 26, 2024

Malicious javascript injected into 100k websites
Malicious javascript injected into 100k websites

Polyfill.js has been found injecting malicious code and performing supply chain attack to visitors.

August 26, 2024

Automating Django Deployment with Github Actions
// //
Automating Django Deployment with Github Actions

In this guide we will learn how to automate django deployment simply using github actions

August 26, 2024

[Solved] Unknown at rule @tailwind warning in VS Code
//
file_type_tailwind
TailwindCss
//
[Solved] Unknown at rule @tailwind warning in VS Code

In this guide, we will see methods to fix "unknown at rule@tailwind" error in VS Code.

August 26, 2024

Boost Your Online Presence with the Best SEO Agency in Nepal
// //
Boost Your Online Presence with the Best SEO Agency in Nepal

In this guide, we will learn why should you not ignore SEO for your business and how it impacts your business growth.

August 26, 2024