Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

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

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

$ cat ~/.ssh/id_rsa.pub

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/id_rsa.pub

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

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 DATABASE btre_prod;

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

GRANT ALL PRIVILEGES ON DATABASE btre_prod TO dbadmin;

Quit out of Postgres

\q

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 https://github.com/yourgithubname/btre_project.git

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 settings.py file and push to server

try:
    from .local_settings import *
except ImportError:
    pass

Create a file called local_settings.py on your server along side of settings.py and add the following

  • SECRET_KEY
  • ALLOWED_HOSTS
  • DATABASES
  • DEBUG
  • EMAIL_*

Run Migrations

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

Create super user

# python manage.py createsuperuser

Create static files

python manage.py collectstatic

Create exception for port 8000

# sudo ufw allow 8000

Run Server

# python manage.py runserver 0.0.0.0:8000

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 0.0.0.0:8000 btre.wsgi

Your images, etc will be gone

Stop server & deactivate virtual env

ctrl-c
# deactivate

Open gunicorn.socket file

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

Copy this code, paste it in and save

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Open gunicorn.service file

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

Copy this code, paste it in and save

[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 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

NGINX Setup

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

@  A Record  YOUR_IP_ADDRESS
www  CNAME  example.com

Go to local_settings.py on the server and change "ALLOWED_HOSTS" to include the domain

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

Edit /etc/nginx/sites-available/btre_project

server {
    listen: 80;
    server_name xxx.xxx.xxx.xxx example.com www.example.com;
}

Reload NGINX & Gunicorn

# sudo systemctl restart nginx
# sudo systemctl restart gunicorn
@kusiboamah

This comment has been minimized.

Copy link

@kusiboamah kusiboamah commented Jan 8, 2019

how do you have access to postgres database

@viseth89

This comment has been minimized.

Copy link

@viseth89 viseth89 commented Jan 13, 2019

try:
from .local_settings import *
except ImportError:
pass
Create a file called local_settings.py on your server along side of settings.py and add the following

SECRET_KEY
ALLOWED_HOSTS
DATABASES
DEBUG
EMAIL_*
Run Migrations

python manage.py makemigrations

python manage.py migrate

@fred4impact

This comment has been minimized.

Copy link

@fred4impact fred4impact commented Mar 4, 2019

thank you please i ran into an issues how do i correct that?
error
502 Bad Gateway
nginx/1.14.0 (Ubuntu) this is when i try to run the nginx to view the site

@TanmoyCtg

This comment has been minimized.

Copy link

@TanmoyCtg TanmoyCtg commented Mar 10, 2019

502 Bad Gateway. Any Help?

@raszidzie

This comment has been minimized.

Copy link

@raszidzie raszidzie commented Mar 11, 2019

502 Bad Gateway. Any Help?
Check this maybe you can find answer (https://stackoverflow.com/questions/55083484/django-502-while-deployment/55087671#55087671)

@Lurgic-error

This comment has been minimized.

Copy link

@Lurgic-error Lurgic-error commented Apr 18, 2019

how to i solve 502 bad gateway nginx 1.15.5(Ubuntu). This is what I get in the log files. [error] 5869#5869: *1 connect() to unix:/run/gunicorn.sock failed (111: Connection refused) while connecting to upstream

@glitchman23

This comment has been minimized.

Copy link

@glitchman23 glitchman23 commented May 15, 2019

● gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Tue 2019-05-14 21:44:58 UTC; 14s ago
Main PID: 945 (code=exited, status=1/FAILURE)

May 14 21:44:58 ubuntu1804-scf gunicorn[945]: self.stop()
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: File "/home/ariedi/pyapps/venv/lib/python3.6/site-packages/gunicorn/arbiter.py", line 393, in st
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: time.sleep(0.1)
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: File "/home/ariedi/pyapps/venv/lib/python3.6/site-packages/gunicorn/arbiter.py", line 245, in ha
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: self.reap_workers()
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: File "/home/ariedi/pyapps/venv/lib/python3.6/site-packages/gunicorn/arbiter.py", line 525, in re
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: raise HaltServer(reason, self.WORKER_BOOT_ERROR)
May 14 21:44:58 ubuntu1804-scf gunicorn[945]: gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>
May 14 21:44:58 ubuntu1804-scf systemd[1]: gunicorn.service: Main process exited, code=exited, status=1/FAILURE
May 14 21:44:58 ubuntu1804-scf systemd[1]: gunicorn.service: Failed with result 'exit-code'.

@feedmepy

This comment has been minimized.

Copy link

@feedmepy feedmepy commented Jul 27, 2019

This was a very helpful tutorial for me and I enjoyed every minute of it. Everything was straight forward and easy to understand.
I hope to see from you more tutorials on Python and Django. Keep up the good work.

@buddhikalb

This comment has been minimized.

Copy link

@buddhikalb buddhikalb commented Aug 19, 2019

I uploaded my project to github and linked to my local ubuntu pc. and this is how I got the project downloaded to my ubuntu pc. My folder structure for Djandoadmin user is;
Home --> pyapps
| - venv
| |- .....
| | - .....
|---btre_project
|--- accounts
|--- btre
|--- contacts
|--- accounts
|--- listings
|--- pages
|--- realtors
|--- static
|--- templates
|--- manage.py

  1. I can activate the ven from pyapps folder
  2. But I can't runserver from pyapps folder, instead I have to CD into btre_project to runserver (because that is where my manage.py is)
  3. Inside btre_project folder, i can run runserver command and can open website from webbrowser (localhost:8000 works)
  4. After that if I stop it and run guincorn (CD to btre_project folder and from there), it won't work I get the below error instead.
    djangoadmin@ubuntu:/pyapps/btre_project$ ls
    accounts contacts manage.py realtors static
    btre listings pages requirements.txt templates
    djangoadmin@ubuntu:
    /pyapps/btre_project$ gunicorn --bind 0.0.0.0:8000 btre.wsgi
    [2019-08-19 10:19:18 +0000] [10598] [INFO] Starting gunicorn 19.9.0
    [2019-08-19 10:19:18 +0000] [10598] [INFO] Listening at: http://0.0.0.0:8000 (10598)
    [2019-08-19 10:19:18 +0000] [10598] [INFO] Using worker: sync
    [2019-08-19 10:19:18 +0000] [10602] [INFO] Booting worker with pid: 10602
    [2019-08-19 10:19:18 +0000] [10602] [ERROR] Exception in worker process
    Traceback (most recent call last):
    File "/usr/lib/python2.7/dist-packages/gunicorn/arbiter.py", line 583, in spawn_worker
    worker.init_process()
    File "/usr/lib/python2.7/dist-packages/gunicorn/workers/base.py", line 129, in init_process
    self.load_wsgi()
    File "/usr/lib/python2.7/dist-packages/gunicorn/workers/base.py", line 138, in load_wsgi
    self.wsgi = self.app.wsgi()
    File "/usr/lib/python2.7/dist-packages/gunicorn/app/base.py", line 67, in wsgi
    self.callable = self.load()
    File "/usr/lib/python2.7/dist-packages/gunicorn/app/wsgiapp.py", line 52, in load
    return self.load_wsgiapp()
    File "/usr/lib/python2.7/dist-packages/gunicorn/app/wsgiapp.py", line 41, in load_wsgiapp
    return util.import_app(self.app_uri)
    File "/usr/lib/python2.7/dist-packages/gunicorn/util.py", line 375, in import_app
    import(module)
    File "/home/djangoadmin/pyapps/btre_project/btre/wsgi.py", line 12, in
    from django.core.wsgi import get_wsgi_application
    ImportError: No module named django.core.wsgi
    [2019-08-19 10:19:18 +0000] [10602] [INFO] Worker exiting (pid: 10602)
    [2019-08-19 10:19:18 +0000] [10598] [INFO] Shutting down: Master
    [2019-08-19 10:19:18 +0000] [10598] [INFO] Reason: Worker failed to boot.
@maria-mcdorman

This comment has been minimized.

Copy link

@maria-mcdorman maria-mcdorman commented Aug 19, 2019

@omerzaa79

This comment has been minimized.

Copy link

@omerzaa79 omerzaa79 commented Sep 5, 2019

"python manage.py runserver 0.0.0.0:8000" runs but when i go to my ip it does not work???

@chakram341

This comment has been minimized.

Copy link

@chakram341 chakram341 commented Oct 18, 2019

Please log into your droplet with SSH to configure the LAMP installation

after sudo Nginx restart these errors coming can anyone please help me. thanks

@lonestarx1

This comment has been minimized.

Copy link

@lonestarx1 lonestarx1 commented Nov 5, 2019

Thanks a lot Brad!
this course helped me a lot

@anapotter

This comment has been minimized.

Copy link

@anapotter anapotter commented Dec 2, 2019

Same problem as fewer other people have, 502 bad gateway error:(

@HarshSonawane

This comment has been minimized.

Copy link

@HarshSonawane HarshSonawane commented Dec 6, 2019

i have problem in serving django admin static file on digitalocean nginx server please suggest changes in nginx configuration file.....

@summonerriftofficial

This comment has been minimized.

Copy link

@summonerriftofficial summonerriftofficial commented Jan 14, 2020

for all who got 502 error make sure the followingWorkingDirectory=/home/djangoadmin/pyapps/btre_project
ExecStart=/home/djangoadmin/pyapps/venv/bin/gunicorn
--access-logfile -
--workers 3
--bind unix:/run/gunicorn.sock
btre.wsgi:application

don't forget to change this :P (btre) to django project app name

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Jan 15, 2020

for beginners this is very useful.... thanks for your handwork i really appreciate that... @bradtraversy

@anilbhattaraitoronto

This comment has been minimized.

Copy link

@anilbhattaraitoronto anilbhattaraitoronto commented Jan 25, 2020

This guide is truly helpful. The tricky part is getting the paths to projects right. The rest is pretty clearly stated here. Thank you Brad for coming up with this very clear, non-nonsense guide.

@pingyangtiaer

This comment has been minimized.

Copy link

@pingyangtiaer pingyangtiaer commented Jan 25, 2020

Will you able to provide a docker-compose yaml for the deployment as a containerized application?

@yickson

This comment has been minimized.

Copy link

@yickson yickson commented Feb 9, 2020

502 bad gateway

server {
listen 80;
server_name your_droplet_ip:8000 www.your_droplet_ip:8000;

root /var/www/html;

 location / {
         proxy_pass http://your_droplet_ip:8000;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header Host $http_host;
         proxy_set_header X-NginX-Proxy true;

          }
 }
@citysiva180

This comment has been minimized.

Copy link

@citysiva180 citysiva180 commented Apr 1, 2020

Hi, I am getting the below error,
Permission denied (publickey)
Please help in this regard.

@cschweipert

This comment has been minimized.

Copy link

@cschweipert cschweipert commented Apr 6, 2020

Try to run this

ssh-add ./.ssh/id_rsa_do

and then try to login again.

@reyesvicente

This comment has been minimized.

Copy link

@reyesvicente reyesvicente commented Apr 23, 2020

Why am I getting client_loop: send disconnect: Broken pipe?

@basherahmadahsas

This comment has been minimized.

Copy link

@basherahmadahsas basherahmadahsas commented May 20, 2020

Hi, tnx for the great tutorial, i deployed my application but i am getting data error while saving non ASCII data.
this is the error i get:
DataError at /admin/faculty/faculty/2/change/
character with byte sequence 0xd8 0xad in encoding "UTF8" has no equivalent in encoding "LATIN1"

@ThemisTheo1995

This comment has been minimized.

Copy link

@ThemisTheo1995 ThemisTheo1995 commented May 31, 2020

Hello,
Everything works perfectly apart from the fact that my main '12XX.X1.23X.' is loading but all the other pages are getting the server error 500. Sometimes my about pages loading and others crash with the same error. Any thoughts anyone?

@abdirashidabdi

This comment has been minimized.

Copy link

@abdirashidabdi abdirashidabdi commented Jun 4, 2020

I think you should run this command python manage.py collectstatic

@ThemisTheo1995

This comment has been minimized.

Copy link

@ThemisTheo1995 ThemisTheo1995 commented Jun 4, 2020

I think you should run this command python manage.py collectstatic

I deleted the droplet made it again from scratch and works. Possibly I made spelling mistake in gunicorn or nginx nano txts. Thanks!

@Elvis2131

This comment has been minimized.

Copy link

@Elvis2131 Elvis2131 commented Jun 18, 2020

For anyone having issue with 502 nginx error. I had to include the venv/project in the working directory in guinicorn.service file.
End result: WorkingDirectory=/home/elvis/python-apps/venv/real_estate

@aggreyfynnm

This comment has been minimized.

Copy link

@aggreyfynnm aggreyfynnm commented Aug 10, 2020

Thank you very much Brad. My website was working correctly with my IP but after some days i did some changes and did a git pull but the changes are not showing on the site. i have cleared my cache and cookies. Please i need help

@hg242322

This comment has been minimized.

Copy link

@hg242322 hg242322 commented Aug 27, 2020

Why am I getting client_loop: send disconnect: Broken pipe?

thank you please i ran into an issues how do i correct that?
error
502 Bad Gateway
nginx/1.14.0 (Ubuntu) this is when i try to run the nginx to view the site

I am also having the same error

@liyi54

This comment has been minimized.

Copy link

@liyi54 liyi54 commented Sep 1, 2020

Thank you for the guide @brad. I am trying to do a deployment to Heroku, do you have links to any resources that could help?

@kashifali554

This comment has been minimized.

Copy link

@kashifali554 kashifali554 commented Sep 9, 2020

Hey Brad,

I finished the Nginx process fully with all the caution while changing the project name and everything. All the tests and everything passed but when I tried to access my website. It gave me a 502 bad gateway error. After looking around for the error solution. I didn't find anything helpful.
I tried recreating the process nginx process.

When I enter this command.

sudo systemctl restart nginx

The error says.

Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.

I am stuck here. Previously, I completed the full task but at the end got the 502 bad gateway error.

@synergit

This comment has been minimized.

Copy link

@synergit synergit commented Nov 11, 2020

Completing the command list for the scenario that after Database is migrated, how to make server pick up the changes. Reference is here:

# restart gunicorn
sudo service gunicorn restart 
# restart nginx
sudo service nginx restart
@barabara-philipo

This comment has been minimized.

Copy link

@barabara-philipo 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?

@Saketh143

This comment has been minimized.

Copy link

@Saketh143 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:

Code:

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

[Service]
User=djangoadmin ------------->change this to your username .
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

@Usman2684

This comment has been minimized.

Copy link

@Usman2684 Usman2684 commented Jan 21, 2021

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, http://167.172.22.216/

@cybernamix

This comment has been minimized.

Copy link

@cybernamix cybernamix commented Jan 23, 2021

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

@wilgens7

This comment has been minimized.

Copy link

@wilgens7 wilgens7 commented Mar 26, 2021

Got rid of the Bad Gateway error using the advice on this link: https://www.digitalocean.com/community/questions/gunicorn-service-no-such-file-or-directory

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!"

@Iamkosgei

This comment has been minimized.

Copy link

@Iamkosgei Iamkosgei commented Apr 1, 2021

@stacytonui check this out.

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