Set up comprehensive Django application monitoring using Prometheus metrics collection and Grafana dashboards. Configure django-prometheus middleware to track request metrics, database queries, and application performance with real-time alerting.
Prerequisites
- Django application running
- Python 3.8+
- 500MB free disk space
- 2GB RAM
- sudo privileges
What this solves
Django applications in production need comprehensive monitoring to track performance bottlenecks, database query efficiency, and user experience metrics. This tutorial sets up Prometheus to collect Django application metrics and Grafana to visualize performance data with automated alerting for critical issues.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of all dependencies.
sudo apt update && sudo apt upgrade -y
Install system dependencies
Install Python development tools and system packages required for Prometheus and monitoring components.
sudo apt install -y python3-pip python3-venv wget curl gnupg2 software-properties-common
Install Prometheus server
Create a prometheus user and download the latest Prometheus release for metrics collection.
sudo useradd --no-create-home --shell /bin/false prometheus
sudo mkdir /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.45.0/prometheus-2.45.0.linux-amd64.tar.gz
tar xf prometheus-2.45.0.linux-amd64.tar.gz
cd prometheus-2.45.0.linux-amd64
sudo cp prometheus promtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
sudo cp -r consoles console_libraries /etc/prometheus/
sudo chown -R prometheus:prometheus /etc/prometheus/consoles /etc/prometheus/console_libraries
Configure Prometheus
Create the main Prometheus configuration file with Django application scrape targets.
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "django_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'django-app'
static_configs:
- targets: ['localhost:8000']
metrics_path: '/metrics'
scrape_interval: 5s
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
Create Prometheus systemd service
Set up systemd service file to manage Prometheus as a system service with proper user permissions.
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file /etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090 \
--web.enable-lifecycle
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
Install Node Exporter
Install Node Exporter to collect system-level metrics alongside Django application metrics.
cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
tar xf node_exporter-1.6.1.linux-amd64.tar.gz
sudo cp node_exporter-1.6.1.linux-amd64/node_exporter /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/node_exporter
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now node-exporter
Install django-prometheus
Install the django-prometheus package to instrument your Django application with Prometheus metrics.
cd /path/to/your/django/project
pip install django-prometheus
Configure Django settings
Add django-prometheus to your Django application settings to enable metrics collection middleware.
# Add to INSTALLED_APPS
INSTALLED_APPS = [
# ... your other apps
'django_prometheus',
]
Add prometheus middleware (order matters)
MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware',
# ... your other middleware
'django_prometheus.middleware.PrometheusAfterMiddleware',
]
Database monitoring
DATABASES = {
'default': {
'ENGINE': 'django_prometheus.db.backends.postgresql', # or mysql, sqlite3
'NAME': 'your_database_name',
'USER': 'your_db_user',
'PASSWORD': 'your_db_password',
'HOST': 'localhost',
'PORT': '5432',
}
}
Cache monitoring (if using cache)
CACHES = {
'default': {
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
Add metrics URL pattern
Configure URL routing to expose Prometheus metrics endpoint for scraping.
from django.urls import path, include
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
path('metrics/', include('django_prometheus.urls')),
# ... your other URL patterns
]
Install Grafana
Install Grafana for visualizing Django metrics with comprehensive dashboards and alerting.
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
Configure Grafana
Configure Grafana with security settings and enable it as a systemd service.
[server]
http_port = 3000
domain = localhost
root_url = http://localhost:3000
[security]
admin_user = admin
admin_password = secure_password_here
secret_key = your_secret_key_here
[database]
type = sqlite3
path = grafana.db
[session]
provider = file
provider_config = sessions
[analytics]
reporting_enabled = false
check_for_updates = true
sudo systemctl daemon-reload
sudo systemctl enable --now grafana-server
Create Django alerting rules
Configure Prometheus alerting rules for Django application performance monitoring and error detection.
groups:
- name: django_alerts
rules:
- alert: DjangoHighErrorRate
expr: rate(django_http_requests_total{status=~"4..|5.."}[5m]) > 0.1
for: 2m
labels:
severity: critical
annotations:
summary: "High error rate in Django application"
description: "Error rate is {{ $value }} errors per second"
- alert: DjangoHighResponseTime
expr: histogram_quantile(0.95, rate(django_http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "High response time in Django application"
description: "95th percentile response time is {{ $value }} seconds"
- alert: DjangoHighDatabaseConnections
expr: django_db_connections > 80
for: 3m
labels:
severity: warning
annotations:
summary: "High database connection count"
description: "Database connections: {{ $value }}"
- alert: DjangoCacheHitRateLow
expr: rate(django_cache_get_hits_total[5m]) / rate(django_cache_get_total[5m]) < 0.8
for: 5m
labels:
severity: info
annotations:
summary: "Low cache hit rate"
description: "Cache hit rate is {{ $value | humanizePercentage }}"
- alert: DjangoHighMemoryUsage
expr: django_cache_memory_usage > 0.9
for: 3m
labels:
severity: critical
annotations:
summary: "Django cache memory usage high"
description: "Memory usage at {{ $value | humanizePercentage }}"
- alert: DjangoSlowDatabaseQueries
expr: histogram_quantile(0.95, rate(django_db_query_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "Slow database queries detected"
description: "95th percentile query time: {{ $value }} seconds"
sudo chown prometheus:prometheus /etc/prometheus/django_rules.yml
sudo systemctl reload prometheus
Install Alertmanager
Install Prometheus Alertmanager for handling and routing alerts from Django monitoring rules.
cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.25.0/alertmanager-0.25.0.linux-amd64.tar.gz
tar xf alertmanager-0.25.0.linux-amd64.tar.gz
sudo cp alertmanager-0.25.0.linux-amd64/alertmanager /usr/local/bin/
sudo cp alertmanager-0.25.0.linux-amd64/amtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/alertmanager /usr/local/bin/amtool
sudo mkdir /etc/alertmanager /var/lib/alertmanager
sudo chown prometheus:prometheus /etc/alertmanager /var/lib/alertmanager
Configure Alertmanager
Set up Alertmanager configuration for email notifications when Django application issues are detected.
global:
smtp_smarthost: 'localhost:587'
smtp_from: 'alerts@example.com'
smtp_auth_username: 'your-smtp-username'
smtp_auth_password: 'your-smtp-password'
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'django-alerts'
receivers:
- name: 'django-alerts'
email_configs:
- to: 'admin@example.com'
subject: 'Django Alert: {{ .GroupLabels.alertname }}'
body: |
{{ range .Alerts }}
Alert: {{ .Annotations.summary }}
Description: {{ .Annotations.description }}
{{ end }}
sudo chown prometheus:prometheus /etc/alertmanager/alertmanager.yml
Create Alertmanager service
Configure systemd service for Alertmanager to handle Django monitoring alerts.
[Unit]
Description=Alertmanager
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/alertmanager \
--config.file /etc/alertmanager/alertmanager.yml \
--storage.path /var/lib/alertmanager/ \
--web.external-url=http://localhost:9093
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager
Create custom Django metrics
Add custom metrics to track specific Django application business logic and performance indicators.
from prometheus_client import Counter, Histogram, Gauge
from django.utils.deprecation import MiddlewareMixin
import time
Custom metrics for Django application
user_registrations = Counter('django_user_registrations_total', 'Total user registrations')
active_users = Gauge('django_active_users', 'Currently active users')
order_value = Histogram('django_order_value_dollars', 'Order values in dollars')
background_tasks = Gauge('django_background_tasks_running', 'Running background tasks')
api_requests = Counter('django_api_requests_total', 'API requests', ['method', 'endpoint', 'status'])
class CustomMetricsMiddleware(MiddlewareMixin):
def process_request(self, request):
request.start_time = time.time()
return None
def process_response(self, request, response):
if hasattr(request, 'start_time'):
if request.path.startswith('/api/'):
api_requests.labels(
method=request.method,
endpoint=request.path,
status=response.status_code
).inc()
return response
Configure firewall rules
Open firewall ports for Prometheus, Grafana, and metrics endpoints access.
sudo ufw allow 3000/tcp comment 'Grafana'
sudo ufw allow 9090/tcp comment 'Prometheus'
sudo ufw allow 9093/tcp comment 'Alertmanager'
sudo ufw allow 9100/tcp comment 'Node Exporter'
Restart Django application
Restart your Django application to load the prometheus middleware and begin collecting metrics.
cd /path/to/your/django/project
python manage.py collectstatic --noinput
python manage.py migrate
sudo systemctl restart your-django-app
Configure Grafana dashboards
Access Grafana web interface
Open your web browser and navigate to Grafana to begin dashboard configuration.
http://your-server-ip:3000
Login with username admin and the password you set in the configuration file.
Add Prometheus data source
Configure Prometheus as a data source in Grafana for Django metrics visualization.
- Click "Add data source" or go to Configuration > Data Sources
- Select "Prometheus" from the list
- Set URL to
http://localhost:9090 - Set Access to "Server (default)"
- Click "Save & Test" to verify connection
Import Django dashboard
Create a comprehensive Django monitoring dashboard with performance metrics and alerts.
{
"dashboard": {
"title": "Django Application Monitoring",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [{
"expr": "rate(django_http_requests_total[5m])",
"legendFormat": "{{ method }} {{ handler }}"
}]
},
{
"title": "Response Time (95th percentile)",
"type": "graph",
"targets": [{
"expr": "histogram_quantile(0.95, rate(django_http_request_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
}]
},
{
"title": "Error Rate",
"type": "graph",
"targets": [{
"expr": "rate(django_http_requests_total{status=~\"4..|5..\"}[5m])",
"legendFormat": "Errors"
}]
},
{
"title": "Database Query Time",
"type": "graph",
"targets": [{
"expr": "histogram_quantile(0.95, rate(django_db_query_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
}]
},
{
"title": "Active Database Connections",
"type": "singlestat",
"targets": [{
"expr": "django_db_connections",
"legendFormat": "Connections"
}]
},
{
"title": "Cache Hit Rate",
"type": "graph",
"targets": [{
"expr": "rate(django_cache_get_hits_total[5m]) / rate(django_cache_get_total[5m])",
"legendFormat": "Hit Rate"
}]
}
]
}
}
Import this dashboard by going to Dashboards > Import and pasting the JSON configuration.
Verify your setup
# Check service status
sudo systemctl status prometheus grafana-server alertmanager node-exporter
Verify Django metrics endpoint
curl http://localhost:8000/metrics
Check Prometheus targets
curl http://localhost:9090/api/v1/targets
Test Grafana API
curl http://admin:your_password@localhost:3000/api/health
# Check Django metrics collection
python manage.py shell -c "from django_prometheus.models import *; print('Metrics collected successfully')"
Verify database monitoring
grep -i "django_db" /var/log/prometheus.log
Test alerting rules
curl http://localhost:9090/api/v1/rules
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Metrics endpoint returns 404 | URL pattern not configured | Add path('metrics/', include('django_prometheus.urls')) to urls.py |
| No database metrics visible | Wrong database engine | Use django_prometheus.db.backends.postgresql instead of django.db.backends.postgresql |
| Grafana shows no data | Prometheus data source misconfigured | Verify Prometheus URL is http://localhost:9090 in data source settings |
| High memory usage from metrics | Too many metric labels | Reduce cardinality by limiting dynamic labels in custom metrics |
| Alerts not firing | Alertmanager not connected | Check /etc/prometheus/prometheus.yml alerting section points to port 9093 |
| Permission denied accessing metrics | Middleware not in correct order | Place PrometheusBeforeMiddleware first and PrometheusAfterMiddleware last |
Next steps
- Configure advanced Grafana dashboards and alerting with Prometheus integration
- Setup centralized log aggregation with Elasticsearch, Logstash, and Kibana (ELK Stack)
- Implement Django application performance optimization with Redis caching
- Configure Django database performance monitoring with PostgreSQL and slow query analysis
- Setup Django application security monitoring with fail2ban and intrusion detection
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default versions
PROMETHEUS_VERSION="2.45.0"
NODE_EXPORTER_VERSION="1.6.1"
DJANGO_PROJECT_PATH=""
# Usage message
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -p PATH Django project path (optional)"
echo " -h Show this help message"
echo ""
echo "Example: $0 -p /opt/myapp"
exit 1
}
# Parse arguments
while getopts "p:h" opt; do
case $opt in
p) DJANGO_PROJECT_PATH="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
# Cleanup function
cleanup() {
echo -e "${RED}[ERROR]${NC} Installation failed. Cleaning up..."
systemctl stop prometheus node-exporter 2>/dev/null || true
systemctl disable prometheus node-exporter 2>/dev/null || true
rm -f /etc/systemd/system/prometheus.service /etc/systemd/system/node-exporter.service
rm -rf /tmp/prometheus-* /tmp/node_exporter-*
echo -e "${YELLOW}[CLEANUP]${NC} Partial installation cleaned up"
}
trap cleanup ERR
# Check if running as root or with sudo
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}[ERROR]${NC} This script must be run as root or with sudo"
exit 1
fi
echo -e "${BLUE}[INFO]${NC} Starting Django Prometheus & Grafana monitoring setup..."
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
PYTHON_PKG="python3-pip python3-venv"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
PYTHON_PKG="python3-pip python3-virtualenv"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
PYTHON_PKG="python3-pip python3-virtualenv"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
PYTHON_PKG="python3-pip python3-virtualenv"
;;
*)
echo -e "${RED}[ERROR]${NC} Unsupported distribution: $ID"
exit 1
;;
esac
else
echo -e "${RED}[ERROR]${NC} Cannot detect Linux distribution"
exit 1
fi
echo -e "${GREEN}[1/9]${NC} Updating system packages for $PRETTY_NAME..."
$PKG_UPDATE
echo -e "${GREEN}[2/9]${NC} Installing system dependencies..."
$PKG_INSTALL $PYTHON_PKG wget curl gnupg2 tar
echo -e "${GREEN}[3/9]${NC} Creating prometheus user and directories..."
if ! id prometheus &>/dev/null; then
useradd --no-create-home --shell /bin/false prometheus
fi
mkdir -p /etc/prometheus /var/lib/prometheus
chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
echo -e "${GREEN}[4/9]${NC} Installing Prometheus server..."
cd /tmp
wget -q "https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz"
tar xf "prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz"
cd "prometheus-${PROMETHEUS_VERSION}.linux-amd64"
cp prometheus promtool /usr/local/bin/
chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
cp -r consoles console_libraries /etc/prometheus/
chown -R prometheus:prometheus /etc/prometheus/consoles /etc/prometheus/console_libraries
echo -e "${GREEN}[5/9]${NC} Configuring Prometheus..."
cat > /etc/prometheus/prometheus.yml << 'EOF'
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "django_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'django-app'
static_configs:
- targets: ['localhost:8000']
metrics_path: '/metrics'
scrape_interval: 5s
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
EOF
chown prometheus:prometheus /etc/prometheus/prometheus.yml
echo -e "${GREEN}[6/9]${NC} Creating Prometheus systemd service..."
cat > /etc/systemd/system/prometheus.service << 'EOF'
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file /etc/prometheus/prometheus.yml \
--storage.tsdb.path /var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090 \
--web.enable-lifecycle
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable prometheus
echo -e "${GREEN}[7/9]${NC} Installing Node Exporter..."
cd /tmp
wget -q "https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VERSION}/node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz"
tar xf "node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz"
cp "node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64/node_exporter" /usr/local/bin/
chown prometheus:prometheus /usr/local/bin/node_exporter
cat > /etc/systemd/system/node-exporter.service << 'EOF'
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable node-exporter
echo -e "${GREEN}[8/9]${NC} Configuring firewall..."
if command -v firewall-cmd &>/dev/null && systemctl is-active firewalld &>/dev/null; then
firewall-cmd --permanent --add-port=9090/tcp --add-port=9100/tcp
firewall-cmd --reload
elif command -v ufw &>/dev/null && ufw status | grep -q "Status: active"; then
ufw allow 9090/tcp
ufw allow 9100/tcp
fi
echo -e "${GREEN}[9/9]${NC} Starting services..."
systemctl start prometheus node-exporter
# Django setup if path provided
if [[ -n "$DJANGO_PROJECT_PATH" ]]; then
echo -e "${BLUE}[INFO]${NC} Setting up Django prometheus integration..."
if [[ -d "$DJANGO_PROJECT_PATH" ]]; then
cd "$DJANGO_PROJECT_PATH"
if [[ -f "requirements.txt" ]]; then
echo "django-prometheus" >> requirements.txt
fi
echo -e "${YELLOW}[WARNING]${NC} Don't forget to add django_prometheus to INSTALLED_APPS in settings.py"
echo -e "${YELLOW}[WARNING]${NC} Add prometheus middleware to MIDDLEWARE in settings.py"
else
echo -e "${YELLOW}[WARNING]${NC} Django project path not found: $DJANGO_PROJECT_PATH"
fi
fi
echo -e "${GREEN}[SUCCESS]${NC} Verification checks..."
if systemctl is-active prometheus &>/dev/null; then
echo -e " ✓ Prometheus service is running"
else
echo -e " ${RED}✗${NC} Prometheus service failed to start"
fi
if systemctl is-active node-exporter &>/dev/null; then
echo -e " ✓ Node Exporter service is running"
else
echo -e " ${RED}✗${NC} Node Exporter service failed to start"
fi
if curl -s http://localhost:9090/-/ready &>/dev/null; then
echo -e " ✓ Prometheus web interface accessible"
else
echo -e " ${YELLOW}⚠${NC} Prometheus web interface not yet ready (may take a moment)"
fi
echo -e "${BLUE}[INFO]${NC} Installation completed!"
echo -e "${BLUE}[INFO]${NC} Prometheus: http://localhost:9090"
echo -e "${BLUE}[INFO]${NC} Node Exporter metrics: http://localhost:9100/metrics"
trap - ERR
Review the script before running. Execute with: bash install.sh