Learn how to write your own framework in Python!

You'll learn how to develop your own Python web framework to see how all the magic works beneath the scenes in Flask, Django, and the other Python-based web frameworks.

Jahongir Rahmonov

I'm a Software Engineer at Delivery Hero. Avid reader. WIUT graduate. Blogger and an amateur speaker.

I write about Python, Django, Kubernetes and sometimes something non-technical.

Welcome to my corner

Tue 21 March 2017

Deploy a Django app to Digital Ocean

In the previousblog posts, we learned how to run a django app with Nginx, Gunicorn and Supervisord. Now, let's make a django app available to everybody by deploying it to a DigitalOcean server.

You will need a DigitalOcean account to follow along.

For this tutorial, I have prepared a sample django app in order to simulate a real scenario. It is just a fun app which shows Donald Trump with his random quotes which can even be personalized. The app makes use of the whatdoestrumpthink API. Please note that we are going to do almost the same stuff that we did in the previous tutorials except that we will use a real server.

Let's get started!

Step I (creating a VPS)

Go to https://cloud.digitalocean.com/droplets and click on Create Droplet button. Then, select Ubuntu 16.04:

Select a server:

Select a region:

Then, preferably add you ssh key and name your server:

And click on Create. After a while, you will see that your server has been created:

Now, copy the IP address of the newly created server and ssh in:

ssh root@104.236.57.112

Replace that IP address with your own. Welcome in! You should see something like this:

Step II (installing system-wide dependencies)

First, let's update and upgrade the packages:

apt-get update
apt-get -y upgrade

Install nginx:

apt-get install -y nginx

Install supervisor:

apt-get install -y supervisor

Install python virtualenv:

apt-get install -y python-virtualenv

Install postgresql:

apt-get install -y postgresql postgresql-contrib

Step III (configuring database)

Switch to the postgres user:

sudo su - postgres

Type this to go to the postgres interactive shell:

psql

Create a database:

postgres=# CREATE DATABASE djtrumpprod;

Create a user:

postgres=# CREATE USER djtrumpuser WITH password 'djtrump';

Give this new user an access to administer the new database:

postgres=# GRANT ALL PRIVILEGES ON DATABASE djtrumpprod TO djtrumpuser;

Quit from the shell and switch back to the root user:

postgres=# \q
postgres@djtrump:~$ exit

Step IV (setting up our project and its environment)

Clone our sample app:

git clone https://github.com/rahmonov/djtrump.git

Create and activate a virtual environment with python3.5 (not critical to use python3.5 though):

virtualenv djtrumpenv --python=$(which python3.5)
source djtrumpenv/bin/active

Now, your prompt will show that you are operating under a Python virtual environment:

(djtrumpenv) root@djtrump:~#

Go ahead and install dependencies:

cd djtrump
pip install -r requirements.txt

Now we should migrate but there is one more thing that we need to do before that. If you go to the settings folder, there are two files: base.py and prod.py. Basically, base.py contains all the configurations and prod.py overrides those needed in the production environment. For example, DATABASES config is overridden in prod.py. That's why, we need to tell our environment to use this prod.py and not the default base.py. This is done by setting DJANGO_SETTINGS_MODULE env variable to prod.py path. Open ~/.bash_profile and add this:

export DJANGO_SETTINGS_MODULE=djtrump.settings.prod

Save and quit. Then, source this file for our changes to take effect:

source ~/.bash_profile

Now, try to migrate. Most probably, it will fail and say something like this:

FATAL:  Peer authentication failed for user "djtrumpuser"

That's because, postgresl uses peer authentication by default, which is it will succeed if the user with the same name as the postgres user uses it. In our case, there is no djtrumpuser user in postgres and thus it fails. To fix it, go to /etc/postgresql/9.5/main/pg_hba.conf and change the line that says this:

local   all     all      peer

to this:

local   all     all      md5

Save and quit. This way, postgres will try to use password to authenticate the user. Now, restart postgresql for our changes to take effect:

sudo service postgresl restart

Go ahead and migrate:

python manage.py migrate

It works now. Cool! Try to run the development server and it will work.

Step V (configuring nginx)

Create a new file: /etc/nginx/sites-available/djtrump and add the following:

server {
    listen 80;
    server_name your_ip;

    location = /favicon.ico { access_log off; log_not_found off; }

    location /static/ {
            alias /root/djtrump/static/;
    }

    location / {
            include proxy_params;
            proxy_pass http://your_ip:8030;
    }
}

Replace your_ip with the IP address of your server. We know what this is doing from the previous tutorials. Basically, it is serving the static files from /root/djtrump/static/ and redirecting http requests to gunicorn which should be running on port 8030.

Now, let's enable this file by linking it to the sites-enabled folder:

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

Restart nginx:

sudo service nginx restart

There are two more things that we need to do before nginx works. First, we need to put all our static files in the folder /root/djtrump/static/ and run gunicorn on port 8030 as we promised in nginx config file.

First, run this to gather all static files in that folder:

python manage.py collectstatic --noinput

Now, run gunicorn:

gunicorn --workers 3 --bind 0.0.0.0:8030 djtrump.wsgi

Go ahead and type in the browser the IP of your address. You will see that the app is running. Congratulations!

Please note that if you cloned the app to the user's home directory, you may face issues with static files (Permission denied error). One of the ways to solve it to run nginx as root. To do that, open /etc/nginx/nginx.conf and change the line that says:

user www-data;

to this:

user root;

and restart the nginx:

sudo service nginx restart

Great! You will now see the pleasant face of Donald Trump and a random quote of his:

Step VI (configuring supervisor)

Create /etc/supervisor/conf.d/djtrump.conf and type in the following:

[program:djtrump]
command=/root/djtrumpenv/bin/gunicorn --workers 3 --bind 0.0.0.0:8030 djtrump.wsgi
directory=/root/djtrump
autostart=true
autorestart=true
stderr_logfile=/var/log/djtrump.err.log
stdout_logfile=/var/log/djtrump.out.log

Restart, reread and update the supervisor:

sudo service supervisor restart
sudo supervisorctl reread
sudo supervisorctl update

Now, you can stop, start and restart your app easily! Try this:

sudo supervisorctl stop djtrump

If you go to the app in the browser, it will respond with 502 (Bad Gateway) response. Go ahead and start it:

sudo supervisorctl start djtrump

Go to the app and you will see it working!

Well, this is pretty much it! Congratulations, your django app is now live and available to everybody!

In the next tutorials, we will introduce ourselves to the world of CI and CD (Continuous Integration and Continuous Delivery).

Fight on!

P.S. If you want to make the style of this app better, please send a PR. I would love some help on CSS side or any other side for that matter.

Send
Share

If you liked what you read, subscribe below. Once in a while, I will send you a list of my new posts.