Serving Django projects

Photo by COPPERTIST WU on Unsplash

Photo by COPPERTIST WU on Unsplash

Django projects served on Ubuntu prod server
Updated 23. June 2024

According to the official documentation, Django projects should be served by a proper web server using a uWSGI (Universal WebService Gateway Interface. 

In this article, I will explain the whole process of serving Django projects using Nginx on an Ubuntu 22 VPS (Virtual Private Server).

Django setup

Before you start hosting the project, you should ensure the website listens to the correct hostname. In this article, the project "Django Haxor" will be hosted at django.haxor.no, with a project named "djangohaxor". Rename the project and hostnames as you see fit.

Start by adding the correct hostname on which the website will be hosted in your settings file.

settings.py
...
ALLOWED_HOSTS = [
    'localhost',
    '127.0.0.1',
    'django.haxor.no'
]
...

Installing the project

Typically, your Django project has a git repository. Clone the repository into your desired directory (usually the /var/www directory on Debian-based operating systems like Ubuntu), and install the dependencies.

# Install Python dependencies
python3 -m venv venv
chmod +x venv/bin/activate 
source venv/bin/activate
pip install -r requirements.txt

# Install JavaScript dependencies
npm install

Once the dependencies are installed, run your migrations, or import a DB dump into your database manager.

Finally, add .env files or make your secrets accessible to the Django app.

uWSGI installation

Once you have cloned your git repository to the VPS and installed your dependencies, you must install several packages on the OS, including the uwsgi Python package (if not already a part of your requirements.txt file.

sudo apt install python3-dev gcc ibxml2-dev
pip install uwsgi

Serve with supervisor

To create a *daemon that manages the uWSGI, I prefer to use "supervisor". I find the configuration files more straightforward than systemctl config files. Because of this, I first make sure that "supervisor" is installed before continuing.

sudo apt update
sudo apt install supervisor

Once installed, the config file can be created.

sudo vim /etc/supervisor/conf.d/django.haxor.no.conf
/etc/supervisor/conf.d/django.haxor.no.conf
[program:django_haxor]
directory=/var/www/django.haxor.no
command=/var/www/django.haxor.no/venv/bin/uwsgi --socket djangohaxor.sock  --module djangohaxor.wsgi --chmod-socket=666
autostart=true
autorestart=true

Update supervisor, to add the project to the list of daemons it manages. By adding it, it will be automatically started, and will be running when the VPS boots.

sudo supervisorctl update

Once added, the Django project can be managed using these commands:

sudo supervisorctl start django_haxor
sudo supervisorctl stop django_haxor
sudo supervisorctl restart django_haxor
sudo supervisorctl status django_haxor

Nginx Reverse Proxy

When the project is managed and running it can be made available on the internet by using Nginx as a webserver. Nginx will use the socket created by the uWSGI and serve it under the hostname you define.

Start by ensuring you have the uwsgi_params file in the nginx config directory.

/etc/nginx/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  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

You can then create a basic virtual host config file that connect to the Django app.

sudo vim /etc/nginx/sites-available/django.haxor.no.conf
/etc/nginx/sites-available/django.haxor.no.conf
upstream djangohaxor{
    server unix:///var/www/django.haxor.no/djangohaxor.sock;
}

server {
    listen 80;
    server_name django.haxor.no;
    charset utf-8;
    
    # max upload size
    client_max_body_size 50M;
    
    location / {
        uwsgi_pass  djangohaxor;
        include uwsgi_params;
    }
}

Once created, a symlink has to be made to enable the site.

sudo ln -s /etc/nginx/sites-available/django.haxor.no.conf /etc/nginx/sites-enabled/

Finally, test the config file for syntax errors and restart nginx to start serving the website.

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl restart nginx

If you can verify with your browser that you can access your project over HTTP, you can create a TLS certificate and start serving the project on HTTPS by running Certbot. 

sudo certbot --nginx -d django.haxor.no

I hope this guide was helpful.