How to Deploy Django with Nginx, Gunicorn and Postgres on Ubuntu

Django application is great but hosting it with the normal web servers isn't so great an idea. Django works awesome with Gunicorn and Nginx along with Postgres in the database end. We will install this setup in Ubuntu 22.04 LTS which will mostly be applicable to further Ubuntu versions as well.

Step 1 : Installing Packages

You can install the required packages for database and python related files from Ubuntu apt repository. First you need to update the package indexes before downloading and installing any other packages, with the following command in terminal:

sudo apt update

Please ensure that python3 is installed before proceeding further:

python3 -V

The above command should show the version installed like Python 3.10.6 If you need to update to different Python version you can update using deadsnakes repository:

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install python3.11

Please run the following command to install Postgres, Nginx and related python files:

sudo apt install python3-venv python3-dev libpq-dev postgresql postgresql-contrib nginx curl

If you update to higher python versions say 3.11 then you can also install the related files similarly, else you can proceed further to the next step.

sudo apt install python3.11-venv python3.11-dev

Step 2: PostgreSQL Database and User Creation

An administrative user in the name of postgres was created during postgresql installation both in linux system and in postgresql database. This user will be needed further to perform administrative tasks in postgressql. You can log in as postgres user using sudo and start the postgresql tasks.

sudo -u postgres psql

You can create the database in the logged in prompt of PostgreSQL.

CREATE DATABASE django_project_1;

Then create a database user for this project with a strong password.

CREATE USER dj_project_1_user WITH PASSWORD 'password';

Then set the default database encoding expected by Django.

ALTER ROLE dj_project_1_user SET client_encoding TO 'utf8';

Then set the default transaction isolation scheme to read committed to ensure the reading from committed transactions only.

ALTER ROLE dj_project_1_user SET default_transaction_isolation TO 'read committed';

Then set the timezone to UTC which is generally used in Django based projects. ALTER ROLE dj_project_1_user SET timezone TO 'UTC';

Finally provide all the privileges of managing the created database to the newly created database user.

GRANT ALL PRIVILEGES ON DATABASE django_project_1 TO dj_project_1_user;

Thereafter you can logout from database console by typing \q and pressing enter. The above steps successfully configured PostgreSQL with database ready for connection.

Step 3: Creation of Python Virtual Environment

We will create the python virtual environment at /var/www/ for our first django project and will create more folders for subsequent django projects as well. You can also keep you files in /var/www/html/ folder or any other folder as you like it.

cd /var/www
mkdir django_project_1

Then create the virtual environment inside the project folder.

cd django_project_1
python3 -m venv venv

You can give a different name to your virtual environment folder. I keep it as venv for simplicity of use. You can use higher version python here if you need.

python3.11 -m venv venv_name

Activate your virtual environment.

source venv/bin/activate

Install Django, Gunicorn and PostgreSQL adapter in the virtual environment.

pip install django gunicorn psycopg2-binary

Step 4: Django Project

Now in your project's activated virtual environment, you can start the django project. You can start the project normally as you do for django projects.

django-admin startproject django_pro_1

I generally rename this project directory as src to know that it is the source directory. You may rename it or keep it as you like, but you need to use the same path hereafter in further steps. You can rename as below.

mv django_pro_1 src

You will further need to add localhost and you server IP in the ALLOWED_HOSTS section in the settings.py file of you Django project. You can update it via nano editor or any other editor.

nano src/django_pro_1/settings.py

Once done, press Ctrl+O to save and Ctrl+X to exit for nano editor.

Here add your server IP and localhost.

ALLOWED_HOSTS = ['server domain name or IP', 'another server domain name or IP', 'localhost', '127.0.0.1']

Next, you need to update the database name and database user name that you created above, in settings.py

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'django_project_1',         
'USER': 'django_project_1_user',         
'PASSWORD': 'password',         
'HOST': 'localhost',         
'PORT': '',
} 
}

Next, you need to mention where the static files of django project will be kept through settings.py, which will help NGINX to server the same.

from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
STATIC_ROOT =  BASE_DIR.parent / 'static'

Thereafter you can perform migrations and create a super user for you django project, and collect the static files, using commands below.

python src/manage.py makemigrations
python src/manage.py migrate
python src/manage.py createsuperuser
python src/manage.py collectstatic

Create a temporary firewall exception in firewall for port 8000 for testing purpose.

sudo ufw allow 8000

You can now run the Django development server to test the Django application.

python src/manage.py runserver 0.0.0.0:8000

Now check your Django website through your IP:

http://your_server_domain_or_IP:8000

You must receive the default Django page up and running. You can check your superadmin account as well from /admin URL. Press Ctrl+C to stop the development server

You can now test Gunicorn web server from your virtual environment.

gunicorn --bind 0.0.0.0:8000 src/django_pro_1.wsgi

The static files will not be served in the above case as gunicorn does not server static files. In our case we will setup Nginx to server static stuff. Press Ctrl+C to stop the development server

You can now deactivate the virtual environment.

deactivate

The virtual environment will be closed.

Step 5: Creating System Files for Gunicorn

In order to use Gunicorn server that we have installed in the virtual environment, we need to create systemd socket and service files for the project. Similarly for multiple projects we can further create their own systemd socket and service files. We will now first create the gunicorn socket by the name guncorn_django_pro_1 to identify it as the socket file for our first django project.

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

Inside the file, we need to mention the name of the socket unit and the socket file for listening. Please enter the below details.

[Unit]
Description=gunicorn socket 
[Socket]
ListenStream=/run/gunicorn_django_pro_1.sock
[Install]
WantedBy=sockets.target

Once the socket file has been saved, we need to create the gunicorn service file.

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

Inside the service file, we need to mention the path details of our project and virtual environment, along with the socket details. Please enter the details as below.

[Unit]
Description=gunicorn daemon
Requires=gunicorn_django_pro_1.socket
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/var/www/django_project_1/src
ExecStart=/var/www/django_project_1/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--env DJANGO_SETTINGS_MODULE=django_pro_1.settings \
--bind unix:/run/gunicorn_django_pro_1.sock \
django_pro_1.wsgi
[Install]
WantedBy=multi-user.target

Once the socket and service has be created we can start and enable them so that they are started every time the system starts. Please enter the commands below for socket.

sudo systemctl start gunicorn_django_pro_1.socket
sudo systemctl enable gunicorn_django_pro_1.socket

Please enter the commands below for gunicorn service files.

sudo systemctl start gunicorn_django_pro_1
sudo systemctl enable gunicorn_django_pro_1

You can check the status of the gunicorn socket using command below.

sudo systemctl status gunicorn_django_pro_1.socket

You can check existence of the socket file by command below.

file /run/gunicorn_django_pro_1.sock

You can check the status of the gunicorn service using command below.

sudo systemctl status gunicorn_django_pro_1

You can also test the web service of gunicorn by sending a request using curl.

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

You can then see the output status using command below.

sudo systemctl status gunicorn_django_pro_1

You can restart the gunicorn service using the command below.

sudo systemctl restart gunicorn_django_pro_1

Step 6: Configuring Nginx

We will configure Nginx to proxy pass requests to Gunicorn server. You can add the detail of the Django projects inside sites-available directory of Nginx, by creating individual files for each Django project. We will start by adding the details of our Django project.

sudo nano /etc/nginx/sites-available/django_pro_1

Please enter the details inside the file as below.

server {
listen 80;
server_name your_domain_name_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/django_project_1/static;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn_django_pro_1.sock;
}
}

Once completed, we need to create a sym link of the file to the sites-enabled directory, using the command below.

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

You can test the Nginx settings using the commands below.

sudo nginx -t

If all is ok, then you can restart Nginx.

sudo systemctl restart nginx

Please remove the 8000 port from firewall and allow Nginx Full to serve both http and https traffic.

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

Now you have successfully setup and deployed your Django project using Nginx, Gunicorn and PostgreSQL. Whenever you need to make any changes, you can do so and then restart the gunicorn and nginx service as required, using the commands below.

sudo systemctl restart gunicorn_django_pro_1
sudo nginx -t
sudo systemctl restart nginx

I hope you enjoyed the tutorial. If you need further consultation services for the same, you can most welcome. Please leave a positive review, if this tutorial helped you.