Setting Up Django with Nginx + Daphne on Ubuntu 18.04

Tags: daphne supervisord django nginx Ubuntu

My Environment

I'll be deploying a Django 3.0 web application on a Digital Ocean droplet running Ubuntu 18.04. The web app will be served by Daphne and Nginx.

Installing Things

Python

I'll be running Django using Python 3.8.1 which isn't currently available as a package on Ubuntu so I'll be installing it from source. The files are available on python.org.

You could use wget to download the files straight from the remote machine, but I chose to just download it locally and transfer it to my remote home directory with scp.

scp Python-3.8.1.tar.xz username@123.123.123.123:/home/username

Dependencies

Before we install Python, we need to make sure we have a few packages installed:

sudo apt-get update && sudo apt-get install libbz2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev zlib1g-dev libffi-dev

Now we get go ahead with our Python install. I personally like to install third-party application in the /opt directory, so I'll do that here.

Move it over, configure, make, test, and install. I'm using altinstall so I we don't overwrite our primary python installation because I don't see any downsides to doing so and it seems less likely to result in unintended consequences.

sudo mv ~/Python-3.8.1.tar.xz /opt
cd /opt
sudo tar -xvf Python-3.8.1.tar.xz
sudo rm Python-3.8.1.tar.xz
cd Python-3.8.1/
./configure --enable-ipv6 --enable-optimizations
make
make test
sudo make altinstall

Sweet, so now we have python3.8 installed. Calls to python3 should still be linked to the system default, which in my case is python 3.6. To use 3.8, you'll actually need to call python3.8 explicitly.

So let's get a virtual environment setup that we can use to run our Django application. I'll use virtualenv which we can install via pip. I'll install the python3 version on my system python to do it:

sudo apt-get install python3-pip
pip3 install virtualenv

Back in our home directory we can use virtualenv to create the environment in a new directory we'll name 'venv'.

cd
virtualenv -p python3.8 venv

Django

Since I already have a Django application ready to go, I'll activate our new virtual environment and use the requirements file from the Django application to install everything the app needs.

source ~/venv/bin/activate
cd ~/averyuslaner.com
pip install -r requirements.txt

My web app uses MySQL for it's database so I need to install the following before I can successfully pip install mysqlclient. If you're interested in more details on how to use MySQL with Django, you can check out my other blog post on Setting up MySQL for Django.

By the way, if you don't have your own requirements file, the only things you need to pip install to follow along are django 3.0+ and daphne.

Once you have at least those two things installed, we can test daphne's ability to serve the app. It will make use of the asgi.py file that lives in the same directory as the settings.py file on a default install. If you're upgrading django from version 2 or below, you'll need to add it yourself.

daphne -b 123.123.123.123 -p 8000 mydjangoproject.asgi:application

The above command binds the application to the given IP and port numbers respectively. You'll want to replace the 123s with the IP address of your own remote machine. If all goes well, your web application should now be accessible from your local machine by entering http://123.123.123.123:8000 into your browser.

Supervising Daphne

Launching daphne manually isn't ideal so let's manage it with supervisord. We can start to attack this problem at along with configuring nginx so let's install both now.

sudo apt-get install supervisor nginx

Next, we write a configuration file for daphne, which we save inside /etc/supervisor/conf.d/.

sudo nano /etc/supervisor/conf.d/averyuslaner.conf

Within the file, add something like what I have below. Note that I'm supplying the absolute path to daphne that's inside my virtual environment.

[fcgi-program:asgi]
# Set Django environment variables
environment=DJANGO_SETTINGS_MODULE="mydjangoproject.settings.production",AVE_SECRET_KEY="087syhu3r8ef79u2h3jr89yihujr38yuhwef"

# TCP socket used by Nginx backend upstream
socket=tcp://localhost:8000

# Directory where your site's project files are located
directory=/home/myusername/averyuslaner.com

# Each process needs to have a separate socket file, so we use process_num
# Make sure to update "mydjangoproject.asgi" to match your project name
command=/home/ave/venv/bin/daphne -u /run/daphne/daphne%(process_num)d.sock --fd 0 --access-log - --proxy-headers mydjangoproject.asgi:application

# Number of processes to startup, roughly the number of CPUs you have
numprocs=1

# Give each process a unique name so they can be told apart
process_name=asgi%(process_num)d

# Automatically start and recover processes
autostart=true
autorestart=true

# Choose where you want your log to go
stdout_logfile=/var/log/asgi.log
redirect_stderr=true

Now we tell supervisor to update so it can read the configuration file:

sudo supervisorctl reread
sudo supervisorctl update

Serving From Nginx

Next, we create the configuration file for nginx which we'll save in /etc/nginx/sites-available/:

sudo nano /etc/nginx/sites-available/averyuslaner.com

Within the file, add this:

upstream daphne-backend {
    server localhost:8000;
}

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

    location / {
        try_files $uri @proxy_to_app;
    }
    location @proxy_to_app {
        proxy_pass http://daphne-backend;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $server_name;
    }
}

Once that's done, we simply symlink the file into nginx's sites-enabled directory and reload nginx.

cd /etc/nginx/sites-enabled
sudo ln -s ../sites-available/averyuslaner.com .
sudo service nginx reload

That should do it! Your web application should now be accessible at your server's IP address. If that isn't the case, check your log files to see where the problem might be. Relevant log files can be found in the following places:

  • Nginx - /var/log/nginx/error.log
  • Supervisord - /var/log/supervisor/supervisord.log
  • Daphne process - /var/log/asgi.log