Django Setup Notes – Ubuntu, Nginx, uWSGI

muppetlabslogo

Notes for getting Django running on Ubuntu 16.04… with Nginx, Supervisord, uWSGI in emperor mode and a bit of Postgres.

Start by setting up your Ubuntu environment.

Newish Python 3.6

add-apt-repository ppa:fkrull/deadsnakes
apt-get update
apt-get install python3.6
apt-get install python3.6-dev
apt-get install python-virtualenv

Postgres

apt-get install postgresql postgresql-contrib
su - postgres
createdb myapp
createuser -P

You may need to dump a database and import it again…

pg_dump -U myuser myapp > myapp.db
psql myapp < myapp.db

SSL Cert Setup

Grab a free SSL cert…

service nginx stop
apt-get install letsencrypt
letsencrypt certonly --standalone -d www.myapp.com -d myapp.com
service nginx start

Nginx

apt-get install nginx

This should give you a nice and secure HTTPS setup which also supports HTTP2…

/etc/nginx/sites-available/myapp.com


server {
server_name www.myapp.com;
listen 443 ssl http2;
charset utf-8;
client_max_body_size 75M;

ssl on;
ssl_certificate /etc/letsencrypt/live/www.myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.myapp.com/privkey.pem;

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers On;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

ssl_session_cache shared:SSL:128m;

add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Xss-Protection "1";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' *.google-analytics.com";
add_header Referrer-Policy origin-when-cross-origin;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;

access_log on;

location /static {
alias /var/www/www.myapp.com/api/static;
}

location / {
uwsgi_pass unix:///var/www/www.myapp.com/api.sock;
include /var/www/www.myapp.com/uwsgi_params;
}
}

/var/www/myapp/uwsgi_params


uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
uwsgi_param UWSGI_SCHEME $scheme;

Django Virtual Environment

cd /var/www
mkdir www.myapp.com
virtualenv -p /usr/bin/python3.6 env

cd /var/www/www.myapp.com
source env/bin/activate

git clone https://github.com/myco/myapp.git api

Note: my Django app name is “api” here.

pip install -r api/requirements.txt

pip install Django
pip install uwsgi

Install uWSGI System-wide

Using the latest Python, install uWSGI in /usr/local/bin…

Make sure you are not in a virtual environment or run “deactivate”.


python3.6 -m pip install --upgrade pip
python3.6 -m pip install uwsgi

Supervisord to Manage uWSGI in Emperor mode

apt-get install supervisor

/etc/supervisor/conf.d/uwsgi.conf


[program:uwsgi]
command=/usr/local/bin/uwsgi --emperor /etc/uwsgi/apps-enabled
autostart=true
autorestart=true
redirect_stderr=true
stopsignal=QUIT
stdout_logfile=/var/log/uwsgi.log

Setup /etc/uwsgi

cd /etc
mkdir uwsgi
cd uwsgi
mkdir apps-available
mkdir apps-enabled
cd apps-available

/etc/uwsgi/apps-available/myapp.ini

[uwsgi]
uwsgi-socket = /var/www/myapp/api.sock
pythonpath = /var/www/myapp/api
virtualenv = /var/www/myapp/env
processes = 2
reload-on-rss = 150
module = api.wsgi
uid = www-data
gid = www-data


cd /etc/uwsgi/apps-enabled
ln -s ../apps-available/myapp.ini
service supervisor restart

You can now also edit / touch your myapp.ini to make uwsgi restart the worker processes.

Done. Test your setup.

Go make some tea and start coding.

More Notes

Let me know if something above can be done better.

Tune your Postgres database. Then su – postgres and run “pg_conftool show all” to see that your setting are up to date.

Tune your SSL setup.

You probably want 2x uwsgi processes for every CPU in your server.

Update: use pipenv

pip3 install pipenv
cd /project
pipenv –three –python /usr/bin/python3.6
pipenv shell

to use:

su – www-data (or user which runs uwsgi)
pipenv shell