Set up comprehensive nginx monitoring with Prometheus and Grafana dashboards. Configure nginx stub_status, deploy nginx-prometheus-exporter, and create production-ready alerts for web server performance metrics.
Prerequisites
- Root access to server
- Basic nginx knowledge
- Understanding of web server metrics
What this solves
This tutorial sets up comprehensive nginx monitoring using Prometheus and Grafana. You'll configure nginx to expose performance metrics, deploy nginx-prometheus-exporter to collect them, and create dashboards that track requests, response times, active connections, and server health in real time.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you have the latest versions available.
sudo apt update && sudo apt upgrade -y
Install nginx if not already present
Install nginx web server if you don't have it running already.
sudo apt install -y nginx
Configure nginx stub_status module
Enable nginx's built-in stub_status module to expose basic performance metrics. This creates an endpoint that shows active connections, requests, and server statistics.
server {
listen 8080;
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
}
}
Enable the status configuration
Link the status configuration to sites-enabled and test the nginx configuration for syntax errors.
sudo ln -s /etc/nginx/sites-available/nginx-status /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Test nginx status endpoint
Verify that nginx is exposing metrics correctly by testing the status endpoint.
curl http://localhost:8080/nginx_status
You should see output similar to:
Active connections: 1
server accepts handled requests
1 1 1
Reading: 0 Writing: 1 Waiting: 0
Install nginx-prometheus-exporter
Download and install the nginx-prometheus-exporter binary. This tool scrapes nginx metrics and converts them to Prometheus format.
cd /tmp
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v1.1.0/nginx-prometheus-exporter_1.1.0_linux_amd64.tar.gz
tar -xzf nginx-prometheus-exporter_1.1.0_linux_amd64.tar.gz
sudo mv nginx-prometheus-exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/nginx-prometheus-exporter
Create nginx exporter user
Create a dedicated system user for the nginx exporter service for security isolation.
sudo useradd --no-create-home --shell /bin/false nginx-exporter
Create systemd service file
Create a systemd service file to manage the nginx-prometheus-exporter as a system service.
[Unit]
Description=NGINX Prometheus Exporter
After=network.target
Wants=network.target
[Service]
User=nginx-exporter
Group=nginx-exporter
Type=simple
ExecStart=/usr/local/bin/nginx-prometheus-exporter \
-nginx.scrape-uri=http://localhost:8080/nginx_status \
-web.listen-address=:9113 \
-web.telemetry-path=/metrics
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Start and enable nginx exporter
Start the nginx exporter service and enable it to start automatically on boot.
sudo systemctl daemon-reload
sudo systemctl enable --now nginx-exporter
sudo systemctl status nginx-exporter
Test nginx exporter metrics
Verify that the exporter is working by checking the Prometheus metrics endpoint.
curl http://localhost:9113/metrics | grep nginx
You should see metrics like nginx_connections_active, nginx_http_requests_total, and nginx_connections_handled.
Install Prometheus
Download and install Prometheus to collect metrics from the nginx exporter.
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-2.48.0.linux-amd64.tar.gz
tar -xzf prometheus-2.48.0.linux-amd64.tar.gz
sudo mv prometheus-2.48.0.linux-amd64 /opt/prometheus
sudo ln -s /opt/prometheus/prometheus /usr/local/bin/prometheus
Create Prometheus user and directories
Set up the Prometheus user and required directories with correct permissions.
sudo useradd --no-create-home --shell /bin/false prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus
Configure Prometheus
Create Prometheus configuration to scrape metrics from the nginx exporter and Prometheus itself.
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "nginx_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets: []
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'nginx'
static_configs:
- targets: ['localhost:9113']
scrape_interval: 5s
metrics_path: /metrics
Create nginx alerting rules
Define alerting rules for nginx performance monitoring and availability checks.
groups:
- name: nginx_alerts
rules:
- alert: NginxDown
expr: up{job="nginx"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Nginx exporter is down"
description: "Nginx prometheus exporter has been down for more than 1 minute"
- alert: NginxHighConnections
expr: nginx_connections_active > 100
for: 5m
labels:
severity: warning
annotations:
summary: "High nginx connections"
description: "Nginx has {{ $value }} active connections"
- alert: NginxHighRequestRate
expr: rate(nginx_http_requests_total[5m]) > 100
for: 2m
labels:
severity: warning
annotations:
summary: "High nginx request rate"
description: "Nginx request rate is {{ $value }} requests per second"
Set configuration file permissions
Set correct ownership for Prometheus configuration files.
sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
sudo chown prometheus:prometheus /etc/prometheus/nginx_rules.yml
Create Prometheus systemd service
Create systemd service file for Prometheus with security hardening options.
[Unit]
Description=Prometheus Server
Documentation=https://prometheus.io/docs/
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--web.console.templates=/opt/prometheus/consoles \
--web.console.libraries=/opt/prometheus/console_libraries \
--web.listen-address=0.0.0.0:9090 \
--web.enable-lifecycle \
--log.level=info
[Install]
WantedBy=multi-user.target
Start Prometheus service
Start Prometheus and enable it to run automatically on system boot.
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl status prometheus
Install Grafana
Add Grafana repository and install Grafana for creating monitoring dashboards.
sudo apt install -y software-properties-common wget
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 -a /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
Start Grafana service
Enable and start Grafana service on port 3000.
sudo systemctl enable --now grafana-server
sudo systemctl status grafana-server
Configure firewall rules
Open firewall ports for Prometheus and Grafana web interfaces.
sudo ufw allow 3000/tcp
sudo ufw allow 9090/tcp
sudo ufw reload
Access Grafana and add Prometheus data source
Open Grafana in your browser and configure Prometheus as a data source. The default login is admin/admin.
Navigate to http://your-server-ip:3000 and log in. Go to Configuration > Data Sources > Add data source > Prometheus. Set the URL to http://localhost:9090 and click Save & Test.
Create nginx dashboard
Create a comprehensive dashboard with nginx performance panels. Go to Create > Dashboard > Add new panel and use these queries:
| Panel Name | Query | Description |
|---|---|---|
| Active Connections | nginx_connections_active | Current active connections |
| Request Rate | rate(nginx_http_requests_total[5m]) | Requests per second |
| Connection States | nginx_connections_reading, nginx_connections_writing, nginx_connections_waiting | Connection breakdown |
| Total Requests | nginx_http_requests_total | Cumulative request count |
Verify your setup
Check that all services are running correctly and metrics are being collected.
sudo systemctl status nginx nginx-exporter prometheus grafana-server
curl http://localhost:8080/nginx_status
curl http://localhost:9113/metrics | grep nginx_connections_active
curl http://localhost:9090/api/v1/query?query=up
Generate some traffic to test monitoring:
for i in {1..100}; do curl -s http://localhost/ > /dev/null; done
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| nginx_status returns 403 | IP not allowed in allow list | Add your IP to nginx status config allow directives |
| nginx-exporter connection refused | nginx status endpoint not accessible | Check nginx status config and restart nginx |
| Prometheus targets down | Firewall blocking ports or services not running | Check service status and firewall rules |
| Grafana shows no data | Prometheus data source misconfigured | Verify Prometheus URL in Grafana data source settings |
| Permission denied errors | Incorrect file ownership | Run sudo chown prometheus:prometheus /etc/prometheus/* |
Next steps
- Configure Prometheus Alertmanager for email notifications
- Implement Grafana advanced alerting with webhooks
- Optimize NGINX performance for high-traffic websites
- Configure NGINX log analysis with Loki and Grafana
- Set up nginx SSL certificate monitoring and health checks
Running this in production?
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'
NC='\033[0m'
# Default values
NGINX_STATUS_PORT=${1:-8080}
EXPORTER_PORT=${2:-9113}
PROMETHEUS_PORT=${3:-9090}
GRAFANA_PORT=${4:-3000}
# Usage message
usage() {
echo "Usage: $0 [nginx_status_port] [exporter_port] [prometheus_port] [grafana_port]"
echo "Defaults: nginx_status=8080, exporter=9113, prometheus=9090, grafana=3000"
exit 1
}
# Cleanup function
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
systemctl stop nginx-exporter prometheus grafana-server 2>/dev/null || true
systemctl disable nginx-exporter prometheus grafana-server 2>/dev/null || true
userdel -r nginx-exporter prometheus grafana 2>/dev/null || true
rm -rf /opt/prometheus /opt/grafana /etc/prometheus /var/lib/prometheus /var/lib/grafana
rm -f /usr/local/bin/nginx-prometheus-exporter /usr/local/bin/prometheus
rm -f /etc/systemd/system/nginx-exporter.service /etc/systemd/system/prometheus.service
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Auto-detect distro
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
NGINX_SITES_AVAILABLE="/etc/nginx/sites-available"
NGINX_SITES_ENABLED="/etc/nginx/sites-enabled"
USE_SITES_DIRS=true
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
NGINX_SITES_AVAILABLE="/etc/nginx/conf.d"
NGINX_SITES_ENABLED="/etc/nginx/conf.d"
USE_SITES_DIRS=false
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
NGINX_SITES_AVAILABLE="/etc/nginx/conf.d"
NGINX_SITES_ENABLED="/etc/nginx/conf.d"
USE_SITES_DIRS=false
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect OS distribution${NC}"
exit 1
fi
echo -e "${GREEN}[1/12] Updating system packages...${NC}"
$PKG_UPDATE
echo -e "${GREEN}[2/12] Installing nginx...${NC}"
$PKG_INSTALL nginx curl wget tar
echo -e "${GREEN}[3/12] Configuring nginx stub_status module...${NC}"
if [ "$USE_SITES_DIRS" = true ]; then
cat > "$NGINX_SITES_AVAILABLE/nginx-status" << EOF
server {
listen ${NGINX_STATUS_PORT};
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
}
}
EOF
ln -sf "$NGINX_SITES_AVAILABLE/nginx-status" "$NGINX_SITES_ENABLED/nginx-status"
else
cat > "$NGINX_SITES_AVAILABLE/nginx-status.conf" << EOF
server {
listen ${NGINX_STATUS_PORT};
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
}
}
EOF
fi
nginx -t
systemctl enable --now nginx
systemctl reload nginx
echo -e "${GREEN}[4/12] Testing nginx status endpoint...${NC}"
sleep 2
curl -f http://localhost:${NGINX_STATUS_PORT}/nginx_status > /dev/null
echo -e "${GREEN}[5/12] Installing nginx-prometheus-exporter...${NC}"
cd /tmp
wget -q https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v1.1.0/nginx-prometheus-exporter_1.1.0_linux_amd64.tar.gz
tar -xzf nginx-prometheus-exporter_1.1.0_linux_amd64.tar.gz
mv nginx-prometheus-exporter /usr/local/bin/
chmod 755 /usr/local/bin/nginx-prometheus-exporter
echo -e "${GREEN}[6/12] Creating nginx-exporter user and service...${NC}"
useradd --no-create-home --shell /bin/false nginx-exporter || true
cat > /etc/systemd/system/nginx-exporter.service << EOF
[Unit]
Description=NGINX Prometheus Exporter
After=network.target nginx.service
Wants=network.target
[Service]
User=nginx-exporter
Group=nginx-exporter
Type=simple
ExecStart=/usr/local/bin/nginx-prometheus-exporter \\
-nginx.scrape-uri=http://localhost:${NGINX_STATUS_PORT}/nginx_status \\
-web.listen-address=:${EXPORTER_PORT} \\
-web.telemetry-path=/metrics
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now nginx-exporter
echo -e "${GREEN}[7/12] Testing nginx exporter...${NC}"
sleep 3
curl -f http://localhost:${EXPORTER_PORT}/metrics | grep -q nginx
echo -e "${GREEN}[8/12] Installing Prometheus...${NC}"
cd /tmp
wget -q https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-2.48.0.linux-amd64.tar.gz
tar -xzf prometheus-2.48.0.linux-amd64.tar.gz
mv prometheus-2.48.0.linux-amd64 /opt/prometheus
ln -sf /opt/prometheus/prometheus /usr/local/bin/prometheus
useradd --no-create-home --shell /bin/false prometheus || true
mkdir -p /etc/prometheus /var/lib/prometheus
chown prometheus:prometheus /var/lib/prometheus
cat > /etc/prometheus/prometheus.yml << EOF
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:${PROMETHEUS_PORT}']
- job_name: 'nginx'
static_configs:
- targets: ['localhost:${EXPORTER_PORT}']
EOF
chown -R prometheus:prometheus /etc/prometheus
cat > /etc/systemd/system/prometheus.service << EOF
[Unit]
Description=Prometheus Server
Documentation=https://prometheus.io/docs/
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
Restart=on-failure
ExecStart=/usr/local/bin/prometheus \\
--config.file=/etc/prometheus/prometheus.yml \\
--storage.tsdb.path=/var/lib/prometheus/ \\
--web.console.templates=/opt/prometheus/consoles \\
--web.console.libraries=/opt/prometheus/console_libraries \\
--web.listen-address=0.0.0.0:${PROMETHEUS_PORT} \\
--web.external-url=
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now prometheus
echo -e "${GREEN}[9/12] Installing Grafana...${NC}"
if [ "$PKG_MGR" = "apt" ]; then
$PKG_INSTALL gnupg2 software-properties-common
wget -q -O - https://packages.grafana.com/gpg.key | apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" > /etc/apt/sources.list.d/grafana.list
apt update
$PKG_INSTALL grafana
else
cat > /etc/yum.repos.d/grafana.repo << EOF
[grafana]
name=grafana
baseurl=https://packages.grafana.com/oss/rpm
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packages.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
$PKG_INSTALL grafana
fi
echo -e "${GREEN}[10/12] Configuring Grafana...${NC}"
systemctl enable --now grafana-server
echo -e "${GREEN}[11/12] Configuring firewall...${NC}"
if command -v ufw >/dev/null 2>&1; then
ufw allow ${NGINX_STATUS_PORT}/tcp
ufw allow ${EXPORTER_PORT}/tcp
ufw allow ${PROMETHEUS_PORT}/tcp
ufw allow ${GRAFANA_PORT}/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=${NGINX_STATUS_PORT}/tcp
firewall-cmd --permanent --add-port=${EXPORTER_PORT}/tcp
firewall-cmd --permanent --add-port=${PROMETHEUS_PORT}/tcp
firewall-cmd --permanent --add-port=${GRAFANA_PORT}/tcp
firewall-cmd --reload
fi
echo -e "${GREEN}[12/12] Final verification...${NC}"
sleep 5
# Verify all services
systemctl is-active --quiet nginx || (echo -e "${RED}Nginx is not running${NC}" && exit 1)
systemctl is-active --quiet nginx-exporter || (echo -e "${RED}Nginx exporter is not running${NC}" && exit 1)
systemctl is-active --quiet prometheus || (echo -e "${RED}Prometheus is not running${NC}" && exit 1)
systemctl is-active --quiet grafana-server || (echo -e "${RED}Grafana is not running${NC}" && exit 1)
# Test endpoints
curl -sf http://localhost:${NGINX_STATUS_PORT}/nginx_status >/dev/null || (echo -e "${RED}Nginx status endpoint failed${NC}" && exit 1)
curl -sf http://localhost:${EXPORTER_PORT}/metrics | grep -q nginx || (echo -e "${RED}Nginx exporter failed${NC}" && exit 1)
curl -sf http://localhost:${PROMETHEUS_PORT}/-/healthy >/dev/null || (echo -e "${RED}Prometheus health check failed${NC}" && exit 1)
trap - ERR
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${YELLOW}Access URLs:${NC}"
echo " Nginx Status: http://localhost:${NGINX_STATUS_PORT}/nginx_status"
echo " Nginx Metrics: http://localhost:${EXPORTER_PORT}/metrics"
echo " Prometheus: http://localhost:${PROMETHEUS_PORT}"
echo " Grafana: http://localhost:${GRAFANA_PORT} (admin/admin)"
Review the script before running. Execute with: bash install.sh