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.
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
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
[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:
[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) 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'