How to Deploy Django with Nginx & Gunicorn to Live Server (Ubuntu <= 21.04)

To make your Django application live on the web using Nginx, you will need to set up a Python Web Server Gateway Interface so that Nginx can communicate with Python.

 

In this tutorial, we will learn how to get your Django application live on the web using Nginx and Guincorn.

 

Step 1 - Update Ubuntu & Install Required Packages

The first step is to update Ubuntu and install the required packages.

 

sudo apt update

 

If you don't have python on your system install it with the following command:

 

sudo apt install python

 

Now install the rest of the requirements (If you plan to use a MySQL database, we'll discuss that later.)

 

sudo apt install python3-pip python3-dev nginx curl

 

Step 2 Install virtualenv

Now we will install the Python virtualenv package VIA pip to use a sandboxed version of Python for the Django project.

 

sudo -H pip install --upgrade pip
sudo -H pip install virtualenv

 

Step 3 - Setup Project Environment

Since we are using Nginx, we'll need to create a directory to house the project inside /var/www with an appropriate name like this:

 

sudo mkdir /var/www/example.com

 

Then change the owner and group of this directory to your user:

 

sudo chgrp -R myusername /var/www/example.com
sudo chown -R myusername /var/www/example.com/

 

now cd into the directory:

 

cd /var/www/example.com

 

Now create let's install our new Python environment in a directory called env with virtualenv.

 

virtualenv env
created virtual environment CPython3.9.5.final.0-64 in 500ms
 creator CPython3Posix(dest=/var/www/example.com/env, clear=False, no_vcs_ignore=False, global=False)
 seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/ubuntu/.local/share/virtualenv)
added seed packages: pip==21.2.4, setuptools==58.1.0, wheel==0.37.0
 activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

 

Step 4 Switch to Python Project Env and Install Requirements

Let's switch into the project's Python environment and install some requirements.

 

/var/www/example.com
source env/bin/activate
pip install django gunicorn

 

At this point you can either start a new Django project with the following command:

 

django-admin.py startproject myproject ~/myprojectdir

 

Or copy your project source code into the var/www/example folder.

 

Step 5 Configure Django

The next step is to configure your Django project to add your domains to allowed hosts. Open your project's settings.py file and add your domain to the ALLOWED_HOSTS list.

 

nano src/myproject/settings.py 

 

src/myproject/settings.py
ALLOWED_HOSTS = ['example.com', 'www.example.com']

 

 

Step 6 - Set up MySQL

Read my tutorial on how to set up Django with MySQL if you are using a MySQL database. 

 

Step 7 Test Site

To test Django is working let's allow connections through port 8000, boot the Django dev server and access the site in a browser.

 

sudo ufw allow 8000

 

src/manage.py runserver 0.0.0.0:8000

 

Now access the site in a browser, replacing server_domain_or_IP with the IP/domain of your server.

 

http://server_domain_or_IP:8000

 

Test Gunicorn Can Run the Django Project

Before leaving the Python virtual environment, let's test if Gunicon can serve the project.

 

cd into the project src and run (replacing myproject with the name of yours:)

 

gunicorn --bind 0.0.0.0:8000 myproject.wsgi
[2021-10-05 02:13:48 +0000] [10661] [INFO] Starting gunicorn 20.1.0
[2021-10-05 02:13:48 +0000] [10661] [INFO] Listening at: http://0.0.0.0:8000 (10661)
[2021-10-05 02:13:48 +0000] [10661] [INFO] Using worker: sync
[2021-10-05 02:13:48 +0000] [10662] [INFO] Booting worker with pid: 10662

 

Can now reload the site in the browser and it should still work. CSS, JavaScript other static assets will not load, this is normal because Gunicorn doesn't know how to serve them and is not responsible for doing so.

 

Stop Guincorn with the key combination CTRL + C

 

Create Service Files and systemd Socket for Gunicorn

Next, we will create a systemd socket and service files for Gunicorn so that on boot a Gunicorn socket will listen for connections and start a new process when one is requested.

 

sudo nano /etc/systemd/system/gunicorn.socket

 

/etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

 

sudo nano /etc/systemd/system/gunicorn.service

 

Replace example with the name of your Django app in the file  below:

 

/etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=john
Group=www-data
WorkingDirectory=/var/www/example.com/src
ExecStart=/var/www/example.com/env/bin/gunicorn \
     --access-logfile - \
     --workers 3 \
     --bind unix:/run/gunicorn.sock \
     example.wsgi:application

[Install]
WantedBy=multi-user.target

 

Replace the WorkingDirectory constant with the full path to the root of your Django project and the ExecStart constant with the path to Gunicorn.

 

Now start and enable the Gunicorn services with systemctl:

 

sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
Created symlink /etc/systemd/system/sockets.target.wants/gunicorn.socket → /etc/systemd/system/gunicorn.socket.

 

Check Gunicorn Socket Status and File

Now we will check the Gunicorn status and presence of a gunicorn.sock file in the /run directory.

 

sudo systemctl status gunicorn.socket
● gunicorn.socket - gunicorn socket
   Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
   Active: active (listening)&nbsp;since Tue 2021-10-05 13:30:40 UTC; 1min 42s ago
  Triggers: ● gunicorn.service
   Listen: /run/gunicorn.sock (Stream)
   Tasks: 0 (limit: 8090)
   Memory: 0B
   CGroup: /system.slice/gunicorn.socket

Oct 05 13:30:40 b2-7-uk1 systemd[1]: Listening on gunicorn socket.

 

Now, check for the existence of the gunicorn.sock file within the /run directory:

 

file /run/gunicorn.sock
/run/gunicorn.sock: socket

 

Test Socket Works with an Incoming Connection

Now we will create a test connection using curl, which should return the HTML output of the application.

 

curl --unix-socket /run/gunicorn.sock localhost

 

We cal also verify Gunicorn is active with the following command:

 

sudo systemctl status gunicorn
● gunicorn.service - gunicorn daemon
  Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2021-10-05 13:41:46 UTC; 9s ago
TriggeredBy: ● gunicorn.socket...

 

If there was an error, check the Gunicron log with the following command:

 

sudo journalctl -u gunicorn

 

Look for any problems related to your /etc/systemd/system/gunicorn.service and when any changes are made reload the service with the following commands:

 

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

 

Configure Nginx to Proxy Pass to Gunicorn

Now Gunicorn is set up and ready to receive connections, we need to create an Nginx config file for the application to handle incoming HTTP connections and proxy them to Gunicorn.

 

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

 

Here is a basic Nginx configuration file, the key thing to use is the proxy_pass and proxy_params in the location block:

 

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

 

Now create a symbolic link to sites-enabled for your Nginx config file:

 

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

 

Test the config file is working:

 

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

 

The last thing we need to do is delete the firewall rule for port 8000 and allow Nginx.

 

sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'