How to Install Django Python Framework on CentOS 8

In this blog post, we are going to learn how to install Django with Nginx on CENTOS 8. To follow the contents of this tutorial you will need to have a basic understanding of the Linux environment and Python/Django as well.

Below, you will see the architecture of our environment.


Django: Python Web framework (backend).
Nginx: Reverse proxy.
Gunicorn: Python WSGI HTTP Server.
Supervisor: Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.

For educational purposes (because you may still be learning), I strongly recommend you to try to implement this environment in a Virtual Machine, with snapshots and other features that it may have, just for precaution and to avoid headaches in the future. If you do anything wrong, you can rollback and try again.

Before we dive into the installation part, here’s what you can expect from this blog post:

Chapter 1 – Installing NGINX
Chapter 2 – Installing Supervisord with internet access
Chapter 3 – Creating a Django Project
Chapter 4 – Installing Gunicorn (Python WSGI HTTP Server)
Chapter 5 – Creating Gunicorn config file
Chapter 6 – Edit Nginx main config file
Chapter 7 – Editing /etc/supervisord.conf
Chapter 8 – Starting Supervisor, Nginx and Django/Gunicorn
Chapter 9 – Problems and Troubleshoot
Chapter 10 – Key Takeaways

Now let’s start installing Django Python Framework on CentOS 8.

In this section, we are going to provide information on how to install Nginx, use the package manager, and grant access through the firewall (that’s very important or you may find yourself in trouble later on). Nginx is needed because it will function as a reverse proxy.

Step 1: To add CENTOS EPEL repository

$ sudo yum install epel-release

Step 2: Now that the Nginx repository is installed, we need to install its package

$ sudo yum install nginx

Step 3: Next, we need to use systemctl to start its process

$ sudo systemctl start nginx

Step 4: If you are running a firewall, it’s a good idea to allow the http traffic and https

$ sudo firewall-cmd –permanent –zone=public –add-service=http
$ sudo firewall-cmd –permanent –zone=public –add-service=https
$ sudo firewall-cmd –reload

Step 5: Now its time to check if the Nginx server is up and running


Step 6: Enable the Nginx service to automatically boot with the OS
$ sudo systemctl enable nginx

At this point, we need the supervisor to control the process and monitor processes that are needed to keep the Django page running.

Step 1: Install through pip. You might need “root” privileges in order to install supervisor

$ pip3 install –user supervisor

Step 2: Run the commands listed below

$ yum install supervisor
$ systemctl enable supervisord
$ systemctl enable supervisord
$ systemctl status supervisord

Step 3: Generate the supervisord.conf in order for the supervisor to run

$ echo_supervisord_conf > supervisord.conf
$ sudo cp supervisord.conf /etc/supervisord.conf

We will now guide you through the necessary steps to create the Django project as well as the directory structure and some settings that need to be changed in the

Prerequisites: You might need “root” privileges in order to create directories as shown in the following steps.

Step 1: Create the following directories as shown below

$ mkdir /opt/django-websites

PS: make sure that you give the appropriate access to the directory “django-websites” and its contents.

Step 2: Create a Python virtual environment with the command listed below in the directory of your project

$ python3 -m venv venv

Step 3: Activate the virtual environment

$ source venv/bin/activate

Step 4: Install Django in the venv that you created

(venv)$ pip install django

Step 5: Now we will use the command “django-project” to create a project with django

(venv)$ startproject project .

Django will create this structure:

|– config
|   `– gunicorn_config.conf
|– logs
|–`– project

Step 6: Go to the “/opt/django-websites” dir and create a static folder and logs directory

/opt/django-websites/ #Directory where all your sites will be located.

|– logs #Directory where the logs of your sites will be located.
|– project-test #Directory of the Django project root
|   |– config #Directory where gunicorn config file is.
|   |    `– gunicorn_config.conf #gunicorn config file
|   |– logs # application log comes here
|   |–
|   `– project #Main Directory of a Django application
|   |    |–
|   |    |–
|   |    |– #Important settings file for your project
|   |    |–
|   |    `–
|   |– venv #python virtual environment
`– static # All static files from your projects are stored here.

Step 7: Open the /opt/django-websites/project-test/project/

Step 8: Add ‘*’ to the ALLOWED_HOSTS

A brief explanation from the Django Documentation: “A list of strings representing the host/domain names that this Django site can serve. This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations.”

Update the option like this.


Step 9: Add these lines at the end of the

# Static files (CSS, JavaScript, Images)
# your application will search for the static files in relative url http://your_nameblalba/static/[projectname]/*
STATIC_URL = ‘/static/project-test/’

# and the root of your static folder will be…
if == “nt”:
STATIC_ROOT = ‘C:/work/django-websites/static/’
STATIC_ROOT = ‘/opt/django-websites/static/project-test/’

Step 10: After any changes are made in python, you must restart the application process in supervisor via web or via systemctl
E.g.: supervisorctl or via web http://server_name:9001 and click restart

$ sudo supervisorctl
PROJECT-TEST RUNNING pid 15146, uptime 0:12:12
supervisor> restart PROJECT-TEST

Now we will install Gunicorn, which will function as a Python WSGI HTTP Server. Pretty simple so far.

Step 1: With the virtual environment activated, install gunicorn by running the commands below.

(venv)$ pip install gunicorn

PS: this will install the latest version.

The gunicorn config file will set how Gunicorn will bind to your network in order to be seen by Nginx and will point to the virtual environment (aka venv) of your Python project.

Step 1: Create a folder named “config” in the root of your Django’s project

$ cd /opt/django-websites/project-test
$ mkdir config

Step 2: The root project must have the following structure

|– project-test #Directory of the Django project root
|   |– config #Directory where gunicorn config file is.
|   | `– gunicorn_config.conf #gunicorn config file
|   |– logs # application log comes here
|   |–
|   `– project #Main Directory of a Django application
|       |–
|       |–
|       |– #Important settings file for your project
|       |–
|       `–
| — venv

Step 3: Create the gunicorn config file

At the folder: /opt/django-websites/project-test/

Create the file config/gunicorn_config.conf with the following contents:


command = ‘gunicorn -c /opt/django-websites/project-test/config/gunicorn_config.conf project.wsgi’
pythonpath = ‘/opt/django-websites/project-test/venv/bin/’
= ‘’
workers = 2

Step 4: It’s important that the bind IP address and port are referenced in the upstream config or else it will not be able to redirect!

This is a tricky part so you have to be very careful with the Nginx config file or you may end up with a non-functioning server. That’s why I‘ve provided the entire file of my Nginx config file.

Step 1: Open the nginx.conf file located at /etc/nginx

# make a backup first, just for if anything goes wrong.
$ cp nginx.conf nginx.conf.bak
$ vim /etc/nginx/nginx.conf if you do not like vim you can use whatever you like.

Step 2: Paste the following contents into the nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;

http {
log_format main ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘$status $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘

    access_log /var/log/nginx/access.log main;

    sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

    include /etc/nginx/mime.types;
default_type application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See
    # for more information.
include /etc/nginx/conf.d/*.conf;
    #Act as a reverse proxy , redirecting incoming connection to gunicorn wsgi server
include /etc/nginx/upstream-enabled/*.conf;

    server {
    listen :[80] default_server;
        server_name VBCENTOS;
        client_max_body_size 50M;
        access_log /opt/django-websites/logs/access.log;
        error_log /opt/django-websites/logs/error.log;

        #Other pages in the same server
    include /etc/nginx/sites-enabled/*.conf;



[/kc_column_text][kc_column_text _id=”911707″ css_custom=”{`kc-css`:{}}”]

Step 3: Create upstream and sites-enabled directories

# At /etc/nginx create the following directories
$ mkdir /etc/nginx/sites-enabled
$ mkdir /etc/nginx/upstream-enabled

Step 4: At the sites-enabled directory, create a file called project-test.conf and insert the following contents

#define where the Nginx will search for the static files.
#project-test django project root dir
#make sure all your static files are under /static like js css etc..
location /static/project-test/ {
      alias /opt/django-websites/static/project-test/;

# if the request that comes to your website is not looking for a virtual

#redirect to the project-test upstream

location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      if (!-f $request_filename) {
            proxy_pass http://project-test;

Step 5: At the upstream-enabled directory, create a file named project-test.conf and insert the following contents

# Reverse proxy in action redirect to Gunicorn python WSGI server
# hint to each project you create you need to define a new port
upstream project-test {

Nginx directory structure

|– conf.d
|– default.d
|– fastcgi.conf
|– fastcgi.conf.default
|– fastcgi_params
|– fastcgi_params.default
|– koi-utf
|– koi-win
|– mime.types
|– mime.types.default
|– nginx.conf
|– nginx.conf.bak
|– nginx.conf.default
|– scgi_params
|– scgi_params.default
|– sites-enabled
| `– project-test.conf
|– upstream-enabled
|   `– project-test.conf

Now we will config the Supervisor to monitor the python process as well as take control of the process itself. You can start, stop, and restart the process by accessing the HTTP interface or via supervisorctl (via bash $ supervisorctl); that’s a CLI that does the same thing.

Step 1: Open the /etc/supervisord.conf
I will use VIM. You can use whatever program you prefer.

$ vim /etc/supervisord.conf

Step 2: Search for [inet_http_server] and change it as shown below

[inet_http_server] ; inet (TCP) server disabled by default
port=*:9001 ; ip_address:port specifier, *:port for all iface
username=admin ; default is no username (open server)
password=YOURPASSWORD ; default is no password (open server)

Step 3: Search for [include] (bottom of the file) make the changes as shown below

;files = relative/directory/*.ini
files = /opt/django-websites/supervisord_confs/*.conf

Step 4: Notice the supervisord_confs folder? We haven’t created it yet. No problem, we will create the folder now

$ mkdir /opt/django-websites/supervisord_confs

Step 5: Create the config file for supervisor that contains the configurations for your web application’s processes

$ touch /opt/django-websites/supervisord_confs/project-test.conf

Step 6. Add the following configuration lines

command=/opt/django-websites/project-test/venv/bin/gunicorn -c /opt/django-websites/project-test/config/gunicorn_config.conf project.wsgi

#HINT project.wsgi can be found at
|– logs
|– project-test
|    |– config
|   | `– gunicorn_config.conf
|   |– logs
|   |–
|   `– project
|     |–
|     |–
|     |–
|     |–
|     `–
`– static

Step 7: Add a rule in the firewall for the port 9001

$ sudo firewall-cmd –zone=public –add-port=9001/tcp –permanent
$ sudo firewall-cmd –reload

Reload the firewall to save the changes.

Step 8: Now you can access the web version of supervisorctl

PS: you can access with http://[YOUR_IP_ADDRESS]:9001
Your login and password are set in the supervisor’s config.

Step 1: Check if the supervisord and Nginx are running

$ sudo systemctl status supervisord
● Active: active (running) since Mon 2020-01-27 19:02:02 WET; 18min ago
$ sudo systemctl status nginx
● Active: active (running) since Mon 2020-01-27 19:02:02 WET; 18min ago

Step 2: Reboot

Nginx can’t run because it can’t create the log file.
Solution: Give Nginx user permission to write the file.

Jan 27 19:28:34 VBCENTOS nginx[27589]: nginx: [emerg] open() “/opt/django-websites/logs/access.log” failed (13: Permission denied)

[/kc_column_text][kc_column_text _id=”699472″ css_custom=”{`kc-css`:{}}”]

Make sure you have the right permissions on #TODO.

If you have SELINUX activated, you can change SELINUX to permissive to only log actions that were blocked earlier, but check with your OS administrator for help.
I will not cover any SELINUX settings in this tutorial.

By completing all of the steps of this tutorial, you will be able to configure a Django Server with Nginx and Gunicorn and monitor the Django applications in Supervisor. If you want to add another app, just repeat the process starting from chapter 3.

I hope you’ve found this tutorial useful and that I have answered some of your questions regarding how to implement Django with Nginx.

MongoDB or SQL? When to apply the right technology
The Importance of Testing Code