Setting up a simple django webserver on a new raspberry pi

In order to make things slightly faster for my blog, I acquired a new raspberry pi. Prior to this, I was using a generation 2 model B. To be honest it was working just fine. The problem here is that the hardware of that little guy was not powerful enough to host all the services without some optimizations. Making things faster was not the only reason that I bought the new pi. I also wanted to improve the architecture and resilience of my hosting. For example, I could now put the database in one server and the NGINX service in the other server.

In this post, I’ll comment on the modifications I did in my old raspberry to improve its performance. Then I will explain step by step how to get started and set up the server. The modifications are simple and they worked quite well. I wish I had done some real benchmark, but before my website was taking around 13~17s to load!!! Now it’s taking on average 300ms. Oh yeah!

Hardware and architecture modifications

The first modification was to get a decent power supply. An idiot stole my power supply which I was using for my bluetooth speakers (and also the speakers). So… I got a new power supply. I also added a heatsink and overclocked the little guy a tiny bit. To overclock you just type sudo raspi-config, go to option 6 (Overclock) and select how much you want. I’m using the moderate option. After that I also reduced the memory allocated for GPU since I was only using my server via terminal. You can also do this by using the raspi-config menu in “Advanced Options” > “Split memory”. I’m using 32M.

You can find heatsinks for the processor and gpu of the PI fairly easy on electronic stores (adafruit, canakit), ebay, amazon if you don’t have them already. You can also do this same thing for your new rasp pi. As I mentioned in the beginning the old rasp pi is now responsible only for the database and storage, while the new is going to be responsible for processing requests.

I also recommend getting an SD card that has a good read and write speed. I got an SD card that was UHS-I (Ultra High Speed). As you get higher throughput, the more expensive per storage your device will be.

The new raspberry. \o/

To start the play I had to download flash and install the new raspbian image available which is Debian Stretch. You can download the image here.

I used the recommended tool by raspberry called Etcher. What a life changing tool. I’ve lost one SD card previously using DD. It was not fun. With Etcher things are super simple and it works quite well.

Next, I had to configure the setup of the server and here it comes the fun part! I will describe each step and give the command to execute the step. The default username and password are pi and raspberry respectively. Log in with this credential. * Change your password with passwd * Add a new user now (without the <>) sudo adduser <new_user> * Grant sudo access to this user. After typing sudo visudo, you can copy the line with ROOT ALL:(ALL) replacing root with your new user. * Add internet: if you are not connected via ethernet cable and if you are using the builtin wifi or a dongle, type sudo nano /etc/wpa_suplicant/wpa_supplicant.conf and add your wifi configuration like this:

network={
    ssid="My Wifi"
    psk="My secret"
}
  • Before rebooting, change your hostname, localization option and interfacing option (enable SSH). We can do that by typing sudo raspi-config.
  • Reboot with sudo reboot

Log in again and you can see your ip straightforward. If not, type ifconfig | grep inet and voila.

Setting up basic NGINX and uWSGI

Now, I will setup a server using NGINX, uWSGI for Django.

First, we install NGINX

sudo apt-get install nginx

Then we install pip3 which doesn’t come with this image.

sudo apt-get install python3-pip

Before we proceed we have to add our ~/.local/bin (the tilde represents $HOME) to our $PATH which is not added by default either.

export PATH=${PATH}:$HOME/.local/bin

Let’s first upgrade pip

pip3 install -U pip

We need to have uwsgi on the global scope.

pip install uwsgi

Now we install virtualenv which will sand box our webserver to other environments.

pip install virtualenv --user

After virtualenv was installed, we can create our server which we will name myproject.

sudo mkdir /var/www/myproject
cd /var/www/myproject
virtualenv .
source bin/active

You will notice now you have (myproject) in your bash. This means you are in the virtual environment. Now all packages you install with pip will go to your /var/www/myproject/lib/python3.x/dist-packages/. Let’s install Django in our sandbox environment and create our project

pip install django
django-admin startproject myproject

This should create your project. Grab your local ip:

ifconfig | grep inet

Let’s test our server with

./myproject/manage.py runserver 0.0.0.0:8000

With a different device connected to the same intranet, visit your ip:8000. Example: http://192.168.1.110:8000/ and you will see the django welcome page.

Diving deeper into uwsgi

Part of this session is based upon a post a saw a while ago on digital ocean which has many great tutorials.

I first added my websites’ configuration to a folder. The configuration for each site is in a *.ini file. Create the folder in /etc/uwsgi/sites. Add the ini with sudo nano myproject.init

[uwsgi]
project         = myproject
base            = /var/www/%(project)

# Django-related settings
# the base directory (full path)
chdir           = %(base)
# Django's wsgi file
module          = myproject.wsgi:application
# the virtualenv (full path)
home            = %(base)

# process-related settings
# master
master          = true
# maximum number of worker processes
processes       = 2
max-worker-lifetime = 300
# the socket (use the full path to be safe)
socket          = %(base)/uwsgi/project.sock
# ... with appropriate permissions - may be needed
chmod-socket    = 664
uid             = www-data
gid             = www-data
chown-socket    = www-data:www-data
# clear environment on exit
vacuum          = true
# logging
logto           = %(base)/uwsgi/logs.txt
# disable logging to speed the process after everything is up and runnig. It doesn't really disable the log, but reduces the info that is logged
# disable-logging = true
log-5xx         = true

Make sure you create the folder that contains the logs (%(base)/uwsgi/) and create the file as well touch /var/www/myproject/uwsgi/logs.txt. You can test if your configuration is correct with uwsgi --ini /etc/uwsgi/sites/myproject.ini. You can read more in their docs Now we want to add a service in /etc/systemd/system/uwsgi.service

[Unit]
Description=uWSGI Emperor service

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown www-data:www-data /run/uwsgi'
ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all

[Install]
WantedBy=multi-user.target

Nginx

Let’s configure quickly our nginx to proxy our server. I like to use some boilerplates to help me configure my server. I use this project to help me out. You can clone it with git clone https://github.com/h5bp/server-configs-nginx.git

First do a backup of your nginx.conf file with sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.backup.conf

I like to enable gzip to compress my files with nginx. You can see how they do with the boilerplate and copy as you wish. They are well commented.

Then, you can add your site in the folder sites-available with sudo nano yourproject

# add this block to allow redirections from port 80 to port 443
server {
    listen 80;
    listen [::]:80;
    server_name yourproject.com
    return 301 https://yourproject.com$request_uri;
}
server {
    server_name yourproject.com 
    root /var/www/myproject/;

    # here you should take a look on the location folder of the boilerplate to add the cache-control header
    # h5bp/location/expires.conf
    location / {
        try_files $uri /index.html;
        include uwsgi_params;
        uwsgi_pass unix:/var/www/myproject/myproject/uwsgi/myproject.sock;
    }
    location /static {
        alias /var/www/myproject/myproject/static;
        # add_header Cache-Control "max-age=2592000";
        access_log off;
    }
    # I like to include these files
    # include h5bp/directive-only/x-ua-compatible.conf;
    # include h5bp/location/protect-system-files.conf;
}

We can now create a symbolic link to the folder sites-enabled

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

We can now sudo nginx -s reload and our server should be up and running.

Let me know if all went all 😉 Cheers!


Leave a Reply

Your email address will not be published. Required fields are marked *