logo

Comprehensive Guide to Deploying Django on a VPS

O

Ohidur Rahman Bappy

MAR 22, 2025

Comprehensive Guide to Deploying Django on a VPS

Introduction

This guide provides detailed steps to create a Virtual Private Server (VPS), secure it, and deploy a Django application using Ubuntu 18.04. The instructions are a summarized and enhanced version of this Digital Ocean guide.

  • Commands starting with $ are run on your local machine.
  • Commands starting with # are run when logged into the server.

Creating a Digital Ocean Droplet

You can start by using this link to get $10 free credit. For non-production applications, select the $5 plan.

Security and Access

Creating SSH Keys (Optional)

Generate SSH keys on your local machine:

$ ssh-keygen

These keys will be created at:

~/.ssh/id_rsa
~/.ssh/id_rsa.pub

Copy the contents of id_rsa.pub to your Digital Ocean account.

Logging Into Your Server

If you set up SSH keys optimally, use:

$ ssh root@YOUR_SERVER_IP

Creating and Configuring a New User

Create a new user, for example, djangoadmin:

# adduser djangoadmin

Grant root privileges:

# usermod -aG sudo djangoadmin

Set up SSH keys for the new user by creating a .ssh/authorized_keys file in their home directory.

Disable Root Login

Edit the SSH config:

# sudo nano /etc/ssh/sshd_config

Set the following:

PermitRootLogin no
PasswordAuthentication no

Reload the SSH service:

# sudo systemctl reload sshd

Setting Up a Simple Firewall

Register your app with the firewall:

# sudo ufw app list

Allow OpenSSH:

# sudo ufw allow OpenSSH

Enable the firewall:

# sudo ufw enable

Check the status:

# sudo ufw status

Software Installation

Updating Packages

# sudo apt update
# sudo apt upgrade

Installing Python, Postgres, and NGINX

# sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl

Postgres Database and User Setup

# sudo -u postgres psql

Create a database and user:

CREATE DATABASE btre_prod;
CREATE USER dbadmin WITH PASSWORD 'abc123!';
ALTER ROLE dbadmin SET client_encoding TO 'utf8';
ALTER ROLE dbadmin SET default_transaction_isolation TO 'read committed';
ALTER ROLE dbadmin SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE btre_prod TO dbadmin;

Exit the Postgres shell with \q.

Virtual Environment Setup

Install the Python virtual environment package:

# sudo apt install python3-venv

Create a project directory and virtual environment:

# mkdir pyapps
# cd pyapps
# python3 -m venv ./venv

Activate the environment:

# source venv/bin/activate

Git and Project Setup

Create a requirements.txt and push it to your repository:

$ pip freeze > requirements.txt

Clone your project:

# git clone https://github.com/yourgithubname/btre_project.git

Install pip modules:

# pip install -r requirements.txt

Configure Local Settings

In settings.py, add:

try:
    from .local_settings import *
except ImportError:
    pass

Create local_settings.py and configure:

  • SECRET_KEY
  • ALLOWED_HOSTS
  • DATABASES
  • DEBUG
  • EMAIL_*

Running Migrations

# python manage.py makemigrations
# python manage.py migrate

Create a superuser:

# python manage.py createsuperuser

Gather static files:

# python manage.py collectstatic

Allow port 8000:

# sudo ufw allow 8000

Test with:

# python manage.py runserver 0.0.0.0:8000

Gunicorn Setup

Install Gunicorn:

# pip install gunicorn

Add to requirements.txt:

# pip freeze > requirements.txt

Test Gunicorn:

# gunicorn --bind 0.0.0.0:8000 btre.wsgi

Configuring Gunicorn as a Service

Create and edit gunicorn.socket:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Create and edit gunicorn.service:

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

[Service]
User=djangoadmin
Group=www-data
WorkingDirectory=/home/djangoadmin/pyapps/btre_project
ExecStart=/home/djangoadmin/pyapps/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          btre.wsgi:application

[Install]
WantedBy=multi-user.target

Start and enable the Gunicorn socket:

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

Check status:

# sudo systemctl status gunicorn.socket

NGINX Configuration

Create a project file in sites-available:

server {
    listen 80;
    server_name YOUR_IP_ADDRESS;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/djangoadmin/pyapps/btre_project;
    }
    
    location /media/ {
        root /home/djangoadmin/pyapps/btre_project;    
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Enable the file:

# sudo ln -s /etc/nginx/sites-available/btre_project /etc/nginx/sites-enabled

Test NGINX and restart:

# sudo nginx -t
# sudo systemctl restart nginx

Adjust firewall settings:

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

Domain Setup

Create DNS records with your domain registrar:

@  A Record  YOUR_IP_ADDRESS
www  CNAME  example.com

Update ALLOWED_HOSTS in local_settings.py:

ALLOWED_HOSTS = ['IP_ADDRESS', 'example.com', 'www.example.com']

Edit the NGINX config and update the server name. Reload NGINX and Gunicorn:

# sudo systemctl restart nginx
# sudo systemctl restart gunicorn

Your Django application should now be live and operational on your VPS!