Deploy a Django application using Gunicorn and Nginx

Deploy a Django application using Gunicorn and Nginx

Differences by responsibility

Django is a web framework used to build web applications.

Gunicorn is an application server which transfers HTTP requests to web applications. It implements WSGI (Web Server Gateway Interface) which works as an interface between the web application and the web server.

Nginx is a web server that can be used as a reverse proxy server. It is capable of handling a large number of requests simultaneously.

Let's go step by step into this process

Django app setup

First, we need to create a simple Django app with minimal requirements. Let's create a simple Django app by following these steps

mkdir djnago_gunicorn_nginx
cd djnago_gunicorn_nginx
python3 -m venv .env
source .env/bin/activate
pip install django
django-admin startproject django_gunicorn_nginx .
python manage.py runserver

Now if you go to http://127.0.0.1:8000, you should be able to see Django application running on your browser. We're not going to do any Django configurations here.

Now that your Django app is set up and running. Let's jump into the Gunicorn setup.

Gunicorn install and setup

Install Gunicorn in your terminal

pip install gunicorn

Now, let's run your app via Gunicorn from shell by running this command

gunicorn -b 0.0.0.0:8000 --workers=2 django_gunicorn_nginx.wsgi:application

Congrats, you are just able to run your web application through Gunicorn.

Here '-b' is binding the app to the socket '0.0.0.0:8000', --workers=2 means 2 worker processes are running for accepting requests. Django_gunicorn_nginx.wsgi:application refers to a pattern $(MODULE_NAME):$(VARIABLE_NAME) in which MODULE_NAME would be to full dotted path to the module and VARIABLE_NAME refers to a WSGI callable that should be found in the specified module.

Let's configure Gunicorn properly in our app.

First, create a directory named 'config'. Then follow these steps.

mkdir config
nano config/gunicorn.py

Now add the following inside the file.

                                                                                                                                                                 """Gunicorn config for *development* environment"""
# Django wsgi appliation path, format MODULE_NAME:VARIABLE_NAME
wsgi_app = "django_gunicorn_nginx.wsgi:application"
# The granularity of error-log
loglevel = "debug"
# The number of processes will be running for handling requets
workers = "2"
# Socket to bind
bind = "0.0.0.0:8000"
# Restart worker when code changes
reload=True
# Write access and error log info
accesslog = errorlog = "/var/log/gunicorn/django_gunicorn_nginx.log"
# Redirect stdout/stderr to log file
capture_output=True
# PID file to fetch the process-id
pidfile = "/var/run/gunicorn/django_gunicorn_nginx.pid"
# Daemonize the gunicorn process
daemon = True

Let's create the necessary folders

sudo mkdir /var/{log,run}/gunicorn/
sudo chown -cR ubuntu:ubuntu /var/{log,run}/gunicorn/

Now start your app with Gunicorn by running this command

gunicorn -c config/gunicorn.py

Your app should be started running in the background through Gunicorn now.

If you want to see the logs generated for your app, please run the following command

tail -f /var/log/gunicorn/django_gunicorn_nginx.log

You should be able to see something like this

127.0.0.1 - - [16/Dec/2022:06:36:14 +0000] "GET /static/admin/css/fonts.css HTTP/1.1" 404 2170 "http://127.0.0.1:8000/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" [2022-12-16 14:20:39 +0530] [3833089] [CRITICAL] WORKER TIMEOUT (pid:3833091) [2022-12-16 14:20:39 +0530] [3833089] [CRITICAL] WORKER TIMEOUT (pid:3833094) [2022-12-16 08:50:39 +0000] [3833091] [INFO] Worker exiting (pid: 3833091) [2022-12-16 08:50:39 +0000] [3833094] [INFO] Worker exiting (pid: 3833094) [2022-12-16 14:20:39 +0530] [4012073] [INFO] Booting worker with pid: 4012073 [2022-12-16 14:20:39 +0530] [3833089] [WARNING] Worker with pid 3833091 was terminated due to signal 9 [2022-12-16 14:20:39 +0530] [3833089] [DEBUG] 1 workers [2022-12-16 14:20:39 +0530] [4012075] [INFO] Booting worker with pid: 4012075 [2022-12-16 14:20:39 +0530] [3833089] [DEBUG] 2 workers

Let's configure your config/gunicorn.py and make it ready for deployment.

import multiprocessing
import os                                                                                                                                                          """Gunicorn config for *development* environment"""
# Django wsgi appliation path, format MODULE_NAME:VARIABLE_NAME
wsgi_app = "django_gunicorn_nginx.wsgi:application"
# The granularity of error-log
loglevel = "warning"
# The number of worker threads for handling requests. Set as per your requirements
threads = multiprocessing.cpu_count() * 2
# The number of processes will be running for handling requets. Set as per requirements
workers = multiprocessing.cpu_count() * 2
# Socket to bind
bind = "0.0.0.0:8000"
# Restart worker when code changes
reload=True
# Write access and error log info
accesslog = errorlog = "/var/log/gunicorn/django_gunicorn_nginx.log"
# Redirect stdout/stderr to log file
capture_output=True
# PID file to fetch the process-id
pidfile = "/var/run/gunicorn/django_gunicorn_nginx.pid"
# Daemonize the gunicorn process
daemon = True
# The number of seconds to wait for requests on a Keep-Alive connection.
keepalive = 60
# Workers silent for more than this many seconds are killed and restarted.
timeout = 90

Do visit the official Gunicorn page for more details about the configuration https://docs.gunicorn.org/en/stable/settings.html

To kill the process of running through Gunicorn, you can run this command.

sudo lsof -i :8000

To kill the process get the PID from the output logs and run the command.

kill <PID>

Nginx configration

Install and run Nginx in your system.

sudo apt install nginx
sudo service nginx start

Let's add Nginx configuration file by nano-editor.

sudo nano /etc/nginx/conf.d/django_gunicorn.conf

Add the following content to your configuration file already opened via nano-editor.

access_log /var/log/nginx/djnago-gunicorn-access.log;
error_log /var/log/nginx/djnago-gunicorn-error.log;


server {
        listen 80;

        location / {
                proxy_pass http://localhost:8000;
                proxy_set_header HOST $host;
      }
}

Now we should remove the default file from /etc/nginx/sites-enabled as it might take priority over the django_gunicorn.conf we created eariler.

And now we need to restart nginx.

sudo rm -rf /etc/nginx/sites-enabled/default
sudo service nginx restart

Now you should be able to see your django-app running via gunicorn and nginx by opening the localhost in your browser : http://localhost/

For adding your server domain name in Nginx you need to add this right below the listen 80; line server_name <your-domain-name>.com; . Also, you need to add the elastic-IP (for AWS) of your server to your DNS register under DNS settings.

Conclusion

If you can follow to the end and it was able to increase your knowledge, then I am
well pleased.

Happy Learning !!