Django is a high-level Python web framework that follows the “batteries included” philosophy — it provides an ORM, authentication, admin interface, form validation, URL routing, templating, and security middleware out of the box. This makes Django the framework of choice for data-driven applications and content management systems where rapid development and maintainability matter. Django’s ORM supports PostgreSQL, MySQL, SQLite, and Oracle, and its built-in admin interface auto-generates a management UI from model definitions. For production deployment, Django applications are typically served via Gunicorn (a WSGI server) behind Nginx, which handles SSL termination and static file serving. This guide covers deploying a Django 5 application with Gunicorn and Nginx on RHEL 9 with a PostgreSQL database backend.

Prerequisites

  • Python 3.12 installed on RHEL 9
  • Nginx and PostgreSQL 16 installed

Step 1 — Create the Django Project

mkdir /var/www/mydjango && cd /var/www/mydjango
python3.12 -m venv .venv
source .venv/bin/activate

pip install django gunicorn psycopg2-binary python-dotenv whitenoise

django-admin startproject mysite .
python manage.py startapp core

Step 2 — Configure settings.py for Production

# mysite/settings.py — production settings
from dotenv import load_dotenv
import os
load_dotenv()

SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
DEBUG = False
ALLOWED_HOSTS = ['mydjango.example.com']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME', 'mydjango_db'),
        'USER': os.environ.get('DB_USER', 'mydjango_user'),
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

# Serve static files via WhiteNoise (production)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
]
STATIC_ROOT = '/var/www/mydjango/staticfiles'
STATIC_URL = '/static/'
STORAGES = {"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"}}

Step 3 — Prepare the Application

cd /var/www/mydjango

# Collect static files
python manage.py collectstatic --noinput

# Run migrations
python manage.py migrate

# Create superuser for admin
python manage.py createsuperuser

Step 4 — Create Gunicorn systemd Service

# /etc/systemd/system/mydjango.service
[Unit]
Description=Django/Gunicorn for mydjango
After=network.target

[Service]
User=nginx
Group=nginx
WorkingDirectory=/var/www/mydjango
EnvironmentFile=/var/www/mydjango/.env
ExecStart=/var/www/mydjango/.venv/bin/gunicorn 
    --workers 4 
    --bind unix:/run/mydjango.sock 
    --access-logfile /var/log/mydjango/access.log 
    --error-logfile /var/log/mydjango/error.log 
    mysite.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target
mkdir -p /var/log/mydjango
chown nginx:nginx /var/log/mydjango
systemctl daemon-reload
systemctl enable --now mydjango

Step 5 — Configure Nginx Reverse Proxy

# /etc/nginx/conf.d/mydjango.conf
server {
    listen 80;
    server_name mydjango.example.com;

    location /static/ {
        alias /var/www/mydjango/staticfiles/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location / {
        proxy_pass http://unix:/run/mydjango.sock;
        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-Proto $scheme;
    }
}
nginx -t && systemctl reload nginx

Conclusion

Django with Gunicorn and Nginx on RHEL 9 is the standard production deployment stack. Gunicorn’s worker count should be set to 2 × CPU_CORES + 1 for CPU-bound applications, or higher for I/O-bound applications that spend most of their time waiting on database queries. The WhiteNoise middleware eliminates the need to configure Nginx to serve static files separately and is production-safe with compressed, cache-optimised static file delivery built in.

Next steps: How to Deploy a Flask Application on RHEL 9, How to Install Python 3 on RHEL 9, and How to Configure PostgreSQL on RHEL 9.