How To Deploy Your Django App On OVH (VPS with Nginx) + SSL certificate + Domain Name

Selmi Abderrahim
9 min readDec 24, 2021

Introduction

Now you’ve created your website using Django, you tested it and everything works perfectly, so it’s time to make it alive and install it on a public web server so that it can be accessed by anyone over the Internet. This article provides an easy-to-follow guide to deploy your Django app on any VPS/Cloud Sever (in particalar on OVH) with Nginx. DjangoNGINX is a popular and well-tested combination used to deploy web applications in production. In this guide, I will try to provide to you the steps you need to have your Django project deployed on a production server using Ubuntu 21.04.

The web hosting provider we will cover today is OVH. It’s not the best option for Django, but it has many great features rather than its attractive price for a 2GB-RAM VPS (compared to Digitalocean: $5 for 0.5GB RAM).

So what do we need?

  • We need a functional Django app that you have tested with Django’s built-in development server.
  • Make sure you migrate your app locally (python manage.py makemigrations)

Note: This tutorial looks easier when you watch the tutorial on YouTube.

Setup OVH

There is no need to cover the creation of an OVH account and buying a VPS, the only things you need to take care of are:

  • Choosing Distribution image: Ubuntu 21.04 LTS (which we will cover in this tutorial)
  • Choosing region: [somewhere close to you]

When OVH confirm and accept your order, they will send you an email with all the information you need.

Here are what we need to know about that email I received:

  • IPv4 address of the VPS : 51.222.13.158
  • A non-root user with sudo privileges: ubuntu
  • Password : a7b5R9ZV7kcW

We will use this infos along this tutorial to make it easy to follow.

Update settings.py file

In order to move our Django app from a local development environment to live server, we must change the settings.py file to make it fit our installed VPS.

from pathlib import Path                                                #1
from decouple import config
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False #2
ALLOWED_HOSTS = ["51.222.13.158", ".selmi.tech"] #3

1 — Django Environment Variables

In the settings file, there are many things that we should keep safe, like the secret key , which is used on sensitive data structures like session identifiers, cookies and password reset tokens. But you can rely on the SECRET_KEY value to cryptographically protect any sensitive data structure in a Django project.

It is important to keep sensitive data like secret key, passwords, API keys and so on away from prying eyes. These data should be loaded with Environment variables on runtime. An environment variable is a variable whose value is set outside the program, typically through a functionality built into the operating system. An environment variable is made up of a name/value pair.

For that, we will use Python-Decouple.

To install it, run this command line:

pip install python-decouple

Then import the config object on the top of the settings file.

from decouple import config

And replace the secret key or any other peice of data in the settings with this line:

SECRET_KEY = config('SECRET_KEY')

And create a .env text file on your repository’s root directory in this form:

SECRET_KEY=your_secret_key_without_double_or_single_quotes

2 — Set DEBUG to False

One of the first things that’s necessary to launch a Django application into the real world is to change the DEBUG variable to False. during the production phase, it is a bad idea to leave DEBUG set to True, because it shows lots of information about your project: excerpts of your source code, local variables, settings, libraries used, etc.

3 — Define ALLOWED_HOSTS

If you notice, the ALLOWED_HOSTS list in settings.py is empty. The goal of ALLOWED_HOSTS is to validate a request’s HTTP Host header. Validation is done to prevent hackers from sending fake HTTP Host headers that can potentially poison caches and password reset emails with links to malicious hosts. Since this issue can only present itself under an uncontrolled user environment, this validation is only done when DEBUG=False.
If you switch to DEBUG=False and ALLOWED_HOSTS is left empty, Django refuses to serve requests and returns 400 bad request pages, since it can’t validate incoming HTTP Host headers.
In my case, I passed the VPS IP address and my domain name as allowed hosts.

Create a private GitHub repo for your project

The easiest way to move your project from your laptop to your VPS is by cloning it from GitHub, so, we need to create a private GitHub repository that holds our project.

Go to your GitHub account and create a new private repository.

Then, you’ll see this window

Then run these commands:

cd 'your_project_folder'git initgit add .git commit -m "first commit"git remote add origin https://github.com/SelmiAbderrahim/selmitechgit push -u origin master

Note: Make sure to add .gitignore file if you have any folder that shouldn’t be uploaded in the GitHub. In my case, I added the virtualenv folder to the this file.

Connect via SSH

Secure Shell (SSH) is a cryptographic network protocol used for a secure connection between a client and a server.

SSH protocol enables client authentication using traditional passwords or public key authentication, and that’s the use case of today, we will try to connect our Ubuntu installed in the VPS.

1 — Installing SSH on Ubuntu

The SSH server is not installed by default on Ubuntu desktop systems but it can be easily installed from the standard Ubuntu repositories.

To install and enable SSH on your Ubuntu system complete the following steps:

Open your terminal either and run this command:

$ sudo apt update && sudo apt install openssh-server

Enter the password when prompted and enter Y to continue with the installation.

Ubuntu comes with a firewall configuration tool called UFW. If the firewall is enabled on your system, make sure to open the SSH port:

$ sudo ufw allow ssh

Connecting to our VPS

To connect to your Ubuntu machine over LAN you only need to enter the following command:

ssh username@ip_address

In my case:

ssh ubuntu@51.222.13.158

Then it asks us for the password.

Set up an A (address) record for our domain

Now, we need to configure our domain name so it can point to our Django project.

I got a domain name from Namecheap, but these instructions will work for almost all of the domain name providers.

1. Sign into your Namecheap account

2. Select Domain List from the left sidebar and click on the Manage button next to your domain:

3. Navigate to the Advanced DNS tab at the top of the page:

4. Find the Host records section and click on the Add New Record button

5. Select A Record for Type and enter the Host you would like to point to an IP address:

@ — used to point a root domain (yourdomain.tld) to the IP address:
A Record | @ | 51.222.13.158
www — is selected when it is needed to point www.yourdomain.tld to the IP address:
A Record | www | 51.222.13.158

Move your Django App to your VPS

In order to deploy your Django app, we first need to have it locally on our VPS. If your project is hosted on GitHub, we can trivially download it by running on our VPS

$ git clone https://github.com/SelmiAbderrahim/selmitech

And since it’s a private repo, it will ask you for your username and password.

Now, we have “selmitech” folder in our vps machine.

Deploy the Django app in the VPS

To start the deployment process, we’ll download and install few things that we need from the Ubuntu repositories. We will use the Python package manager pip to install additional components.

$ sudo apt-get update
$ sudo apt-get install python3-pip python3-dev libpq-dev nginx

Create a Python Virtual Environment

Now that we have installed Python pip, we will be installing our Django project requirements within a virtual environment for better control over the packages.

To do this, we first need to install the virtualenv tool. We can install this with pip:

$ python -m pip install virtualenv

Within the project directory, create a Python virtual environment by typing:

$ cd selmitech$ python -m virtualenv env

This will create a directory called env within selmitech directory.

Before we install our project’s Python requirements, we need to activate the virtual environment. You can do that by typing:

$ source env/bin/activate

Then we need to install our project’s packages in our virtual environment.

$ python -m pip install -r requirements.txt

Also, we need to install gunicorn.

$ python -m pip install gunicorn

If you followed the initial server setup guide, you should have a UFW firewall protecting your server. In order to test the development server, we’ll have to allow access to the port we’ll be using.

Create an exception for port 8000 by typing:

$ sudo ufw allow 8000

Then, we can test our your project by starting up the Django development server with this command:

$ python manage.py runserver 0.0.0.0:8000

In your web browser, visit your server’s domain name or IP address followed by :8000:

51.222.13.158:8000

And you should see your app working out there, and it’s okay if thhe static files aren’t laoded, we will fix it later.

Testing Gunicorn’s Ability to Serve the Project

The last thing we want to do before leaving our virtual environment is test Gunicorn to make sure that it can serve the application. We can do this by entering our project directory and using gunicorn to load the project’s WSGI module:

$ gunicorn --bind 0.0.0.0:8000 project.wsgi

This will start Gunicorn on the same interface that the Django development server was running on. You can go back and test the app again.

Create a Gunicorn systemd Service File

We have tested that Gunicorn can interact with our Django application, but we should implement a more robust way of starting and stopping the application server. To accomplish this, we’ll make a systemd service file.

Create and open a systemd service file for Gunicorn with sudo privileges in your text editor:

$ sudo nano /etc/systemd/system/gunicorn.service

And paste this inside of this file:

[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/selmitech
ExecStart=/home/ubuntu/selmitech/env/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/ubuntu/selmitech/project.sock project.wsgi:application
[Install]
WantedBy=multi-user.target

Just make sure to change the:

  • User, in my case it’s ubuntu
  • Working directory, to know the path, just get into your project folder (in my case selmitech) and type the command “pwd”
  • Path of gunicorn in your virtual environment folder, in my case (/home/ubuntu/selmitech/env/bin/gunicorn)
  • Django project’s folder name, the one that contains “settings.py”, in my case it’s (project)

With that, our systemd service file is complete. Save and close it now.

We can now start the Gunicorn service we created and enable it so that it starts at boot:

$ sudo systemctl start gunicorn
$ sudo systemctl enable gunicorn

Configure Nginx to Proxy Pass to Gunicorn

Now that Gunicorn is set up, we need to configure Nginx to pass traffic to the process.

Start by creating and opening a new server block in Nginx’s sites-available directory:

$ sudo nano /etc/nginx/sites-available/selmitech

Inside, open up a new server block. We will start by specifying that this block should listen on the normal port 80 and that it should respond to our server’s domain name or IP address:

server {
listen 80;
server_name selmi.tech www.selmi.tech;
location = /favicon.ico { access_log off; log_not_found off; }

location /static/ {
root /home/ubuntu/selmitech;
}
location /media/ {
root /home/ubuntu/selmitech;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/ubuntu/selmitech/project.sock;
}
}

ave and close the file when you are finished. Now, we can enable the file by linking it to the sites-enabled directory:

$ sudo ln -s /etc/nginx/sites-available/selmitech /etc/nginx/sites-enabled

Test your Nginx configuration for syntax errors by typing:

$ sudo nginx -t

If no errors are reported, go ahead and restart Nginx by typing:

$ sudo systemctl restart nginx

Finally, we need to open up our firewall to normal traffic on port 80. Since we no longer need access to the development server, we can remove the rule to open port 8000 as well:

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

You should now be able to go to your server’s domain or IP address to view your application.

Create a Self-Signed SSL Certificate for Nginx

TLS, or transport layer security, and its predecessor SSL, which stands for secure sockets layer, are web protocols used to wrap normal traffic in a protected, encrypted wrapper.

Using this technology, servers can send traffic safely between the server and clients without the possibility of the messages being intercepted by outside parties. The certificate system also assists users in verifying the identity of the sites that they are connecting with.

Install Let’s Encrypt SSL on Ubuntu with Certbot

Let’s Encrypt provides free SSLs for your websites to use secure (SSL) connections. Certbot is free open source software that allows you to easily create Let’s Encrypt SSLs on your cloud server hosting.

Make sure you have snapd installed in your system

$ sudo apt install snapd

Install Certbot using snapd

$ sudo snap install --classic certbot

Get a free SSL encryption for your domain:

$ sudo certbot --nginx

That’s it.

Congratulations! You have officially deployed your Django App on OVH with a secure SSL certificate and great performance.

If you faced any problems during your deployment, feel free to comment below.

Watch the tutorial on YouTube.

--

--

Selmi Abderrahim

I’m a computer science student. I do freelancing in my free time. I am a big fan of affiliate marketing, it helped me a lot to monetize my daily stuff.