In this guide we will learn how to set up django application on a Rocky Linux 9 server. We will be setting up a PostgreSQL database instead of using the default SQLite database. We will configure the Gunicorn application server to interface with our applications. We will then set up Nginx to reverse proxy to Gunicorn, giving us access to its security and performance features to serve our apps.
Related content:
- How to run Django and Postgres in docker-compose
- How to Run Postgresql 14 with Docker and Docker-Compose
- Getting started with Django – A Simple CRUD application
Prerequisites
In order to follow along, you should have a Rocky Linux 9 server with root access or user with sudo access. We will be installing Django within a virtual environment. Installing Django into an environment specific to your project will allow your projects and their requirements to be handled separately.
Once we have our database and application up and running, we will install and configure the Gunicorn application server. This will serve as an interface to our application, translating client requests in HTTP to Python calls that our application can process. We will then set up Nginx in front of Gunicorn to take advantage of its high performance connection handling mechanisms and its easy-to-implement security features.
Step 1 – Ensure that the system is up to date and enable epel repository
Before proceeding, ensure that you have up to date packages. Use this command to update the packages on your server
sudo dnf update
Next, let us enable the epel repository:
sudo dnf install epel-release
Configuring SELinux is out of the scope of this guide, if you want to disable, open the /etc/selinux/config
file and set the SELINUX
mod to disabled
:
File /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled
To disable without rebooting the system, use the setenforce
tool as follows:
sudo setenforce 0
To view the current SELinux status and the SELinux policy that is being used on your system, use the sestatus
command:
sestatus
Step 2: Install required packages and set up database
Next let us install the required packages. Since we will use virtualenv, install the python packages and dependencies:
sudo dnf install -y python3 python3-devel python3-pip gcc
Next let us install postgres. Checkout this comprehensive guide on setting up postgres – How to Install and Configure Postgres 14 on Rocky Linux 9.
Install the repository RPM using this command:
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
Then install PostgreSQL 14 server and its dependencies:
sudo dnf install -y postgresql14 postgresql14-server postgresql14-contrib postgresql14-devel python3-psycopg2
Step 3: Set Up PostgreSQL for Django
Let us set up postgres for our application. First initialize the PostgreSQL database:
$ sudo /usr/pgsql-14/bin/postgresql-14-setup initdb
Initializing database ... OK
Start the postgres service with this command:
sudo systemctl start postgresql-14
Now connect to the postgresql so we can create the user
sudo su - postgres
psql
Then create a postgres user
create database djangoapp;
create user djangouser with encrypted password 'dbpassword';
grant all privileges on database djangoapp to djangouser;
Step 4: Create a Python Virtual Environment for your Project
We will be installing our Python requirements within a virtual environment for easier management.
To do this, we first need access to the virtualenv
command. We can install this with pip
:
sudo pip3 install virtualenv
With virtualenv
installed, we can start forming our project. Create a directory where you wish to keep your project and move into the directory afterwards:
mkdir /opt/djangoapp
cd /opt/djangoapp
Within the project directory, create a Python virtual environment by typing:
virtualenv appenv
This will create a directory called appenv
within your project directory. Inside, it will install a local version of Python and a local version of pip
. We can use this to install and configure an isolated Python environment for our project.
Before we install our project’s Python requirements, we need to activate the virtual environment. You can do that by typing:
source appenv/bin/activate
With your virtual environment active, install Django, Gunicorn, and the psycopg2
PostgreSQL adaptor with the local instance of pip
:
pip install django gunicorn psycopg2 psycopg2-binary
If you run into an issue installing the dependencies regarding pg_*
commands, append this to path before doing pip install:
export PATH=$PATH:/usr/pgsql-14/bin
Step 5 – Create and Configure a New Django Project
With our Python components installed, we can create the actual Django project files.
Since we already have a project directory, we will tell Django to install the files here. It will create a second level directory with the actual code, which is normal, and place a management script in this directory. The key to this is the dot at the end that tells Django to create the files in the current directory. This will create /opt/djangoapp/myapp
:
django-admin startproject myapp .
The first thing we should do with our newly created project files is adjust the settings. Open the settings file in your text editor:
cd myapp
vim myapp/settings.py
Start by finding the section that configures database access. It will start with DATABASES
. The configuration in the file is for a SQLite database. We already created a PostgreSQL database for our project, so we need to adjust the settings.
Change the settings with your PostgreSQL database information. We tell Django to use the psycopg2
adaptor we installed with pip
. We need to give the database name, the database username, the database username’s password, and then specify that the database is located on the local computer. You can leave the PORT
setting as an empty string. We are going to expect the settings from the env variables:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': os.environ.get('DATABASE_DB'),
'USER': os.environ.get('DATABASE_USER'),
'PASSWORD': os.environ.get('DATABASE_PASSWORD'),
'HOST': os.environ.get('DATABASE_HOST'),
'PORT': os.environ.get('DATABASE_PORT'),
},
}
Next, move down to the bottom of the file and add a setting indicating where the static files should be placed. This is necessary so that Nginx can handle requests for these items. The following line tells Django to place them in a directory called static
in the base project directory:
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
Save and close the file when you are finished.
Django initial set up
Now, we can migrate the initial database schema to our PostgreSQL database using the management script:
export DATABASE_DB=<db name>
export DATABASE_USER=<db user>
export DATABASE_PASSWORD=<db password>
export DATABASE_HOST=127.0.0.1
export DATABASE_PORT=5432
python manage.py makemigrations
python manage.py migrate
Create an administrative user for the project by typing:
python manage.py createsuperuser
You will have to select a username, provide an email address, and choose and confirm a password.
We can collect all of the static content into the directory location we configured by typing:
python manage.py collectstatic
You will have to confirm the operation. The static files will then be placed in a directory called static
within your project directory.
Finally, you can test your project by starting up the Django development server with this command:
./manage.py runserver 0.0.0.0:8000
In your web browser, visit your server’s domain name or IP address followed by :8000
:
http://server_ip_or_domain:8000
You should see the default Django index page. If you append /admin
to the end of the URL in the address bar, you will be prompted for the administrative username and password you created with the createsuperuser
command.
Testing Gunicorn’s Ability to Serve the Project
The last thing we want to do before leaving our virtual environment is test Gunicorn to make sure that it can serve the application. We can do this easily by typing:
gunicorn --bind 0.0.0.0:8000 myapp.wsgi:application
This will start Gunicorn on the same interface that the Django development server was running on. You can go back and test the app again. Note that the admin interface will not have any of the styling applied since Gunicorn does not know about the static content responsible for this.
We passed Gunicorn a module by specifying the relative directory path to Django’s wsgi.py
file, which is the entry point to our application, using Python’s module syntax. Inside of this file, a function called application
is defined, which is used to communicate with the application.
When you are finished testing, hit CTRL-C in the terminal window to stop Gunicorn.
We’re now finished configuring our Django application. We can back out of our virtual environment by typing:
deactivate
Step 6: Create a Systemd Service File
We have tested that Gunicorn can interact with our Django application, but we should implement a more robust way of starting and stopping the application server. To accomplish this, we’ll make a Systemd service file.
Create and open a Systemd service file for Gunicorn with sudo
privileges in your text editor:
sudo vim /etc/systemd/system/myapp.service
Add these content to the file:
[Unit]
Description=MyApp service
Wants=network-online.target
After=network-online.target
[Service]
User=rocky
Group=nginx
Type=simple
PIDFile=/opt/djangoapp/app.pid
Environment=ENV=live
Environment=DEBUG=1
Environment=ALLOWED_HOSTS=*
Environment=SECRET_KEY='django-secure-ATk95j*F!y2JM^5w*S2EG7uYpKUVL&#GsSa&xaFqHMgDzL5kUTj'
Environment=APP_PATH=/opt/djangoapp/myapp
Environment=DATABASE_DB=citizix
Environment=DATABASE_USER=citizix
Environment=DATABASE_PASSWORD=citizix@_24R
Environment=DATABASE_HOST=127.0.0.1
Environment=DATABASE_PORT=5432
WorkingDirectory=/opt/djangoapp/myapp
ExecStart=/opt/djangoapp/appenv/bin/gunicorn \
-t 3000 \
--bind unix:/opt/djangoapp/app.sock \
--pid /opt/djangoapp/app.pid \
--reload \
myapp.wsgi:application -w 2
[Install]
WantedBy=multi-user.target
With that, our Systemd service file is complete. Save and close it now.
We can now start the Gunicorn service we created and enable it so that it starts at boot:
sudo systemctl start myapp
sudo systemctl enable myapp
Step 7: Configure Nginx to Proxy Pass to Gunicorn
Now that Gunicorn is set up, we need to configure Nginx to pass traffic to the process.
Create a new virtualhost for our application:
sudo vim /etc/nginx/conf.d/myapp.conf
Then add these content to the file:
server {
listen 80;
server_tokens off;
client_max_body_size 100M;
server_name myapp.citizix.com;
ignore_invalid_headers off;
if ($host !~* ^(myapp.citizix.com)$ ) {
return 444;
}
location = /favicon.ico {
rewrite "/favicon.ico" /static/img/favicon.ico;
access_log off; log_not_found off;
}
location /static/ {
root /opt/djangoapp;
}
location /media/ {
root /opt/djangoapp;
}
location / {
send_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_connect_timeout 600;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/opt/djangoapp/app.sock;
}
}
Update the domain name from myapp.citizix.com
to your domain and create a DNS record for that to point to the server.
Adjust Group Membership and Permissions
The nginx
user must have access to our application directory so that it can serve static files, access the socket files, etc. We will add the nginx
user to our user’s group so that we can then open up the minimum permissions necessary to get this to function.
Add the nginx
user to your group with the following command. Substitute your own username for the user
in the command:
sudo usermod -a -G user nginx
Now, we can give our user group execute permissions on our directory. This will allow the Nginx process to enter and access content within:
chmod 710 /opt/djangoapp
With the permissions set up, we can test our Nginx configuration file for syntax errors:
sudo nginx -t
If no errors are present, restart the Nginx service by typing:
sudo systemctl start nginx
Tell the init system to start the Nginx server at boot by typing:
sudo systemctl enable nginx
You should now have access to your Django application in your browser over your server’s domain name or IP address without specifying a port.
Conclusion
In this guide, we managed to set up django to run on Rocky Linux 9 instance. We started by installing the required packages and setting up postgres then created a virtual env that we used to isolate our app dependencies.
We also learnt how to use gunicorn to serve our django app and nginx to proxy requests to the app.
1 Comment
After doing all the things, I still see the nginx welcoming page when I visit my VPS IP. Everything except visiting results are as expected according to the steps. When I run “python3 manage.py 0.0.0.0:8000” and visit http://:8000 it works but for both VPS IP and domain name they direct me to registrar welcoming pages.