Comprehensive Guide to Deploying Django on a VPS
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!