From 81466aebacc5d43280332ff4996ef24d1f2fce63 Mon Sep 17 00:00:00 2001
From: Benji Shohet <97973081+benjisho@users.noreply.github.com>
Date: Wed, 16 Jul 2025 03:21:23 +0000
Subject: [PATCH 1/5] Add Docker Compose setup for NGINX Prometheus Exporter
- Add docker-compose.yml with nginx-exporter, nginx, app, prometheus, and grafana services
- Add docker-compose.test.yml for minimal testing setup
- Configure NGINX with stub_status endpoint on port 8081
- Add sample web application with custom HTML content
- Configure Prometheus to scrape nginx-exporter metrics
- Add Grafana with pre-configured datasources and dashboards
- Set up proper networking and service dependencies
- Use examples/docker-compose/ directory structure for all configuration files
---
docker-compose.test.yml | 50 ++
docker-compose.yml | 93 +++
examples/docker-compose/app/html/index.html | 190 ++++++
examples/docker-compose/app/nginx.conf | 37 ++
.../grafana/dashboards/nginx-dashboard.json | 567 ++++++++++++++++++
.../provisioning/dashboards/dashboards.yml | 12 +
.../provisioning/datasources/prometheus.yml | 9 +
examples/docker-compose/nginx/nginx.conf | 55 ++
.../docker-compose/prometheus/prometheus.yml | 28 +
9 files changed, 1041 insertions(+)
create mode 100644 docker-compose.test.yml
create mode 100644 docker-compose.yml
create mode 100644 examples/docker-compose/app/html/index.html
create mode 100644 examples/docker-compose/app/nginx.conf
create mode 100644 examples/docker-compose/grafana/dashboards/nginx-dashboard.json
create mode 100644 examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml
create mode 100644 examples/docker-compose/grafana/provisioning/datasources/prometheus.yml
create mode 100644 examples/docker-compose/nginx/nginx.conf
create mode 100644 examples/docker-compose/prometheus/prometheus.yml
diff --git a/docker-compose.test.yml b/docker-compose.test.yml
new file mode 100644
index 00000000..568ee59b
--- /dev/null
+++ b/docker-compose.test.yml
@@ -0,0 +1,50 @@
+version: '3.8'
+
+services:
+ # Sample web application
+ app:
+ image: nginx:alpine
+ container_name: sample-app
+ volumes:
+ - ./docker/app/nginx.conf:/etc/nginx/nginx.conf
+ - ./docker/app/html:/usr/share/nginx/html
+ ports:
+ - "8080:80"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+
+ # NGINX with stub_status enabled
+ nginx:
+ image: nginx:alpine
+ container_name: nginx-server
+ depends_on:
+ - app
+ volumes:
+ - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
+ - ./docker/nginx/conf.d:/etc/nginx/conf.d
+ ports:
+ - "80:80"
+ - "8081:8081" # stub_status port
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+
+ # NGINX Prometheus Exporter
+ nginx-exporter:
+ image: nginx/nginx-prometheus-exporter:latest
+ container_name: nginx-prometheus-exporter
+ depends_on:
+ - nginx
+ command:
+ - --nginx.scrape-uri=http://nginx:8081/stub_status
+ - --web.listen-address=0.0.0.0:9113
+ ports:
+ - "9113:9113"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+
+networks:
+ nginx-monitoring:
+ driver: bridge
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..6028b038
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,93 @@
+services:
+ # Sample web application
+ app:
+ image: nginx:alpine
+ container_name: sample-app
+ volumes:
+ - ./examples/docker-compose/app/nginx.conf:/etc/nginx/nginx.conf
+ - ./examples/docker-compose/app/html:/usr/share/nginx/html
+ ports:
+ - "8080:80"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+
+ # NGINX with stub_status enabled
+ nginx:
+ image: nginx:alpine
+ container_name: nginx-server
+ depends_on:
+ - app
+ volumes:
+ - ./examples/docker-compose/nginx/nginx.conf:/etc/nginx/nginx.conf
+ - ./examples/docker-compose/nginx/conf.d:/etc/nginx/conf.d
+ ports:
+ - "80:80"
+ - "8081:8081" # stub_status port
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+
+ # NGINX Prometheus Exporter
+ nginx-exporter:
+ image: nginx/nginx-prometheus-exporter:latest
+ container_name: nginx-prometheus-exporter
+ depends_on:
+ - nginx
+ command:
+ - --nginx.scrape-uri=http://nginx:8081/stub_status
+ - --web.listen-address=0.0.0.0:9113
+ ports:
+ - "9113:9113"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+
+ # Prometheus
+ prometheus:
+ image: prom/prometheus:latest
+ container_name: prometheus
+ depends_on:
+ - nginx-exporter
+ volumes:
+ - ./examples/docker-compose/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
+ - prometheus_data:/prometheus
+ ports:
+ - "9090:9090"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+ command:
+ - --config.file=/etc/prometheus/prometheus.yml
+ - --storage.tsdb.path=/prometheus
+ - --web.console.libraries=/etc/prometheus/console_libraries
+ - --web.console.templates=/etc/prometheus/consoles
+ - --storage.tsdb.retention.time=200h
+ - --web.enable-lifecycle
+
+ # Grafana
+ grafana:
+ image: grafana/grafana:latest
+ container_name: grafana
+ depends_on:
+ - prometheus
+ volumes:
+ - grafana_data:/var/lib/grafana
+ - ./examples/docker-compose/grafana/provisioning:/etc/grafana/provisioning
+ - ./examples/docker-compose/grafana/dashboards:/var/lib/grafana/dashboards
+ ports:
+ - "3000:3000"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+ environment:
+ - GF_SECURITY_ADMIN_PASSWORD=admin123
+ - GF_USERS_ALLOW_SIGN_UP=false
+
+volumes:
+ prometheus_data:
+ grafana_data:
+
+networks:
+ nginx-monitoring:
+ driver: bridge
diff --git a/examples/docker-compose/app/html/index.html b/examples/docker-compose/app/html/index.html
new file mode 100644
index 00000000..c0380fd7
--- /dev/null
+++ b/examples/docker-compose/app/html/index.html
@@ -0,0 +1,190 @@
+
+
+
+
+
+ NGINX Prometheus Exporter Demo
+
+
+
+
+
+
+
+
📊 Prometheus
+
Time-series database collecting metrics from NGINX
+
Open Prometheus →
+
+
+
+
📈 Grafana
+
Visualization dashboard for NGINX metrics
+
Login: admin / admin123
+
Open Grafana →
+
+
+
+
🔍 NGINX Exporter
+
Prometheus exporter for NGINX metrics
+
View Metrics →
+
+
+
+
⚙️ NGINX Status
+
Raw NGINX stub_status information
+
View Status →
+
+
+
+
+
🚀 Load Testing
+
Generate some traffic to see metrics in action:
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/docker-compose/app/nginx.conf b/examples/docker-compose/app/nginx.conf
new file mode 100644
index 00000000..3fa3e06d
--- /dev/null
+++ b/examples/docker-compose/app/nginx.conf
@@ -0,0 +1,37 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+ error_log /var/log/nginx/error.log;
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+
+ server {
+ listen 80;
+ server_name localhost;
+ root /usr/share/nginx/html;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+
+ location /api/ {
+ return 200 '{"status": "ok", "message": "API endpoint"}';
+ add_header Content-Type application/json;
+ }
+ }
+}
diff --git a/examples/docker-compose/grafana/dashboards/nginx-dashboard.json b/examples/docker-compose/grafana/dashboards/nginx-dashboard.json
new file mode 100644
index 00000000..4adfb200
--- /dev/null
+++ b/examples/docker-compose/grafana/dashboards/nginx-dashboard.json
@@ -0,0 +1,567 @@
+{
+ "__inputs": [
+ {
+ "description": "",
+ "label": "Prometheus",
+ "name": "DS_PROMETHEUS",
+ "pluginId": "prometheus",
+ "pluginName": "Prometheus",
+ "type": "datasource"
+ }
+ ],
+ "__requires": [
+ {
+ "id": "grafana",
+ "name": "Grafana",
+ "type": "grafana",
+ "version": "5.0.0"
+ },
+ {
+ "id": "graph",
+ "name": "Graph",
+ "type": "panel",
+ "version": ""
+ },
+ {
+ "id": "prometheus",
+ "name": "Prometheus",
+ "type": "datasource",
+ "version": "1.0.0"
+ },
+ {
+ "id": "singlestat",
+ "name": "Singlestat",
+ "type": "panel",
+ "version": ""
+ }
+ ],
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "description": "Official dashboard for NGINX Prometheus exporter",
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "iteration": 1562682051068,
+ "links": [],
+ "panels": [
+ {
+ "collapsed": false,
+ "datasource": "${DS_PROMETHEUS}",
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 4,
+ "panels": [],
+ "title": "Status",
+ "type": "row"
+ },
+ {
+ "cacheTimeout": null,
+ "colorBackground": true,
+ "colorPostfix": false,
+ "colorPrefix": false,
+ "colorValue": false,
+ "colors": [
+ "#E02F44",
+ "#FF9830",
+ "#299c46"
+ ],
+ "datasource": "${DS_PROMETHEUS}",
+ "decimals": null,
+ "description": "",
+ "format": "none",
+ "gauge": {
+ "maxValue": 100,
+ "minValue": 0,
+ "show": false,
+ "thresholdLabels": false,
+ "thresholdMarkers": true
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 12,
+ "x": 0,
+ "y": 1
+ },
+ "id": 8,
+ "interval": null,
+ "links": [],
+ "mappingType": 1,
+ "mappingTypes": [
+ {
+ "name": "value to text",
+ "value": 1
+ },
+ {
+ "name": "range to text",
+ "value": 2
+ }
+ ],
+ "maxDataPoints": 100,
+ "nullPointMode": "connected",
+ "nullText": null,
+ "options": {},
+ "postfix": "",
+ "postfixFontSize": "50%",
+ "prefix": "",
+ "prefixFontSize": "50%",
+ "rangeMaps": [
+ {
+ "from": "null",
+ "text": "N/A",
+ "to": "null"
+ }
+ ],
+ "repeat": "instance",
+ "repeatDirection": "h",
+ "sparkline": {
+ "fillColor": "rgba(31, 118, 189, 0.18)",
+ "full": false,
+ "lineColor": "rgb(31, 120, 193)",
+ "show": false
+ },
+ "tableColumn": "",
+ "targets": [
+ {
+ "expr": "nginx_up{instance=~\"$instance\"}",
+ "format": "time_series",
+ "instant": false,
+ "intervalFactor": 1,
+ "refId": "A"
+ }
+ ],
+ "thresholds": "1,1",
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "NGINX Status for $instance",
+ "type": "singlestat",
+ "valueFontSize": "100%",
+ "valueMaps": [
+ {
+ "op": "=",
+ "text": "Down",
+ "value": "0"
+ },
+ {
+ "op": "=",
+ "text": "Up",
+ "value": "1"
+ }
+ ],
+ "valueName": "current"
+ },
+ {
+ "collapsed": false,
+ "datasource": "${DS_PROMETHEUS}",
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 4
+ },
+ "id": 6,
+ "panels": [],
+ "title": "Metrics",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "${DS_PROMETHEUS}",
+ "decimals": null,
+ "description": "",
+ "fill": 1,
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 0,
+ "y": 5
+ },
+ "id": 10,
+ "legend": {
+ "alignAsTable": false,
+ "avg": false,
+ "current": false,
+ "hideEmpty": false,
+ "max": false,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {},
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "irate(nginx_connections_accepted{instance=~\"$instance\"}[5m])",
+ "format": "time_series",
+ "instant": false,
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} accepted",
+ "refId": "A"
+ },
+ {
+ "expr": "irate(nginx_connections_handled{instance=~\"$instance\"}[5m])",
+ "format": "time_series",
+ "instant": false,
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} handled",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Processed connections",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 1,
+ "format": "short",
+ "label": "Connections (rate)",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": "",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "${DS_PROMETHEUS}",
+ "decimals": 0,
+ "fill": 1,
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 12,
+ "y": 5
+ },
+ "id": 12,
+ "legend": {
+ "alignAsTable": false,
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "rightSide": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {},
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "nginx_connections_active{instance=~\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} active",
+ "refId": "A"
+ },
+ {
+ "expr": "nginx_connections_reading{instance=~\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} reading",
+ "refId": "B"
+ },
+ {
+ "expr": "nginx_connections_waiting{instance=~\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} waiting",
+ "refId": "C"
+ },
+ {
+ "expr": "nginx_connections_writing{instance=~\"$instance\"}",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} writing",
+ "refId": "D"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Active Connections",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "decimals": 0,
+ "format": "short",
+ "label": "Connections",
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "${DS_PROMETHEUS}",
+ "fill": 1,
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 15
+ },
+ "id": 15,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "links": [],
+ "nullPointMode": "null",
+ "options": {},
+ "percentage": false,
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "irate(nginx_http_requests_total{instance=~\"$instance\"}[5m])",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "{{instance}} total requests",
+ "refId": "A"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeRegions": [],
+ "timeShift": null,
+ "title": "Total requests",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 18,
+ "style": "dark",
+ "tags": [
+ "nginx",
+ "prometheus",
+ "nginx prometheus exporter"
+ ],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": false,
+ "tags": [],
+ "text": "default",
+ "value": "default"
+ },
+ "hide": 0,
+ "includeAll": false,
+ "label": "datasource",
+ "multi": false,
+ "name": "DS_PROMETHEUS",
+ "options": [],
+ "query": "prometheus",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "type": "datasource"
+ },
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_PROMETHEUS}",
+ "definition": "label_values(nginx_up, instance)",
+ "hide": 0,
+ "includeAll": true,
+ "label": "",
+ "multi": true,
+ "name": "instance",
+ "options": [],
+ "query": "label_values(nginx_up, instance)",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tags": [],
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ }
+ ]
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "NGINX",
+ "uid": "MsjffzSZz",
+ "version": 1
+}
diff --git a/examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml b/examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml
new file mode 100644
index 00000000..7435f09d
--- /dev/null
+++ b/examples/docker-compose/grafana/provisioning/dashboards/dashboards.yml
@@ -0,0 +1,12 @@
+apiVersion: 1
+
+providers:
+ - name: 'default'
+ orgId: 1
+ folder: ''
+ type: file
+ disableDeletion: false
+ updateIntervalSeconds: 10
+ allowUiUpdates: true
+ options:
+ path: /var/lib/grafana/dashboards
diff --git a/examples/docker-compose/grafana/provisioning/datasources/prometheus.yml b/examples/docker-compose/grafana/provisioning/datasources/prometheus.yml
new file mode 100644
index 00000000..1a57b69c
--- /dev/null
+++ b/examples/docker-compose/grafana/provisioning/datasources/prometheus.yml
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ url: http://prometheus:9090
+ isDefault: true
+ editable: true
diff --git a/examples/docker-compose/nginx/nginx.conf b/examples/docker-compose/nginx/nginx.conf
new file mode 100644
index 00000000..78791539
--- /dev/null
+++ b/examples/docker-compose/nginx/nginx.conf
@@ -0,0 +1,55 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+ error_log /var/log/nginx/error.log;
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+
+ # Include additional configuration files
+ include /etc/nginx/conf.d/*.conf;
+
+ # Main server
+ server {
+ listen 80;
+ server_name localhost;
+
+ # Proxy to sample app
+ location / {
+ proxy_pass http://app:80;
+ 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;
+ }
+ }
+
+ # Status server for metrics
+ server {
+ listen 8081;
+ server_name localhost;
+
+ location /stub_status {
+ stub_status;
+ access_log off;
+ allow all;
+ }
+
+ location / {
+ return 404;
+ }
+ }
+}
diff --git a/examples/docker-compose/prometheus/prometheus.yml b/examples/docker-compose/prometheus/prometheus.yml
new file mode 100644
index 00000000..e9b2aca2
--- /dev/null
+++ b/examples/docker-compose/prometheus/prometheus.yml
@@ -0,0 +1,28 @@
+global:
+ scrape_interval: 15s
+ evaluation_interval: 15s
+
+rule_files:
+ # - "first_rules.yml"
+ # - "second_rules.yml"
+
+scrape_configs:
+ # The job name is added as a label `job=` to any timeseries scraped from this config.
+ - job_name: "prometheus"
+ static_configs:
+ - targets: ["localhost:9090"]
+
+ # NGINX Prometheus Exporter
+ - job_name: "nginx-exporter"
+ static_configs:
+ - targets: ["nginx-exporter:9113"]
+ scrape_interval: 5s
+ metrics_path: /metrics
+ scrape_timeout: 5s
+
+ # Optional: Direct monitoring of services
+ - job_name: "nginx-status"
+ static_configs:
+ - targets: ["nginx:8081"]
+ metrics_path: /stub_status
+ scrape_interval: 10s
From 3d7ff39972ef2d220b58333deca7eb5a9f8e3331 Mon Sep 17 00:00:00 2001
From: Benji Shohet <97973081+benjisho@users.noreply.github.com>
Date: Wed, 16 Jul 2025 03:21:38 +0000
Subject: [PATCH 2/5] Add Docker Compose documentation
- Add comprehensive Docker Compose documentation in examples/docker-compose/README.md
- Include architecture overview, quick start guide, and service descriptions
- Add testing instructions and troubleshooting section
- Update main README.md to reference Docker Compose example
- Focus quick start on nginx-exporter only for existing NGINX setups
- Provide separate instructions for complete monitoring stack
- Add configuration file references and usage examples
---
README.md | 21 +++++
examples/docker-compose/README.md | 148 ++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 examples/docker-compose/README.md
diff --git a/README.md b/README.md
index 2d868186..422ab37f 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,27 @@ To start the exporter we use the [docker run](https://docs.docker.com/engine/ref
follow the example in [examples/systemd](./examples/systemd/README.md). Alternatively, you can run the exporter
in a Docker container.
+### Docker Compose Setup
+
+For a complete monitoring stack including NGINX, NGINX Prometheus Exporter, Prometheus, and Grafana, see the [Docker Compose example](./examples/docker-compose/README.md).
+
+The Docker Compose setup provides:
+- NGINX server with stub_status enabled
+- NGINX Prometheus Exporter for metrics collection
+- Prometheus for time-series storage
+- Grafana for visualization with pre-configured dashboards
+- Sample web application for testing
+
+Quick start (nginx-exporter only):
+```console
+docker-compose up -d nginx-exporter
+```
+
+For a complete monitoring stack with all services:
+```console
+docker-compose up -d
+```
+
## Usage
### Command-line Arguments
diff --git a/examples/docker-compose/README.md b/examples/docker-compose/README.md
new file mode 100644
index 00000000..17206d09
--- /dev/null
+++ b/examples/docker-compose/README.md
@@ -0,0 +1,148 @@
+# Docker Compose Setup for NGINX Prometheus Exporter
+
+This Docker Compose configuration provides a complete monitoring stack for NGINX using Prometheus and Grafana.
+
+## Table of Contents
+
+- [Architecture](#architecture)
+- [Quick Start](#quick-start)
+- [Services](#services)
+ - [nginx-exporter](#nginx-exporter)
+ - [nginx (Main Server)](#nginx-main-server)
+ - [app (Sample Application)](#app-sample-application)
+ - [prometheus](#prometheus)
+ - [grafana](#grafana)
+- [Configuration Files](#configuration-files)
+- [Testing](#testing)
+- [Stopping](#stopping)
+- [Troubleshooting](#troubleshooting)
+- [Network](#network)
+- [Volumes](#volumes)
+
+## Architecture
+
+The setup includes:
+- **nginx**: Main NGINX server with stub_status enabled (for demo purposes)
+- **app**: Sample web application served by NGINX (for demo purposes)
+- **nginx-exporter**: NGINX Prometheus Exporter that scrapes metrics from NGINX
+- **prometheus**: Prometheus time-series database for metrics collection
+- **grafana**: Grafana dashboard for visualization
+
+**Note**: If you have an existing NGINX setup, you only need to run the `nginx-exporter` service. Make sure your NGINX has stub_status enabled and is accessible at the configured scrape URI.
+
+## Quick Start
+
+1. **Navigate to the project root directory**:
+ ```bash
+ cd /path/to/nginx-prometheus-exporter
+ ```
+
+2. **For existing NGINX setup - Start only the nginx-exporter**:
+ ```bash
+ docker-compose up -d nginx-exporter
+ ```
+ This assumes you already have NGINX running with stub_status enabled at `http://nginx:8081/stub_status`.
+
+3. **For complete demo setup - Start the full monitoring stack**:
+ ```bash
+ docker-compose up -d
+ ```
+ This starts all services including a sample NGINX server, Prometheus, and Grafana.
+
+4. **Access the services**:
+ - **Sample App**: http://localhost:8080
+ - **NGINX Server**: http://localhost:80
+ - **NGINX Metrics**: http://localhost:9113/metrics
+ - **NGINX Stub Status**: http://localhost:8081/stub_status
+ - **Prometheus**: http://localhost:9090
+ - **Grafana**: http://localhost:3000 (admin/admin123)
+
+## Services
+
+### nginx-exporter
+- **Image**: `nginx/nginx-prometheus-exporter:latest`
+- **Port**: 9113
+- **Function**: Scrapes NGINX stub_status and converts to Prometheus metrics
+
+### nginx (Main Server)
+- **Image**: `nginx:alpine`
+- **Ports**: 80, 8081 (stub_status)
+- **Function**: Proxies to sample app and provides metrics endpoint
+
+### app (Sample Application)
+- **Image**: `nginx:alpine`
+- **Port**: 8080
+- **Function**: Serves sample web content
+
+### prometheus
+- **Image**: `prom/prometheus:latest`
+- **Port**: 9090
+- **Function**: Collects and stores metrics from nginx-exporter
+
+### grafana
+- **Image**: `grafana/grafana:latest`
+- **Port**: 3000
+- **Function**: Provides visualization dashboards
+
+## Configuration Files
+
+- `nginx/nginx.conf`: Main NGINX configuration with stub_status
+- `app/nginx.conf`: Sample application NGINX configuration
+- `prometheus/prometheus.yml`: Prometheus scraping configuration
+- `grafana/provisioning/`: Grafana datasource and dashboard configuration
+
+## Testing
+
+1. **Check nginx-exporter metrics**:
+ ```bash
+ curl http://localhost:9113/metrics | grep nginx
+ ```
+
+2. **Check stub_status directly**:
+ ```bash
+ curl http://localhost:8081/stub_status
+ ```
+
+3. **Generate traffic**:
+ ```bash
+ for i in {1..10}; do curl -s http://localhost:80 > /dev/null; done
+ ```
+
+## Stopping
+
+Stop all services:
+```bash
+docker-compose down
+```
+
+Stop and remove volumes:
+```bash
+docker-compose down -v
+```
+
+## Troubleshooting
+
+1. **Check container logs**:
+ ```bash
+ docker-compose logs nginx-exporter
+ docker-compose logs nginx
+ ```
+
+2. **Verify container status**:
+ ```bash
+ docker-compose ps
+ ```
+
+3. **Test internal connectivity**:
+ ```bash
+ docker exec nginx-server curl -s http://localhost:8081/stub_status
+ ```
+
+## Network
+
+All services run on the `nginx-monitoring` bridge network for internal communication.
+
+## Volumes
+
+- `prometheus_data`: Persistent storage for Prometheus metrics
+- `grafana_data`: Persistent storage for Grafana configurations and dashboards
From 54ebea930532cc858aff80ddf4d25e2d9f3b315e Mon Sep 17 00:00:00 2001
From: Benji Shohet <97973081+benjisho@users.noreply.github.com>
Date: Wed, 16 Jul 2025 03:21:53 +0000
Subject: [PATCH 3/5] Add CI workflow for Docker Compose testing
- Add test-docker-compose-ci.yml workflow to test Docker Compose setup
- Test 'docker-compose up -d nginx-exporter' command functionality
- Verify nginx-exporter container starts correctly and serves metrics
- Test full monitoring stack integration (nginx, prometheus, grafana)
- Validate service connectivity and metrics collection
- Include proper error handling and cleanup procedures
- Add detailed logging for troubleshooting failures
- Follow project CI conventions and use consistent action versions
---
.github/workflows/test-docker-compose-ci.yml | 286 +++++++++++++++++++
1 file changed, 286 insertions(+)
create mode 100644 .github/workflows/test-docker-compose-ci.yml
diff --git a/.github/workflows/test-docker-compose-ci.yml b/.github/workflows/test-docker-compose-ci.yml
new file mode 100644
index 00000000..33b1f689
--- /dev/null
+++ b/.github/workflows/test-docker-compose-ci.yml
@@ -0,0 +1,286 @@
+name: Docker Compose Test
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'docker-compose.yml'
+ - 'examples/docker-compose/**'
+ - '.github/workflows/test-docker-compose-ci.yml'
+ pull_request:
+ branches:
+ - main
+ paths:
+ - 'docker-compose.yml'
+ - 'examples/docker-compose/**'
+ - '.github/workflows/test-docker-compose-ci.yml'
+
+defaults:
+ run:
+ shell: bash
+
+concurrency:
+ group: ${{ github.ref_name }}-test-docker-compose-ci
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ docker-compose-nginx-exporter:
+ name: Docker Compose - NGINX Exporter
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
+
+ - name: Validate Docker Compose configuration
+ run: |
+ echo "Validating Docker Compose configuration..."
+ docker-compose config --quiet
+ echo "✓ Docker Compose configuration is valid"
+
+ - name: Start NGINX Exporter with dependencies
+ run: |
+ echo "Starting NGINX Exporter and its dependencies..."
+ docker-compose up -d nginx-exporter
+ echo "✓ NGINX Exporter started successfully"
+
+ - name: Wait for services to be ready
+ run: |
+ echo "Waiting for services to be ready..."
+
+ # Wait for NGINX to be ready
+ for i in {1..30}; do
+ if curl -s http://localhost:8081/stub_status > /dev/null 2>&1; then
+ echo "✓ NGINX stub_status is ready"
+ break
+ fi
+ echo "Waiting for NGINX stub_status... (attempt $i/30)"
+ sleep 2
+ done
+
+ # Wait for NGINX Exporter to be ready
+ for i in {1..30}; do
+ if curl -s http://localhost:9113/metrics > /dev/null 2>&1; then
+ echo "✓ NGINX Exporter metrics endpoint is ready"
+ break
+ fi
+ echo "Waiting for NGINX Exporter metrics... (attempt $i/30)"
+ sleep 2
+ done
+
+ - name: Verify container status
+ run: |
+ echo "Verifying container status..."
+ docker-compose ps
+
+ # Check if nginx-exporter container is running
+ if ! docker-compose ps nginx-exporter | grep -q "Up"; then
+ echo "❌ NGINX Exporter container is not running"
+ docker-compose logs nginx-exporter
+ exit 1
+ fi
+ echo "✓ NGINX Exporter container is running"
+
+ # Check if nginx container is running
+ if ! docker-compose ps nginx | grep -q "Up"; then
+ echo "❌ NGINX container is not running"
+ docker-compose logs nginx
+ exit 1
+ fi
+ echo "✓ NGINX container is running"
+
+ - name: Test NGINX stub_status endpoint
+ run: |
+ echo "Testing NGINX stub_status endpoint..."
+ response=$(curl -s http://localhost:8081/stub_status)
+
+ if [[ -z "$response" ]]; then
+ echo "❌ NGINX stub_status endpoint returned empty response"
+ exit 1
+ fi
+
+ # Check if response contains expected metrics
+ if echo "$response" | grep -q "Active connections:"; then
+ echo "✓ NGINX stub_status endpoint is working correctly"
+ else
+ echo "❌ NGINX stub_status endpoint response is invalid"
+ echo "Response: $response"
+ exit 1
+ fi
+
+ - name: Test NGINX Exporter metrics endpoint
+ run: |
+ echo "Testing NGINX Exporter metrics endpoint..."
+ response=$(curl -s http://localhost:9113/metrics)
+
+ if [[ -z "$response" ]]; then
+ echo "❌ NGINX Exporter metrics endpoint returned empty response"
+ exit 1
+ fi
+
+ # Check if response contains expected NGINX metrics
+ if echo "$response" | grep -q "nginx_connections_accepted"; then
+ echo "✓ NGINX Exporter metrics endpoint is working correctly"
+ else
+ echo "❌ NGINX Exporter metrics endpoint response is invalid"
+ echo "Response: $response"
+ exit 1
+ fi
+
+ - name: Verify metrics collection
+ run: |
+ echo "Verifying metrics collection..."
+
+ # Generate some traffic to NGINX
+ echo "Generating traffic to NGINX..."
+ for i in {1..5}; do
+ curl -s http://localhost:80 > /dev/null || true
+ sleep 1
+ done
+
+ # Wait a moment for metrics to be updated
+ sleep 3
+
+ # Check if metrics are being collected
+ metrics=$(curl -s http://localhost:9113/metrics)
+
+ # Verify nginx_up metric exists and is 1
+ if echo "$metrics" | grep -q "nginx_up 1"; then
+ echo "✓ NGINX Exporter is successfully collecting metrics"
+ else
+ echo "❌ NGINX Exporter is not collecting metrics correctly"
+ echo "nginx_up metric:"
+ echo "$metrics" | grep "nginx_up" || echo "nginx_up metric not found"
+ exit 1
+ fi
+
+ # Verify connection metrics exist
+ if echo "$metrics" | grep -q "nginx_connections_accepted"; then
+ echo "✓ Connection metrics are being collected"
+ else
+ echo "❌ Connection metrics are not being collected"
+ exit 1
+ fi
+
+ - name: Test service connectivity
+ run: |
+ echo "Testing service connectivity..."
+
+ # Test that services can communicate internally
+ docker exec nginx-prometheus-exporter curl -f http://nginx:8081/stub_status > /dev/null
+ echo "✓ Services can communicate internally"
+
+ - name: Show container logs on failure
+ if: failure()
+ run: |
+ echo "=== NGINX Exporter Logs ==="
+ docker-compose logs nginx-exporter
+ echo "=== NGINX Logs ==="
+ docker-compose logs nginx
+ echo "=== App Logs ==="
+ docker-compose logs app || true
+ echo "=== Container Status ==="
+ docker-compose ps
+
+ - name: Cleanup
+ if: always()
+ run: |
+ echo "Cleaning up..."
+ docker-compose down -v
+ docker system prune -f
+
+ docker-compose-full-stack:
+ name: Docker Compose - Full Stack
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
+
+ - name: Start full monitoring stack
+ run: |
+ echo "Starting full monitoring stack..."
+ docker-compose up -d
+ echo "✓ Full monitoring stack started successfully"
+
+ - name: Wait for all services to be ready
+ run: |
+ echo "Waiting for all services to be ready..."
+
+ # Wait for all services to be healthy
+ services=("nginx:8081/stub_status" "nginx-exporter:9113/metrics" "prometheus:9090/-/ready" "grafana:3000/api/health")
+
+ for service in "${services[@]}"; do
+ IFS=':' read -r host_port path <<< "$service"
+
+ for i in {1..60}; do
+ if curl -s "http://localhost:${host_port}${path}" > /dev/null 2>&1; then
+ echo "✓ Service ${host_port} is ready"
+ break
+ fi
+ echo "Waiting for service ${host_port}... (attempt $i/60)"
+ sleep 2
+ done
+ done
+
+ - name: Verify all containers are running
+ run: |
+ echo "Verifying all containers are running..."
+ docker-compose ps
+
+ # Check that all expected services are up
+ services=("nginx" "nginx-exporter" "prometheus" "grafana" "app")
+
+ for service in "${services[@]}"; do
+ if ! docker-compose ps "$service" | grep -q "Up"; then
+ echo "❌ Service $service is not running"
+ docker-compose logs "$service"
+ exit 1
+ fi
+ echo "✓ Service $service is running"
+ done
+
+ - name: Test full stack integration
+ run: |
+ echo "Testing full stack integration..."
+
+ # Test Prometheus can scrape NGINX Exporter
+ prometheus_targets=$(curl -s http://localhost:9090/api/v1/targets)
+ if echo "$prometheus_targets" | grep -q "nginx-exporter:9113"; then
+ echo "✓ Prometheus is configured to scrape NGINX Exporter"
+ else
+ echo "❌ Prometheus is not configured to scrape NGINX Exporter"
+ exit 1
+ fi
+
+ # Test Grafana is accessible
+ grafana_health=$(curl -s http://localhost:3000/api/health)
+ if echo "$grafana_health" | grep -q "ok"; then
+ echo "✓ Grafana is healthy"
+ else
+ echo "❌ Grafana is not healthy"
+ exit 1
+ fi
+
+ - name: Show container logs on failure
+ if: failure()
+ run: |
+ echo "=== All Container Logs ==="
+ docker-compose logs
+ echo "=== Container Status ==="
+ docker-compose ps
+
+ - name: Cleanup
+ if: always()
+ run: |
+ echo "Cleaning up..."
+ docker-compose down -v
+ docker system prune -f
From e7452697a3566118c8c92c68ed9d4281e975c7a9 Mon Sep 17 00:00:00 2001
From: Benji Shohet <97973081+benjisho@users.noreply.github.com>
Date: Thu, 17 Jul 2025 12:26:39 +0000
Subject: [PATCH 4/5] Fix pre-commit issues: trailing whitespace, line length,
YAML, and TOC updates
---
.github/workflows/test-docker-compose-ci.yml | 42 +++++++-------
README.md | 7 ++-
docker-compose.test.yml | 55 +++++++++++++++++--
examples/docker-compose/README.md | 34 +++++++++---
examples/docker-compose/app/html/index.html | 6 +-
examples/docker-compose/nginx/nginx.conf | 6 +-
.../docker-compose/prometheus/prometheus.yml | 2 +-
7 files changed, 110 insertions(+), 42 deletions(-)
diff --git a/.github/workflows/test-docker-compose-ci.yml b/.github/workflows/test-docker-compose-ci.yml
index 33b1f689..b94a4b57 100644
--- a/.github/workflows/test-docker-compose-ci.yml
+++ b/.github/workflows/test-docker-compose-ci.yml
@@ -53,7 +53,7 @@ jobs:
- name: Wait for services to be ready
run: |
echo "Waiting for services to be ready..."
-
+
# Wait for NGINX to be ready
for i in {1..30}; do
if curl -s http://localhost:8081/stub_status > /dev/null 2>&1; then
@@ -63,7 +63,7 @@ jobs:
echo "Waiting for NGINX stub_status... (attempt $i/30)"
sleep 2
done
-
+
# Wait for NGINX Exporter to be ready
for i in {1..30}; do
if curl -s http://localhost:9113/metrics > /dev/null 2>&1; then
@@ -78,7 +78,7 @@ jobs:
run: |
echo "Verifying container status..."
docker-compose ps
-
+
# Check if nginx-exporter container is running
if ! docker-compose ps nginx-exporter | grep -q "Up"; then
echo "❌ NGINX Exporter container is not running"
@@ -86,7 +86,7 @@ jobs:
exit 1
fi
echo "✓ NGINX Exporter container is running"
-
+
# Check if nginx container is running
if ! docker-compose ps nginx | grep -q "Up"; then
echo "❌ NGINX container is not running"
@@ -99,12 +99,12 @@ jobs:
run: |
echo "Testing NGINX stub_status endpoint..."
response=$(curl -s http://localhost:8081/stub_status)
-
+
if [[ -z "$response" ]]; then
echo "❌ NGINX stub_status endpoint returned empty response"
exit 1
fi
-
+
# Check if response contains expected metrics
if echo "$response" | grep -q "Active connections:"; then
echo "✓ NGINX stub_status endpoint is working correctly"
@@ -118,12 +118,12 @@ jobs:
run: |
echo "Testing NGINX Exporter metrics endpoint..."
response=$(curl -s http://localhost:9113/metrics)
-
+
if [[ -z "$response" ]]; then
echo "❌ NGINX Exporter metrics endpoint returned empty response"
exit 1
fi
-
+
# Check if response contains expected NGINX metrics
if echo "$response" | grep -q "nginx_connections_accepted"; then
echo "✓ NGINX Exporter metrics endpoint is working correctly"
@@ -136,20 +136,20 @@ jobs:
- name: Verify metrics collection
run: |
echo "Verifying metrics collection..."
-
+
# Generate some traffic to NGINX
echo "Generating traffic to NGINX..."
for i in {1..5}; do
curl -s http://localhost:80 > /dev/null || true
sleep 1
done
-
+
# Wait a moment for metrics to be updated
sleep 3
-
+
# Check if metrics are being collected
metrics=$(curl -s http://localhost:9113/metrics)
-
+
# Verify nginx_up metric exists and is 1
if echo "$metrics" | grep -q "nginx_up 1"; then
echo "✓ NGINX Exporter is successfully collecting metrics"
@@ -159,7 +159,7 @@ jobs:
echo "$metrics" | grep "nginx_up" || echo "nginx_up metric not found"
exit 1
fi
-
+
# Verify connection metrics exist
if echo "$metrics" | grep -q "nginx_connections_accepted"; then
echo "✓ Connection metrics are being collected"
@@ -171,7 +171,7 @@ jobs:
- name: Test service connectivity
run: |
echo "Testing service connectivity..."
-
+
# Test that services can communicate internally
docker exec nginx-prometheus-exporter curl -f http://nginx:8081/stub_status > /dev/null
echo "✓ Services can communicate internally"
@@ -214,13 +214,13 @@ jobs:
- name: Wait for all services to be ready
run: |
echo "Waiting for all services to be ready..."
-
+
# Wait for all services to be healthy
services=("nginx:8081/stub_status" "nginx-exporter:9113/metrics" "prometheus:9090/-/ready" "grafana:3000/api/health")
-
+
for service in "${services[@]}"; do
IFS=':' read -r host_port path <<< "$service"
-
+
for i in {1..60}; do
if curl -s "http://localhost:${host_port}${path}" > /dev/null 2>&1; then
echo "✓ Service ${host_port} is ready"
@@ -235,10 +235,10 @@ jobs:
run: |
echo "Verifying all containers are running..."
docker-compose ps
-
+
# Check that all expected services are up
services=("nginx" "nginx-exporter" "prometheus" "grafana" "app")
-
+
for service in "${services[@]}"; do
if ! docker-compose ps "$service" | grep -q "Up"; then
echo "❌ Service $service is not running"
@@ -251,7 +251,7 @@ jobs:
- name: Test full stack integration
run: |
echo "Testing full stack integration..."
-
+
# Test Prometheus can scrape NGINX Exporter
prometheus_targets=$(curl -s http://localhost:9090/api/v1/targets)
if echo "$prometheus_targets" | grep -q "nginx-exporter:9113"; then
@@ -260,7 +260,7 @@ jobs:
echo "❌ Prometheus is not configured to scrape NGINX Exporter"
exit 1
fi
-
+
# Test Grafana is accessible
grafana_health=$(curl -s http://localhost:3000/api/health)
if echo "$grafana_health" | grep -q "ok"; then
diff --git a/README.md b/README.md
index 422ab37f..f4eb9704 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@ NGINX Prometheus exporter makes it possible to monitor NGINX or NGINX Plus using
- [Prerequisites](#prerequisites)
- [Running the Exporter in a Docker Container](#running-the-exporter-in-a-docker-container)
- [Running the Exporter Binary](#running-the-exporter-binary)
+ - [Docker Compose Setup](#docker-compose-setup)
- [Usage](#usage)
- [Command-line Arguments](#command-line-arguments)
- [Exported Metrics](#exported-metrics)
@@ -157,9 +158,11 @@ in a Docker container.
### Docker Compose Setup
-For a complete monitoring stack including NGINX, NGINX Prometheus Exporter, Prometheus, and Grafana, see the [Docker Compose example](./examples/docker-compose/README.md).
+For a complete monitoring stack including NGINX, NGINX Prometheus Exporter, Prometheus, and Grafana,
+see the [Docker Compose example](./examples/docker-compose/README.md).
The Docker Compose setup provides:
+
- NGINX server with stub_status enabled
- NGINX Prometheus Exporter for metrics collection
- Prometheus for time-series storage
@@ -167,11 +170,13 @@ The Docker Compose setup provides:
- Sample web application for testing
Quick start (nginx-exporter only):
+
```console
docker-compose up -d nginx-exporter
```
For a complete monitoring stack with all services:
+
```console
docker-compose up -d
```
diff --git a/docker-compose.test.yml b/docker-compose.test.yml
index 568ee59b..6028b038 100644
--- a/docker-compose.test.yml
+++ b/docker-compose.test.yml
@@ -1,13 +1,11 @@
-version: '3.8'
-
services:
# Sample web application
app:
image: nginx:alpine
container_name: sample-app
volumes:
- - ./docker/app/nginx.conf:/etc/nginx/nginx.conf
- - ./docker/app/html:/usr/share/nginx/html
+ - ./examples/docker-compose/app/nginx.conf:/etc/nginx/nginx.conf
+ - ./examples/docker-compose/app/html:/usr/share/nginx/html
ports:
- "8080:80"
networks:
@@ -21,8 +19,8 @@ services:
depends_on:
- app
volumes:
- - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
- - ./docker/nginx/conf.d:/etc/nginx/conf.d
+ - ./examples/docker-compose/nginx/nginx.conf:/etc/nginx/nginx.conf
+ - ./examples/docker-compose/nginx/conf.d:/etc/nginx/conf.d
ports:
- "80:80"
- "8081:8081" # stub_status port
@@ -45,6 +43,51 @@ services:
- nginx-monitoring
restart: unless-stopped
+ # Prometheus
+ prometheus:
+ image: prom/prometheus:latest
+ container_name: prometheus
+ depends_on:
+ - nginx-exporter
+ volumes:
+ - ./examples/docker-compose/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
+ - prometheus_data:/prometheus
+ ports:
+ - "9090:9090"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+ command:
+ - --config.file=/etc/prometheus/prometheus.yml
+ - --storage.tsdb.path=/prometheus
+ - --web.console.libraries=/etc/prometheus/console_libraries
+ - --web.console.templates=/etc/prometheus/consoles
+ - --storage.tsdb.retention.time=200h
+ - --web.enable-lifecycle
+
+ # Grafana
+ grafana:
+ image: grafana/grafana:latest
+ container_name: grafana
+ depends_on:
+ - prometheus
+ volumes:
+ - grafana_data:/var/lib/grafana
+ - ./examples/docker-compose/grafana/provisioning:/etc/grafana/provisioning
+ - ./examples/docker-compose/grafana/dashboards:/var/lib/grafana/dashboards
+ ports:
+ - "3000:3000"
+ networks:
+ - nginx-monitoring
+ restart: unless-stopped
+ environment:
+ - GF_SECURITY_ADMIN_PASSWORD=admin123
+ - GF_USERS_ALLOW_SIGN_UP=false
+
+volumes:
+ prometheus_data:
+ grafana_data:
+
networks:
nginx-monitoring:
driver: bridge
diff --git a/examples/docker-compose/README.md b/examples/docker-compose/README.md
index 17206d09..ebab8127 100644
--- a/examples/docker-compose/README.md
+++ b/examples/docker-compose/README.md
@@ -22,64 +22,76 @@ This Docker Compose configuration provides a complete monitoring stack for NGINX
## Architecture
The setup includes:
+
- **nginx**: Main NGINX server with stub_status enabled (for demo purposes)
- **app**: Sample web application served by NGINX (for demo purposes)
- **nginx-exporter**: NGINX Prometheus Exporter that scrapes metrics from NGINX
- **prometheus**: Prometheus time-series database for metrics collection
- **grafana**: Grafana dashboard for visualization
-**Note**: If you have an existing NGINX setup, you only need to run the `nginx-exporter` service. Make sure your NGINX has stub_status enabled and is accessible at the configured scrape URI.
+**Note**: If you have an existing NGINX setup, you only need to run the `nginx-exporter` service.
+Make sure your NGINX has stub_status enabled and is accessible at the configured scrape URI.
## Quick Start
1. **Navigate to the project root directory**:
+
```bash
cd /path/to/nginx-prometheus-exporter
```
2. **For existing NGINX setup - Start only the nginx-exporter**:
+
```bash
docker-compose up -d nginx-exporter
```
+
This assumes you already have NGINX running with stub_status enabled at `http://nginx:8081/stub_status`.
3. **For complete demo setup - Start the full monitoring stack**:
+
```bash
docker-compose up -d
```
+
This starts all services including a sample NGINX server, Prometheus, and Grafana.
4. **Access the services**:
- - **Sample App**: http://localhost:8080
- - **NGINX Server**: http://localhost:80
- - **NGINX Metrics**: http://localhost:9113/metrics
- - **NGINX Stub Status**: http://localhost:8081/stub_status
- - **Prometheus**: http://localhost:9090
- - **Grafana**: http://localhost:3000 (admin/admin123)
+ - **Sample App**:
+ - **NGINX Server**:
+ - **NGINX Metrics**:
+ - **NGINX Stub Status**:
+ - **Prometheus**:
+ - **Grafana**: (admin/admin123)
## Services
### nginx-exporter
+
- **Image**: `nginx/nginx-prometheus-exporter:latest`
- **Port**: 9113
- **Function**: Scrapes NGINX stub_status and converts to Prometheus metrics
### nginx (Main Server)
+
- **Image**: `nginx:alpine`
- **Ports**: 80, 8081 (stub_status)
- **Function**: Proxies to sample app and provides metrics endpoint
### app (Sample Application)
+
- **Image**: `nginx:alpine`
- **Port**: 8080
- **Function**: Serves sample web content
### prometheus
+
- **Image**: `prom/prometheus:latest`
- **Port**: 9090
- **Function**: Collects and stores metrics from nginx-exporter
### grafana
+
- **Image**: `grafana/grafana:latest`
- **Port**: 3000
- **Function**: Provides visualization dashboards
@@ -94,16 +106,19 @@ The setup includes:
## Testing
1. **Check nginx-exporter metrics**:
+
```bash
curl http://localhost:9113/metrics | grep nginx
```
2. **Check stub_status directly**:
+
```bash
curl http://localhost:8081/stub_status
```
3. **Generate traffic**:
+
```bash
for i in {1..10}; do curl -s http://localhost:80 > /dev/null; done
```
@@ -111,11 +126,13 @@ The setup includes:
## Stopping
Stop all services:
+
```bash
docker-compose down
```
Stop and remove volumes:
+
```bash
docker-compose down -v
```
@@ -123,17 +140,20 @@ docker-compose down -v
## Troubleshooting
1. **Check container logs**:
+
```bash
docker-compose logs nginx-exporter
docker-compose logs nginx
```
2. **Verify container status**:
+
```bash
docker-compose ps
```
3. **Test internal connectivity**:
+
```bash
docker exec nginx-server curl -s http://localhost:8081/stub_status
```
diff --git a/examples/docker-compose/app/html/index.html b/examples/docker-compose/app/html/index.html
index c0380fd7..e4e8f1aa 100644
--- a/examples/docker-compose/app/html/index.html
+++ b/examples/docker-compose/app/html/index.html
@@ -121,7 +121,7 @@ 🚀 Load Testing
function sendRequests() {
const results = document.getElementById('results');
results.innerHTML = 'Sending 100 requests...
';
-
+
let completed = 0;
for (let i = 0; i < 100; i++) {
fetch('/?load-test=' + i)
@@ -143,7 +143,7 @@ 🚀 Load Testing
function sendSlowRequests() {
const results = document.getElementById('results');
results.innerHTML = 'Sending slow requests...
';
-
+
let completed = 0;
for (let i = 0; i < 20; i++) {
setTimeout(() => {
@@ -167,7 +167,7 @@ 🚀 Load Testing
function sendAPIRequests() {
const results = document.getElementById('results');
results.innerHTML = 'Sending API requests...
';
-
+
let completed = 0;
for (let i = 0; i < 50; i++) {
fetch('/api/test' + i)
diff --git a/examples/docker-compose/nginx/nginx.conf b/examples/docker-compose/nginx/nginx.conf
index 78791539..7f66a2e0 100644
--- a/examples/docker-compose/nginx/nginx.conf
+++ b/examples/docker-compose/nginx/nginx.conf
@@ -26,7 +26,7 @@ http {
server {
listen 80;
server_name localhost;
-
+
# Proxy to sample app
location / {
proxy_pass http://app:80;
@@ -41,13 +41,13 @@ http {
server {
listen 8081;
server_name localhost;
-
+
location /stub_status {
stub_status;
access_log off;
allow all;
}
-
+
location / {
return 404;
}
diff --git a/examples/docker-compose/prometheus/prometheus.yml b/examples/docker-compose/prometheus/prometheus.yml
index e9b2aca2..b5ae9362 100644
--- a/examples/docker-compose/prometheus/prometheus.yml
+++ b/examples/docker-compose/prometheus/prometheus.yml
@@ -2,7 +2,7 @@ global:
scrape_interval: 15s
evaluation_interval: 15s
-rule_files:
+rule_files: []
# - "first_rules.yml"
# - "second_rules.yml"
From c1e5b237ceb7150bf035783080c6909cea0a356c Mon Sep 17 00:00:00 2001
From: Benji Shohet <97973081+benjisho@users.noreply.github.com>
Date: Thu, 17 Jul 2025 12:29:59 +0000
Subject: [PATCH 5/5] fix: address YAML lint issues
- Fix comment indentation in prometheus.yml
- Fix empty values in docker-compose volumes sections
- All pre-commit hooks now pass
---
docker-compose.test.yml | 4 ++--
docker-compose.yml | 4 ++--
examples/docker-compose/prometheus/prometheus.yml | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/docker-compose.test.yml b/docker-compose.test.yml
index 6028b038..b5a15bc4 100644
--- a/docker-compose.test.yml
+++ b/docker-compose.test.yml
@@ -85,8 +85,8 @@ services:
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- prometheus_data:
- grafana_data:
+ prometheus_data: {}
+ grafana_data: {}
networks:
nginx-monitoring:
diff --git a/docker-compose.yml b/docker-compose.yml
index 6028b038..b5a15bc4 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -85,8 +85,8 @@ services:
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- prometheus_data:
- grafana_data:
+ prometheus_data: {}
+ grafana_data: {}
networks:
nginx-monitoring:
diff --git a/examples/docker-compose/prometheus/prometheus.yml b/examples/docker-compose/prometheus/prometheus.yml
index b5ae9362..a0c1bb1c 100644
--- a/examples/docker-compose/prometheus/prometheus.yml
+++ b/examples/docker-compose/prometheus/prometheus.yml
@@ -3,8 +3,8 @@ global:
evaluation_interval: 15s
rule_files: []
- # - "first_rules.yml"
- # - "second_rules.yml"
+# - "first_rules.yml"
+# - "second_rules.yml"
scrape_configs:
# The job name is added as a label `job=` to any timeseries scraped from this config.