Skip to content

Instantly share code, notes, and snippets.

Last active May 28, 2024 12:03
Show Gist options
  • Save bradtraversy/cfa565b879ff1458dba08f423cb01d71 to your computer and use it in GitHub Desktop.
Save bradtraversy/cfa565b879ff1458dba08f423cb01d71 to your computer and use it in GitHub Desktop.
Django Deployment - Digital Ocean

Django Deployment to Ubuntu 18.04

In this guide I will go through all the steps to create a VPS, secure it and deploy a Django application. This is a summarized document from this digital ocean doc

Any commands with "$" at the beginning run on your local machine and any "#" run when logged into the server

Create A Digital Ocean Droplet

Use this link and get $10 free. Just select the $5 plan unless this a production app.

Security & Access

Creating SSH keys (Optional)

You can choose to create SSH keys to login if you want. If not, you will get the password sent to your email to login via SSH

To generate a key on your local machine

$ ssh-keygen

Hit enter all the way through and it will create a public and private key at


You want to copy the public key (.pub file)

$ cat ~/.ssh/

Copy the entire output and add as an SSH key for Digital Ocean

Login To Your Server

If you setup SSH keys correctly the command below will let you right in. If you did not use SSH keys, it will ask for a password. This is the one that was mailed to you

$ ssh root@YOUR_SERVER_IP

Create a new user

It will ask for a password, use something secure. You can just hit enter through all the fields. I used the user "djangoadmin" but you can use anything

# adduser djangoadmin

Give root privileges

# usermod -aG sudo djangoadmin

SSH keys for the new user

Now we need to setup SSH keys for the new user. You will need to get them from your local machine

Exit the server

You need to copy the key from your local machine so either exit or open a new terminal

# exit

You can generate a different key if you want but we will use the same one so lets output it, select it and copy it

$ cat ~/.ssh/

Log back into the server

$ ssh root@YOUR_SERVER_IP

Add SSH key for new user

Navigate to the new users home folder and create a file at '.ssh/authorized_keys' and paste in the key

# cd /home/djangoadmin
# mkdir .ssh
# cd .ssh
# nano authorized_keys
Paste the key and hit "ctrl-x", hit "y" to save and "enter" to exit

Login as new user

You should now get let in as the new user

$ ssh djangoadmin@YOUR_SERVER_IP

Disable root login

# sudo nano /etc/ssh/sshd_config

Change the following

PermitRootLogin no
PasswordAuthentication no

Reload sshd service

# sudo systemctl reload sshd

Simple Firewall Setup

See which apps are registered with the firewall

# sudo ufw app list

Allow OpenSSH

### sudo ufw allow OpenSSH

Enable firewall

# sudo ufw enable

To check status

# sudo ufw status

We are now done with access and security and will move on to installing software


Update packages

# sudo apt update
# sudo apt upgrade

Install Python 3, Postgres & NGINX

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

Postgres Database & User Setup

# sudo -u postgres psql

You should now be logged into the pg shell

Create a database


Create user

CREATE USER dbadmin WITH PASSWORD 'abc123!';

Set default encoding, tansaction isolation scheme (Recommended from Django)

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';

Give User access to database


Quit out of Postgres


Vitrual Environment

You need to install the python3-venv package

# sudo apt install python3-venv

Create project directory

# mkdir pyapps
# cd pyapps

Create venv

# python3 -m venv ./venv

Activate the environment

# source venv/bin/activate

Git & Upload

Pip dependencies

From your local machine, create a requirements.txt with your app dependencies. Make sure you push this to your repo

$ pip freeze > requirements.txt

Create a new repo and push to it (you guys know how to do that)

Clone the project into the app folder on your server (Either HTTPS or setup SSH keys)

# git clone

Install pip modules from requirements

You could manually install each one as well

# pip install -r requirements.txt

Local Settings Setup

Add code to your file and push to server

    from .local_settings import *
except ImportError:

Create a file called on your server along side of and add the following

  • EMAIL_*

Run Migrations

# python makemigrations
# python migrate

Create super user

# python createsuperuser

Create static files

python collectstatic

Create exception for port 8000

# sudo ufw allow 8000

Run Server

# python runserver

Test the site at YOUR_SERVER_IP:8000

Add some data in the admin area

Gunicorn Setup

Install gunicorn

# pip install gunicorn

Add to requirements.txt

# pip freeze > requirements.txt

Test Gunicorn serve

# gunicorn --bind btre.wsgi

Your images, etc will be gone

Stop server & deactivate virtual env

# deactivate

Open gunicorn.socket file

# sudo nano /etc/systemd/system/gunicorn.socket

Copy this code, paste it in and save

Description=gunicorn socket



Open gunicorn.service file

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

Copy this code, paste it in and save

Description=gunicorn daemon

ExecStart=/home/djangoadmin/pyapps/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \


Start and enable Gunicorn socket

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

Check status of guinicorn

# sudo systemctl status gunicorn.socket

Check the existence of gunicorn.sock

# file /run/gunicorn.sock


Create project folder

# sudo nano /etc/nginx/sites-available/btre_project

Copy this code and paste into the file

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 by linking to the sites-enabled dir

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

Test NGINX config

# sudo nginx -t

Restart NGINX

# sudo systemctl restart nginx

Remove port 8000 from firewall and open up our firewall to allow normal traffic on port 80

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

You will probably need to up the max upload size to be able to create listings with images

Open up the nginx conf file

# sudo nano /etc/nginx/nginx.conf

Add this to the http{} area

client_max_body_size 20M;

Reload NGINX

# sudo systemctl restart nginx

Media File Issue

You may have some issues with images not showing up. I would suggest, deleting all data and starting fresh as well as removeing the "photos" folder in the "media folder"

# sudo rm -rf media/photos

Domain Setup

Go to your domain registrar and create the following a record

www  CNAME

Go to on the server and change "ALLOWED_HOSTS" to include the domain


Edit /etc/nginx/sites-available/btre_project

server {
    listen: 80;

Reload NGINX & Gunicorn

# sudo systemctl restart nginx
# sudo systemctl restart gunicorn
Copy link

barabara-philipo commented Dec 14, 2020

Navigate to the new users home folder and create a file at '.ssh/authorized_keys' and paste in the key

cd /home/djangoadmin

mkdir .ssh

cd .ssh

nano authorized_keys

where to do so?

Copy link

Saketh143 commented Dec 22, 2020

in my case , reason behind 502 bad gateway was not replacing djangoadmin with myusername
I guess everyone change username in WorkingDirecotry path but not below [service] . :)

see below:


Description=gunicorn daemon

User=djangoadmin ------------->change this to your username .
--access-logfile -
--workers 3
--bind unix:/run/gunicorn.sock


Copy link

I have hosted my project but the static file are not loading, can some solve this issue? I have tried many things, nothing work's for me, this is my ip,

Copy link

yea getting same issue as Usman - static files are there but not loading

Copy link

wilgens7 commented Mar 26, 2021

Got rid of the Bad Gateway error using the advice on this link:

I'm pasting it here for your convenience.

"Go back into your virtualenv with source [your_project_env]/bin/activate and enter which gunicorn
That will return the path to your gunicorn exectuable.

Paste that into the path section of the ‘ExecStart’ value inside the ’/etc/systemd/system/gunicorn.service’ file, and run the
‘sudo systemctl daemon-reload’ and
'sudo systemctl restart gunicorn’
commands to restart your daemon and try curling again with
curl –unix-socket /run/gunicorn.sock localhost

I hope this helps!"

Copy link

@stacytonui check this out.

Copy link

@Usman2684 and @cybernamix, facing the same issue. collectstatic collecting everything needed and paths seem are correct but still can't find statics files. how did you guys solve the problem?
Thanks a lot in advance!

Copy link

Usman2684 commented Dec 5, 2021 via email

Copy link

tunino91 commented Mar 13, 2022

Has anyone gotten a net::ERR_CONNECTION_TIMED_OUT error trying to interact with the database like signing up a user? P.S. My firewall is disabled on my droplet.

Copy link

alan-white-m commented Mar 26, 2022

@Usman2684 @Shamsullo @cybernamix I hack way around this (if you are not using nginx yet) would be editing your so that DEBUG = True

Copy link


  1. Run: eval ssh-agent -s
  2. Run: ssh-add ./.ssh/id_rsa_your_private_key
  3. Now you should be able to ssh in

Copy link

Hey, some how my base.html is not being correctly updated in the browser. I am getting network error's because my base.html is apparently trying to retrieve master.css, even though all my code has zero mentions of master.css, I recently removed this and started using different css files for each app. I have confirmed that locally, on github, and in my digital ocean repository, my base.html has been updated to have no mentions of master.css what so ever. I have used search, grep and find to track down any text's or file names. I have no idea, other than the possibility that digital ocean is somehow caching my html files. Has anyone had a similar issue, I looked into removing the caching for my droplet, but this doesn't appear to be an option.

Copy link

alan-white-m commented Mar 29, 2022

Update, if you are having the problem where your html is not updating after a git pull, it is because your gunicorn or nginx is responsible for caching static files. Run:

sudo systemctl restart gunicorn
sudo systemctl restart nginx

And it should be working now

Copy link

kaybrian commented Sep 5, 2022

This method is working very well but my static files cant be loaded at all

Copy link

Hi @kaybrian how did you solve the static files not loading issue?

Copy link

@Shamsullo could you solve the static files not loading issue?

Copy link

Usman2684 commented Jan 15, 2023 via email

Copy link

ghost commented Feb 7, 2023

I have hosted my project but the static file are not loading, can some solve this issue? I have tried many things, nothing work's for me, this is my ip,

You got any solutions, bro?

Copy link

Hello. How can we add SSL certification (secure connection via https) to our site? Is anyone familiar with that? Thank you.

Copy link

Hello everybody. I see that a lot of people have received errors specifically when it comes to the loading of static files on their production nginx-served django web apps. If you are getting a 403: Forbidden error from nginx when trying to load static files like images, the issue directly corresponds to the permissions, or the lack there of, within the default user home directories.

I have spent the last 3 hours trying to get my static files to load. I tried to change permissions and give ownership to root, which owned my nginx process, but this did not fix the issue. I fixed the issue by moving the location of the entire project. Instead of storing it in my /home/djangoadmin/* directory, I stored it in my /var/www/* directory. You will obviously have to change the gunicorn, and nginx conf files to service the new directory, as well as restart the daemon and nginx, gunicorn services, and re python collectstatic files after the move, but overall this fixed the problem with minimal alterations to the files.

I discovered that this was the issue and solution from this git gist thread (it is 10 years old, but the same issue that we are having):

Copy link

Hi @kaybrian, how did you solve the static files not loading issue?

i think I did the collect static and that's the way I did

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment