Deploying Django app on Ubuntu at digitalocean + SSL certificate 🇬🇧

Xiaoou Wang

It can be quite frustrating/challenging for django newbies to deploy their app because a bunch of new concepts should be known in order to play the game with confidence. This tutorial gives you all the basics and further reference. The structure of the project can be seen in the following screenshot: the project my_blog is itself located in the blog folder.

Deployment in a nutshell

You need

  • a web server (often Nginx or apache) to handle http requests

  • an application server called gunicorn to serve your django app (the middle layer)

  • a django app

  • a database management system (often mysql or postgresql) to store information.

  • sometimes a firewall system to block some requests

Step by step

A server/droplet on digitalocean with often a Linux system as Ubuntu

Note, some tutorials said that a password would be sent to your email. This is outdated, remember, for an easy start, to choose the password method and set your password as follows. The 5$ plan is largely sufficient for personal use.

A regular user with some root privileges

For our purpose you just need the following steps:

First connect to the server using ssh, on Mac you are good to go already on Windows you can use PuTTy, see the reference

ref https://www.digitalocean.com/docs/droplets/how-to/connect-with-ssh/

ssh root@your_server_ip

The server ip can be seen here

Then add a regular user with root privileges

ref https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04

adduser sammy # add a user
usermod -aG sudo sammy # give sammy root privileges

Now log out and log in this regular user

ssh sammy@your_server_ip

Update apt-get to install the most recent packages

Here we install pip, postgresql (database tool) and nginx (web server). In simple terms, nginx is the server who takes http requests, another common server is apache.

sudo apt-get update
sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx

Set up the database

First log into postgresql

sudo -u postgres psql

Then create a database, user and password.

CREATE DATABASE yourproject; # replace yourproject with your database name
CREATE USER myprojectuser WITH PASSWORD 'password'; # set username and password
ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
\q # quit

Add the database setting set up in step 4 into your Settings.py on your local computer

if DEBUG:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': 'yourdatabasename',
            'USER': 'yourdatabaseuser',
            'PASSWORD': 'yourdatabasepassword',
            'HOST': 'localhost',
            'PORT': '',
        }
    }

Set up a virtual environment for Python

sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

Since my website’s source code is hosted on github I will use git to clone the project into the folder blog.

Run pip freeze > requirements.txt on your local computer then use pip install -r requirements.txt to install dependencies for your app. Remember to run pip install psycopg2-binary gunicorn to have these two packages in the requirements.txt.

Also add your service ip in ALLOWED_HOSTS of your globalsettings.py

Update your git repository, be sure to have uploaded the most recent requirements.txt. Then get the repository on the server by running:

git clone yourrepository blog
cd blog
git config credential.helper store # remember your username and password

And then create a virtualenv named env and activate it

virtualenv env
source env/bin/activate
pip install -r requirements.txt

Run migrate

python manage.py makemigrations
python manage.py migrate

Test on 8000 port with and without gunicorn

sudo ufw allow 8000 # allow 8000 in the firewall
python manage.py runserver 0.0.0.0:8000

Go to http://server_domain_or_IP:8000, it’s very important to use http since you haven’t set ssl certificate (no https support)

Now test the gunicorn. Gunicorn is an application server which serves your django app.

gunicorn --bind 0.0.0.0:8000 my_blog.wsgi

Set up Gunicorn service file

I use vim here, but you are free to use some more beginner-friendly editor like nano. The gunicorn service file is to ensure that gunicorn is run automatically in background to serve django

sudo vim /etc/systemd/system/gunicorn.service

An example service file using sammy as user looks as follows:

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/blog
ExecStart=/home/sammy/blog/env/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/sammy/my_blog.sock my_blog.wsgi:application

[Install]
WantedBy=multi-user.target

Now, start gunicorn!

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

Set Nginx

sudo vim /etc/nginx/sites-available/blog

An example setting looks as following

server {
    server_name 142.93.110.76;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/blog;
    }

    location /media/ {
        root /home/sammy/media;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/my_blog.sock;
    }
}

Make a symbolic link to make your setting available to nginx

sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled

Test and start your Nginx server, you are good to go!

sudo nginx -t
sudo systemctl restart nginx
# stop 8000 port in the firewall
sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'

Add a ssl certificate to make https available

Almost nobody uses http these days. Let’s now see how to secure our site by adding a ssl certificate!

To have a more solid grasp on ssl certificate, see reference here.

sudo apt install certbot python3-certbot-nginx # install some useful packages
sudo vim /etc/nginx/sites-available/blog

Change the server_name line, example:

server_name 142.93.110.76 nlpinpython.com www.nlpinpython.com;

Restart Nginx and allow https in the firewall:

sudo systemctl reload nginx
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'

Get a ssl certificat

sudo certbot --nginx -d nlpinpython.com -d www.nlpinpython.com

At the last step, select redirect

Congratulations to make it this far.

Most useful commands:

# set up nginx
sudo vim /etc/nginx/sites-available/blog
# set up gunicorn
sudo vim /etc/systemd/system/gunicorn.service
# restart gunicorn after change
sudo systemctl restart gunicorn
# restart nginx
sudo systemctl restart nginx

Export your database for backup:

sudo -i -u postgres -W
pg_dump dbname > dbexport.pgsql

Supplementary references:

  • about firewall https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-18-04

  • about postgresql backup https://www.digitalocean.com/community/tutorials/how-to-backup-postgresql-databases-on-an-ubuntu-vps

  • general instructions https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-20-04