uWSGI, Django & Nginx

It's a pain. Here's a quick run down of what I did so that I can remember to do it right the next time and not waste hours and hours configuring an app.

Best to create a new user and delete the password later.

Create a user

sudo adduser django  
# set a password here and then delete it
sudo passwd -d django  

That way we can isolate the permissions and contain an attack if need be. Anyway, onward.

Create an environment

Python3 is the way to go. Chances are, pip isn't installed. So first install setup-tools and then install pip

sudo apt install python3-setuptools  
sudo easy_install3 pip  
sudo pip install virtualenvwrapper

echo "export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3" >>  
~/.bashrc
echo "export WORKON_HOME=~/Env" >> ~/.bashrc  
echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc  
source ~/.bashrc

mkvirtualenv project --python=`which python3`  

We'll be using the environment in ~/Env and uWSGI will be pointing to that.

Configure uWSGI

Install uWSGI globally.

sudo pip3 install uwsgi

Create the uWSGI conf file at /etc/uwsgi/sites/project.ini

[uwsgi]
project = project_name  
uid = django  
base = /home/%(uid)

chdir = %(base)/%(project)  
home = %(base)/Env/%(project)  
env=STAGING=True  
module = %(project).wsgi:application

master = true  
processes = 5

socket = /run/uwsgi/%(project).sock  
chown-socket = %(uid):www-data  
chmod-socket = 660  
vacuum = true  
logto = /var/log/uwsgi/uwsgi.log  

NOTE: module points to wsgi file. So its' assumed that the wsgi file is inside project_name package.

And create a service so that systemctl can recognize it

[Unit]
Description=uWSGI Emperor service

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown django: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  

That should take care of the socket that'll be used to run the app and host for the reverse proxy (nginx). It should run it in emperor mode which will spawn vassals and they should handle the incoming requests. Anyway, next step, configure nginx. I hate this part, but the good thing is, it's pretty straight forward.

Nginx

sudo apt install nginx  
sudo touch /etc/nginx/sites-available/project_name  
sudo ln -s /etc/nginx/sites-available/project_name /etc/nginx/sites-enabled/.  

And of course project conf is simply

server {  
    listen 80;
    server_name site_name;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        alias /var/www/static/;
    }
    location /media/ {
        alias /var/www/media/;
    }

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/run/uwsgi/project_name.sock;
    }
}

Of course the path to the app is actually /home/django/project_name. Just need to chown it

sudo chown -R django:www-data /home/django/project_name.

Yeah, that does it. I should probably create an ansible playbook for this, but that's a lot of work for now.