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
http://your_machine_ip/
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 setting.py.
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)$ django-admin.py startproject project .
Django will create this structure:
./project-test
|– config
| `– gunicorn_config.conf
|– logs
|– manage.py`– project
|– __init__.py
|– asgi.py
|– settings.py
|– urls.py
`– wsgi.py
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
| |– manage.py
| `– project #Main Directory of a Django application
| | |– __init__.py
| | |– asgi.py
| | |– settings.py #Important settings file for your project
| | |– urls.py
| | `– wsgi.py
| |– venv #python virtual environment
`– static # All static files from your projects are stored here.
Step 7: Open the /opt/django-websites/project-test/project/settings.py
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.
ALLOWED_HOSTS = [‘*’]
Step 9: Add these lines at the end of the setting.py
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
# 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 os.name == “nt”:
STATIC_ROOT = ‘C:/work/django-websites/static/’
else:
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
PROJECT-TEST: stopped
PROJECT-TEST: started
supervisor>
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
| |– manage.py
| `– project #Main Directory of a Django application
| |– __init__.py
| |– asgi.py
| |– settings.py #Important settings file for your project
| |– urls.py
| `– wsgi.py
| — 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:
source
command = ‘gunicorn -c /opt/django-websites/project-test/config/gunicorn_config.conf project.wsgi’
pythonpath = ‘/opt/django-websites/project-test/venv/bin/’
bind = ‘127.0.0.1:8001’
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/nginx.pid;
# 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 http://nginx.org/en/docs/ngx_core_module.html#include
# 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
server
#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;
break;
}
}
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 {
server 127.0.0.1:8001;
}
Nginx directory structure
/etc/nginx/
|– 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
[include]
;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
[program:PROJECT-TEST]
command=/opt/django-websites/project-test/venv/bin/gunicorn -c /opt/django-websites/project-test/config/gunicorn_config.conf project.wsgi
directory=/opt/django-websites/project-test/
stderr_logfile=/opt/django-websites/project-test/logs/long.err.log
stdout_logfile=/opt/django-websites/project-test/logs/long.out.log
#HINT project.wsgi can be found at
./django-websites/
|– logs
|– project-test
| |– config
| | `– gunicorn_config.conf
| |– logs
| |– manage.py
| `– project
| |– __init__.py
| |– asgi.py
| |– settings.py
| |– urls.py
| `– wsgi.py
`– 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.