Building and deploying a personal site with blog. Part 2

Published 2018-07-21 on Yaroslav's weblog

Welcome back, this post is a continuation of the previous post in which I wrote about how to initialize and setup a Django project for a personal site with blog locally. Now we will be focusing on the juicy part, getting your site deployed so that everyone on the interwebz can get to see your nice new dank memes in your very own site! the fruits of your sweat and blood.

You might want to read part one

Part II: Deploying your site

I will assume that you already have bought a VPS or dedicated server in any of the many hosting services available online, or maybe if you are cool enough, might even have a machine of your own with a properly setup static IP address. Note that if you are using shared hosting, the deployment process is usually already automatized if they have support for Python and Django, if they don't support Python and Django, you need to get another hosting solution.

If you still don't have any kind of hosting, and you are looking for a nice and comfy VPS for your projects, I would recommend using DigitalOcean, you can get a VPS powerful enough for Django and other Python web projects for as low as $5. You can follow this referral link if you have decided to try DigitalOcean, it will give a $10 credit for your first account, enough for two whole months on their most inexpensive VPS.

Once you have your server set and ready to go, we can continue with the fun part. Deploying your site. I will be assuming that you have a Debian-based distro in your server.

First steps

First let's make sure that our server is up to date

$ sudo apt-get update
$ sudo apt-get upgrade

Next, we need to install all the needed packages

$ sudo apt-get install python3 python3-pip python-virtualenv postgresql nginx supervisor

You can set the virtual environment with your login user, or create another user exclusively to run your Django project and other related stuff.

You can add a user to run the app like so

$ adduser webuser

Let's go ahead and create a directory for virtual environments and create the environment for our project

$ virtualenv mysite -p python3

Now we source the environment

$ source mysite/bin/activate

And use pip to install the necessary modules

$ pip install Django Pillow psycopg2 django-summernote w3blog

After that, we start and enable postgresql

$ sudo systemctl start postgresql
$ sudo systemctl enable postgresql

Login to the postgres shell, set the password for user postgres, and create the database for our project

$ sudo su - postgres
$ psql
postgres=# \password
postgres=# create database mysitedb;

In this case, I am using the postgres user directly since I am using this server just for myself, and I only use PSQL for my blog. However, if you host multiple projects, and/or multiple users have a more direct access to postgres, you might want to create separate users and roles for each database or group of databases.

Copying your Django project to the server

One way to copy your project files to the server would be using scp, for example

scp -r /path/to/project user@serveripordomain:/home/webuser/

Or if you are already hosting your project as a git repository in a service like GitHub or BitBucket, you could clone directly into you repository in the home folder, like so

$ git clone https:/github.com/user/repo

After the files have been copied, we need to set some things up for our Django project. Before running this commads, you might need to export the needed environmental variables, if you set up your Django settings using environmental variables as I wrote in the first part. Otherwise, you might want to edit your settings.py file now with the correct settings.

First we collect static files

$ ./manage.py collectstatic

Then, we migrate our models to the database

$ ./manage.py migrate

And after migrating we create a superuser for the Django admin

$ ./manage.py createsuperuser

If you get an authentication error while performing any of the above commands, you will have to edit the following file

$ sudoedit /etc/postgresql/9.1/main/pg_hba.conf

Change this line

local        all        postgres        peer

So that it looks like this

local        all        postgres        md5

Configuring the server

I will be assuming that you are going to use nginx as a proxy server for Django. There are other alternatives such as Apache, but I found nginx to be a really powerful and easy to setup server.

First, while in our virtual environment, we will install gunicorn. We might have been using Django's own development server while building and testing our app locally, but that's what it is, a development server. It is not mean for production, so we will use gunicorn for that

pip install gunicorn

Now we'll cd into our env's bin folder and create a file called gunicorn_start, and add the following lines to it:

#!/bin/bash
NAME="mysite"
# Our site's directory
DIR=/home/webuser/mysite
USER=webuser
GROUP=webuser
WORKERS=3
BIND=127.0.0.1:8001
DJANGO_SETTINGS_MODULE=mysite.settings
DJANGO_WSGI_MODULE=mysite.wsgi
LOG_LEVEL=error

cd $DIR
source /home/webuser/venvs/mysite/bin/activate

export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DIR:$PYTHONPATH

# This are the environment variables needed for your Django settings
# (see the first part of this guide)
export SITE_SECRETKEY="YOURSECRETKEYGOESHERE"
export SITE_DEBUG="false"
export SITE_DBPASSWORD="YOURPOSTGRESPWDGOESHERE"
export SITE_DBUSER="postgres"
export SITE_HOST="localhost"
export SITE_PORT="5342"

exec /home/webuser/venvs/mysite/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $WORKERS \
  --user=$USER \
  --group=$GROUP \
  --bind=$BIND \
  --log-level=$LOG_LEVEL \
  --log-file=-

And make it executable

$ chmod a+x gunicorn_start

That's almost it, but before actually running gunicorn, we will install one more program which will take care of the gunicorn server, so that if for any reason we need to reboot our server, it will restart gunicorn for us. It needs to be some kind of supervisor. Well why my dear friend, we are going to use Supervisor.

But first, let's create a folder name logs inside our webuser's home folder, and create a file to be used to log any application errors:

$ mkdir logs
$ touch logs/gunicorn.log

Now we create a new Supervisor config file:

$ sudoedit /etc/supervisor/conf.d/mysite.conf

Add the following:

[program:mysite]
command=/home/webuser/venvs/mysite/bin/gunicorn_start
user=webuser
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/webuser/logs/gunicorn.log

We reread the Supervisor configuration files, and start the program:

$ sudo supervisorctl reread
$ sudo supervisorctl update

And check the status to make sure it is running fine and dandy

$ sudo supervisorctl status mysite

Next we should configure nginx, and for that we are going to make the configuration file for our site. Open it up

$ sudoedit /etc/nginx/sites-available/mysite

And add the following lines

server {
        listen 80;
        listen [::]:80;

        server_name www.example.com;

        location /static/ {
                alias /home/webuser/mysite/static/;
        }

        location /media/ {
                alias /home/webuser/mysite/media/;
        }

        location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://127.0.0.1:8001;
        }
}

I won't be explaining in this guide how to setup an SSL certificate, since it is out of its scope, but, if you want to get a free certificate, I cant point you to letsencrypt.com. It is backed by many of the top internet organizations. If you already have an SSL certificate, and would like to use HTTPS, your file should look like this

server {
        listen 80;
        listen [::]:80;
        server_name www.example.com;
        return 301 https://$host$request_uri;
}

server {
        listen 443 ssl;

        server_name www.example.com;
        ssl_certificate /path/to/cert/fullchain.pem;
        ssl_certificate_key /path/to/key/privkey.pem;
        location /static/ {
                alias /home/webuser/mysite/static/;
        }
        location /media/ {
                alias /home/webuser/mysite/media/;
        }
        location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://127.0.0.1:8001;
        }
}

After creating the needed config file, we create a symbolic link to it in sites-enabled

$ sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

Remove the default nginx site

$ sudo rm /etc/nginx/sites-enabled/default

Finally, we restart nginx

$ sudo service nginx restart

Et voila!

It's alive!

Final thoughts

This was by no means the most comprehensive guide to Django, but it might help you get started on setting up your own projects, and most importantly, actually deploying them to a live server.

© 2018—2024 Yaroslav de la Peña Smirnov.