Set up NGINX log analysis and monitoring with Prometheus and Grafana dashboards

Intermediate 45 min Apr 18, 2026 512 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Configure comprehensive NGINX monitoring with Prometheus exporters, structured log parsing, and real-time Grafana dashboards for web server observability and performance analysis.

Prerequisites

  • Root or sudo access
  • NGINX web server installed
  • At least 2GB RAM
  • 10GB available disk space

What this solves

NGINX logs contain valuable data about request patterns, response times, error rates, and traffic sources, but analyzing them manually becomes impossible at scale. This tutorial shows you how to transform raw NGINX access and error logs into structured metrics using Prometheus exporters and create real-time monitoring dashboards in Grafana. You'll get automated alerting for issues like high error rates, slow response times, and traffic anomalies before they impact users.

Step-by-step configuration

Update system packages

Start with fresh package repositories to ensure you get the latest versions of all monitoring tools.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install required packages

Install NGINX if not already present, plus tools for downloading and managing the monitoring stack.

sudo apt install -y nginx curl wget tar
sudo dnf install -y nginx curl wget tar

Configure NGINX status module

Enable the NGINX status module to expose basic server metrics that Prometheus can scrape. Add a status location block to your main server configuration.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    # Your existing configuration
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name _;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # Status endpoint for monitoring
    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;
    }
}

Configure structured NGINX logging

Replace the default NGINX log format with a structured JSON format that includes response times, upstream information, and request details for better parsing.

http {
    # JSON log format for better parsing
    log_format json_analytics escape=json '
    {
        "time": "$time_iso8601",
        "remote_addr": "$remote_addr",
        "remote_user": "$remote_user",
        "request": "$request",
        "status": $status,
        "body_bytes_sent": $body_bytes_sent,
        "request_time": $request_time,
        "upstream_response_time": "$upstream_response_time",
        "http_referrer": "$http_referer",
        "http_user_agent": "$http_user_agent",
        "http_x_forwarded_for": "$http_x_forwarded_for",
        "server_name": "$server_name"
    }';
    
    # Apply to access logs
    access_log /var/log/nginx/access.log json_analytics;
    error_log /var/log/nginx/error.log warn;
    
    # Your existing configuration
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # Performance settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Test and reload NGINX

Verify the configuration syntax and reload NGINX to apply the new logging format and status endpoint.

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx

Install NGINX Prometheus Exporter

Download and install the official NGINX Prometheus exporter that will scrape the status endpoint and convert metrics 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 systemd service

Create a systemd service file to manage the NGINX exporter as a proper system service with automatic startup and logging.

[Unit]
Description=NGINX Prometheus Exporter
After=network.target nginx.service
Requires=nginx.service

[Service]
Type=simple
User=nginx
Group=nginx
ExecStart=/usr/local/bin/nginx-prometheus-exporter -nginx.scrape-uri=http://127.0.0.1/nginx_status
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Install Prometheus log exporter

Install a log parsing exporter to convert NGINX JSON logs into Prometheus metrics for request rates, response times, and status codes.

cd /tmp
wget https://github.com/martin-helmich/prometheus-nginxlog-exporter/releases/download/v1.10.0/prometheus-nginxlog-exporter_1.10.0_linux_amd64.tar.gz
tar -xzf prometheus-nginxlog-exporter_1.10.0_linux_amd64.tar.gz
sudo mv prometheus-nginxlog-exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/prometheus-nginxlog-exporter

Configure log exporter

Create a configuration file that defines how to parse NGINX logs and what metrics to extract from them.

listen:
  port: 4040
  address: "0.0.0.0"

consul:
  enable: false

namespaces:
  - name: nginx
    format: "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" \"$http_x_forwarded_for\""
    source:
      files:
        - /var/log/nginx/access.log
    labels:
      service: "nginx"
      environment: "production"
    histogram_buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]
    print_log: false
    
    metrics:
      - name: response_time_seconds
        type: histogram
        help: "Response time in seconds"
        match: '$request_time'
        value: '$request_time'
        labels:
          method: '$request_method'
          status: '$status'
          
      - name: bytes_sent_total
        type: counter
        help: "Total bytes sent"
        match: '$body_bytes_sent'
        value: '$body_bytes_sent'
        labels:
          method: '$request_method'
          status: '$status'
          
      - name: requests_total
        type: counter
        help: "Total number of requests"
        match: '.*'
        labels:
          method: '$request_method'
          status: '$status'

Create log exporter systemd service

Set up the log exporter as a systemd service with proper permissions to read NGINX log files.

[Unit]
Description=Prometheus NGINX Log Exporter
After=network.target

[Service]
Type=simple
User=root
Group=adm
ExecStart=/usr/local/bin/prometheus-nginxlog-exporter -config-file /etc/prometheus-nginxlog-exporter.yml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Start the exporters

Enable and start both Prometheus exporters to begin collecting NGINX metrics.

sudo systemctl daemon-reload
sudo systemctl enable --now nginx-exporter
sudo systemctl enable --now prometheus-nginxlog-exporter
sudo systemctl status nginx-exporter
sudo systemctl status prometheus-nginxlog-exporter

Install Prometheus

Download and install Prometheus server to scrape metrics from the NGINX exporters and store time-series data.

cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.45.0/prometheus-2.45.0.linux-amd64.tar.gz
tar -xzf prometheus-2.45.0.linux-amd64.tar.gz
sudo mv prometheus-2.45.0.linux-amd64 /opt/prometheus
sudo useradd --no-create-home --shell /bin/false prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus
sudo chown -R prometheus:prometheus /opt/prometheus

Configure Prometheus

Create a Prometheus configuration file that defines scraping intervals and targets for the NGINX exporters.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "nginx_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'nginx-exporter'
    static_configs:
      - targets: ['localhost:9113']
    scrape_interval: 10s
    metrics_path: /metrics
    
  - job_name: 'nginx-log-exporter'
    static_configs:
      - targets: ['localhost:4040']
    scrape_interval: 10s
    metrics_path: /metrics

Create Prometheus alerting rules

Define alerting rules for common NGINX issues like high error rates, slow response times, and service availability.

groups:
  - name: nginx
    rules:
      - alert: NginxDown
        expr: up{job="nginx-exporter"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "NGINX is down"
          description: "NGINX has been down for more than 1 minute"

      - alert: NginxHighErrorRate
        expr: rate(nginx_requests_total{status=~"5.."}[5m]) / rate(nginx_requests_total[5m]) > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High NGINX 5xx error rate"
          description: "NGINX 5xx error rate is {{ $value | humanizePercentage }} for 5 minutes"

      - alert: NginxHighLatency
        expr: histogram_quantile(0.95, rate(nginx_response_time_seconds_bucket[5m])) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High NGINX response time"
          description: "95th percentile response time is {{ $value }}s for 5 minutes"

      - alert: NginxHighConnections
        expr: nginx_connections_active > 1000
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "High number of NGINX connections"
          description: "NGINX has {{ $value }} active connections"

Set Prometheus permissions

Ensure the prometheus user owns all configuration files and the data directory.

sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
sudo chown prometheus:prometheus /etc/prometheus/nginx_rules.yml

Create Prometheus systemd service

Set up Prometheus as a systemd service with appropriate resource limits and security settings.

[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/opt/prometheus/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
Restart=always

[Install]
WantedBy=multi-user.target

Install Grafana

Install Grafana to create visual dashboards and set up alerting based on the Prometheus metrics.

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
sudo tee /etc/yum.repos.d/grafana.repo<

Start all services

Enable and start Prometheus and Grafana to begin collecting and visualizing NGINX metrics.

sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl enable --now grafana-server
sudo systemctl status prometheus
sudo systemctl status grafana-server

Configure firewall access

Open ports for Prometheus (9090) and Grafana (3000) while keeping the exporters restricted to localhost.

sudo ufw allow 9090/tcp
sudo ufw allow 3000/tcp
sudo ufw reload
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload

Generate test traffic

Create some test requests to populate the monitoring dashboards with data.

for i in {1..50}; do
    curl -s http://localhost/ > /dev/null
    curl -s http://localhost/nonexistent > /dev/null
    sleep 0.1
done

Configure Grafana dashboards

Access Grafana web interface

Open your browser and navigate to Grafana using the server's IP address. The default credentials are admin/admin.

http://your-server-ip:3000

Change the default password when prompted for security.

Add Prometheus data source

In Grafana, go to Configuration > Data Sources > Add data source > Prometheus. Configure the connection to your local Prometheus instance.

URL: http://localhost:9090
Access: Server (default)
Scrape interval: 15s

Click "Save & Test" to verify the connection.

Import NGINX dashboard

Create a comprehensive NGINX monitoring dashboard or import a community dashboard. Go to Dashboards > Import and use dashboard ID 12708 for a pre-built NGINX overview.

Note: Dashboard 12708 provides request rates, response times, error rates, and top URLs. Modify the queries to match your metric names if needed.

Create custom panels

Add custom panels for specific NGINX metrics that matter to your application. Here are some useful queries:

# Request rate per second
rate(nginx_requests_total[5m])

95th percentile response time

histogram_quantile(0.95, rate(nginx_response_time_seconds_bucket[5m]))

Error rate percentage

rate(nginx_requests_total{status=~"5.."}[5m]) / rate(nginx_requests_total[5m]) * 100

Active connections

nginx_connections_active

Request size distribution

histogram_quantile(0.95, rate(nginx_bytes_sent_bucket[5m]))

Set up log aggregation with Loki

Install Grafana Loki

Download and install Loki for centralized log aggregation that integrates seamlessly with Grafana.

cd /tmp
wget https://github.com/grafana/loki/releases/download/v2.9.0/loki-linux-amd64.zip
unzip loki-linux-amd64.zip
sudo mv loki-linux-amd64 /usr/local/bin/loki
sudo chmod +x /usr/local/bin/loki

Configure Loki

Create a Loki configuration file optimized for NGINX log ingestion and retention.

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  path_prefix: /var/lib/loki
  storage:
    filesystem:
      chunks_directory: /var/lib/loki/chunks
      rules_directory: /var/lib/loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

query_range:
  results_cache:
    cache:
      embedded_cache:
        enabled: true
        max_size_mb: 100

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

limits_config:
  reject_old_samples: true
  reject_old_samples_max_age: 168h
  ingestion_rate_mb: 16
  ingestion_burst_size_mb: 32

Install Promtail

Install Promtail to ship NGINX logs to Loki with proper parsing and labeling.

cd /tmp
wget https://github.com/grafana/loki/releases/download/v2.9.0/promtail-linux-amd64.zip
unzip promtail-linux-amd64.zip
sudo mv promtail-linux-amd64 /usr/local/bin/promtail
sudo chmod +x /usr/local/bin/promtail

Configure Promtail

Set up Promtail to parse NGINX JSON logs and send them to Loki with appropriate labels.

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /var/lib/promtail/positions.yaml

clients:
  - url: http://localhost:3100/loki/api/v1/push

scrape_configs:
  - job_name: nginx-access
    static_configs:
      - targets:
          - localhost
        labels:
          job: nginx-access
          host: localhost
          __path__: /var/log/nginx/access.log
    pipeline_stages:
      - json:
          expressions:
            time: time
            remote_addr: remote_addr
            request: request
            status: status
            request_time: request_time
            body_bytes_sent: body_bytes_sent
            http_user_agent: http_user_agent
      - labels:
          status:
          remote_addr:
      - timestamp:
          source: time
          format: "2006-01-02T15:04:05-07:00"

  - job_name: nginx-error
    static_configs:
      - targets:
          - localhost
        labels:
          job: nginx-error
          host: localhost
          __path__: /var/log/nginx/error.log

Create directories and set permissions

Create necessary directories for Loki and Promtail with proper ownership.

sudo mkdir -p /var/lib/loki/chunks /var/lib/loki/rules /var/lib/promtail
sudo useradd --no-create-home --shell /bin/false loki
sudo useradd --no-create-home --shell /bin/false promtail
sudo chown -R loki:loki /var/lib/loki
sudo chown -R promtail:promtail /var/lib/promtail
sudo usermod -a -G adm promtail

Create systemd services for Loki and Promtail

Set up both services to run automatically with proper resource limits.

[Unit]
Description=Loki
After=network.target

[Service]
Type=simple
User=loki
Group=loki
ExecStart=/usr/local/bin/loki -config.file=/etc/loki-config.yml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
[Unit]
Description=Promtail
After=network.target

[Service]
Type=simple
User=promtail
Group=promtail
ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail-config.yml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Start Loki and Promtail

Enable and start the log aggregation stack to begin collecting NGINX logs.

sudo systemctl daemon-reload
sudo systemctl enable --now loki
sudo systemctl enable --now promtail
sudo systemctl status loki
sudo systemctl status promtail

Add Loki data source to Grafana

In Grafana, add Loki as a data source to enable log exploration alongside metrics. Go to Configuration > Data Sources > Add data source > Loki.

URL: http://localhost:3100
Access: Server (default)

Configure alerting

Set up comprehensive alerting for NGINX monitoring to get notified about issues before they impact users. You can configure email notifications through Prometheus Alertmanager or integrate with Slack and Microsoft Teams for team notifications.

Test alert rules

Verify that your alerting rules trigger correctly by generating test conditions.

# Test high error rate
for i in {1..20}; do curl -s http://localhost/nonexistent > /dev/null; done

Test high load

for i in {1..100}; do curl -s http://localhost/ > /dev/null & done

Verify your setup

Confirm that all components are running and collecting data properly.

# Check all services are running
sudo systemctl status nginx nginx-exporter prometheus-nginxlog-exporter prometheus grafana-server loki promtail

Verify metrics are being collected

curl http://localhost:9113/metrics | grep nginx_connections curl http://localhost:4040/metrics | grep nginx_requests_total

Check Prometheus targets

curl http://localhost:9090/api/v1/targets

Test log ingestion

curl http://localhost:3100/loki/api/v1/label

Generate test traffic and check logs

curl http://localhost/ tail -f /var/log/nginx/access.log

Common issues

SymptomCauseFix
Nginx-exporter fails to startNGINX status module not enabledAdd stub_status on; to NGINX config and reload
No metrics in PrometheusFirewall blocking scrape targetsCheck targets are accessible: curl localhost:9113/metrics
Log exporter shows permission deniedInsufficient permissions to read logsAdd user to adm group: sudo usermod -a -G adm prometheus
Grafana dashboards show no dataData source configuration incorrectVerify Prometheus URL and test connection in Grafana
Alerts not firingAlertmanager not configuredInstall and configure Alertmanager or use Grafana alerting
High memory usageToo much log retentionReduce retention periods in Loki and Prometheus configs
Loki ingestion errorsLog format doesn't match parserCheck Promtail pipeline stages match NGINX JSON format

Next steps

Running this in production?

Want this handled for you? Setting up monitoring once is straightforward. Keeping it patched, tuned, and responding to alerts across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.