> Plain-text rendering of a visual post, for machine consumers.
> Charts, diagrams and images are omitted; their captions are kept inline.
> Titles on collapsible asides are dropped, their contents kept.
> The complete version is at https://abhay.fyi/blog/post/ostrich-tips-launch-your-django-app-vps-15-mins/

# Launch your Django app on a VPS in 15 mins!

> This is a step by step guide to launch your Django app on a VPS in 15 mins! There are plenty of references and similar tutorials online and this is just one of many ways to configure things.

- Author: Abhay Kashyap (https://abhay.fyi/#person)
- Published: 2014-12-04
- Canonical: https://abhay.fyi/blog/post/ostrich-tips-launch-your-django-app-vps-15-mins/

---
## First things first!

Lately, my shared hosting was too slow for my taste and I decided to move to VPS. I chose [Digital Ocean](https://www.digitalocean.com/) since [GitHub Student Package](https://education.github.com/pack) gave me \$100 worth of credit. You will need a ".edu" account to get this credit. You could also get \$10 worth of credit if you sign up using this [referral link](https://www.digitalocean.com/?refcode=ea42c5c0fe58) (It will also help out a hungry student!).

There are several tutorials online to help with initial server setup. I particularly like the tutorials from DigitalOcean since even my tiny brain could understand it! To get started, you can follow this [initial server setup](https://www.digitalocean.com/community/tutorials/how-to-connect-to-your-droplet-with-ssh) (for Ubuntu 14.04). Make sure you complete all 3 tutorials in the series!

---

## Django with uWSGI & Nginx

There are numerous ways to deploy Django apps. uWSGI and Nginx is one such combination that is claimed to be reasonably fast. Nginx (pronounced engine-x) is a lightweight HTTP and reverse proxy server and WSGI (Web Server Gateway Interface) is a protocol for the interface between web servers and web applications. It is the standard for Django! uWSGI is an application server container that implements WSGI. More comprehensive tutorial [here](https://www.digitalocean.com/community/tutorials/how-to-deploy-python-wsgi-applications-using-uwsgi-web-server-with-nginx).

Too formal? In simple terms, when someone asks for a webpage from, lets say, mynewsite.com, there must be some program running on the server that will receive and process that request. Nginx is that program! It will patiently wait and listen to all incoming requests and promptly deliver pages/files it is asked to give. If there were only static files, Nginx would have been sufficient. But our HTML pages are generated by Django using data stored in some database! So in this case, Nginx will in turn have to ask Django for that page. This is where uWSGI comes into play. It will help Nginx and Django talk to each other.

### Optional Setup

For the database, it is recommended you move away from SQLite when in production. You could either use MySQL or the more popular PostgreSQL. They both come with a nice GUI. For MySQL, you could setup phpMyAdmin following this guide. For PostgreSQL, you could setup phpPgAdmin following [this guide](/blog/post/ostrich-tips-quick-setup-postgresql-phppgadmin-nginx/).

---

## 1. Installation

Lets install Nginx if we haven't already done so.

```bash
sudo apt-get install nginx
```

Now lets install uWSGI. We will use pip since it has updated versions when compared to debian. Also we will do a system wide install so deactivate your virtual environment if you're using it.

```bash
sudo pip install uwsgi
```

---

## 2. Setup uWSGI

At a bare minimum, uWSGI requires a python callable and an endpoint to take requests. Additional configuration can be supplied as command line arguments. A better way is to run uWSGI with a .ini file that has all our configurations. uWSGI comes with a neat little feature called the [emperor mode](http://uwsgi-docs.readthedocs.org/en/latest/Emperor.html). In this mode, you can tell the uWSGI Emperor process to watch a directory and it will automatically spawn new processes for every `.ini` file it sees. It will also restart a process if its `.ini` file changes. Pretty cool right?

If you installed uWSGI using debian (`apt-get`) then the uWSGI directory would be created at `/etc/uwsgi/`. If it is not there, lets create the following directories

```bash
sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/apps-available
sudo mkdir /etc/uwsgi/apps-enabled
```

We will configure the emperor to watch the apps-enabled directory. Create a file called `emperor.ini` in the `/etc/uwsgi/` directory with the following contents.

```bash
# /etc/uwsgi/emperor.ini
[uwsgi]
# This is the directory the emperor will watch for new .ini files
emperor = /etc/uwsgi/apps-enabled

# Set the uid and gid. This could be any user you wish
uid = www-data
gid = www-data

# Set the log file path.
# Make sure the log file has the required write
# permissions for the emperor process.
logto = /var/log/uwsgi/emperor.log
```

Now we will deploy the emperor process.

```bash
uwsgi --ini emperor.ini
```

If you want the process to run in the background,

```bash
uwsgi --ini emperor.ini &
```

You could also add the command to `rc.local` to make sure the script runs on startup.

_Common issue: The `.log` file either hasn't been created or doesn't have the right permissions. Make sure it does!_

---

## 3. Deploy the Django App

To deploy a Django project, lets first create its `.ini` file and add it to the `/etc/uwsgi/apps-available/` directory. The following shows the configuration for my Django project called Grub Club, a project to host all gourmet grub recipes!

```bash
# /etc/uwsgi/apps-available/grub_club.ini
[uwsgi]
# This should point to your Django project directory
chdir   = /home/ostrich/django/grub_club

# This should point to your Django wsgi file.
# This usually lives alongside your settings.py file
module  = grub_club.wsgi:application

# This should point to your python environment
# If you have a virtual environment you like to use,
# specify that here
home    = /home/ostrich/.virtualenvs/django_env

master  = true

# This is the number of cores on your server
processes = 1

# You can either connect using HTTP OR
# you can use unix sockets (recommended)
socket  = /tmp/grub_club.sock
chmod-socket = 664

# Removes all generated sockets and pids when closed
vacuum  = true

# Log file for this Project.
# Make sure the log file has the required write
# permissions for the emperor process.
logto = /var/log/uwsgi/app/grub_club.log
```

Now lets add a symbolic link to the directory that the emperor is watching.

```bash
sudo ln -s /etc/uwsgi/apps-available/grub_club.ini /etc/uwsgi/apps-enabled/
```

If all goes well, the emperor process must automatically create a process for our Django project. Have a look at `/var/log/uwsgi/emperor.log` file for any failures.

_Common issue: The `.log` file either hasn't been created or doesn't have the right permissions. Make sure it does!_

---

## 4. Make Nginx talk to uWSGI

So far we configured uWSGI to run our Django project and handle any request made to the defined endpoint (In my case, its the unix socket `/tmp/grub_club.sock`). Now, lets configure Nginx to forward any request it gets to this socket. If you are not using a CDN to deliver static and media files, we must also configure Nginx to serve these files. You must run collectstatic first. Lets start by creating a server block and place it in the `/etc/nginx/sites-available/` directory. The following shows the configuration for my Grub Club project.

```bash
# /etc/nginx/sites-available/grub_club
upstream grub_club_cluster{
    # This will contain one or more endpoints that uWSGI is listening to
    server unix:///tmp/grub_club.sock;
}
server {
    server_name     grubclub.com;
    charset         utf-8;

    # Configuration to deliver static files
    location ^~ /static/ {
        # This will point to the folder where all the static files reside
        # Same location as Django's STATIC_ROOT
        alias     /var/www/grub_club/static/;
        # Sets the HTTP headers for expiration time
        location ~*  \.(jpg|jpeg|png|gif|ico|css|js|woff)$ {
               expires 60d;
        }
    }
    # Configuration to deliver media files
    location ^~ /media/ {
        # This will point to the folder where all the media files reside
        # Same location as Django's MEDIA_ROOT
        alias    /var/www/grub_club/media/;
        # Sets the HTTP headers for expiration time
        location ~* \.(jpg|jpeg|png|gif){
            expires 365d;
        }
    }
    # All other requests are sent to the Django project
    location / {
        include     /etc/nginx/uwsgi_params;
        uwsgi_pass    grub_club_cluster;
    }
}
```

Create a symbolic link to sites-enabled and reload the configuration.

```bash
sudo ln -s /etc/nginx/sites-available/grub_club /etc/nginx/sites-enabled/
sudo service nginx reload
```

---

## Tips

By default, all requests to `grubclub.com` are handled by Nginx. What about `www.grubclub.com`? Lets add the following server block to the grub_club file to redirect requests to the main domain.

```bash
server {
    server_name www.grubclub.com;
    return 301 $scheme://grubclub.com$request_uri;
}
```

Another useful thing to do is to prevent bots from making a brute force attack on your Django admin page. You could look at Django applications like [Django Admin Honeypot](http://django-admin-honeypot.readthedocs.org/en/latest/) or limit the number of requests by configuring Nginx as [shown here](http://lincolnloop.com/blog/rate-limiting-nginx/).