An NGINX worker that answers a scrape on its own listener is a different engineering posture from running a second HTTP service beside it. Today, Categorica has released nginx-stats-prometheus as open-source software, and the design goal is deliberately narrow: collect statistics without spawning a separate exporter process. For teams whose observability path already runs NGINX in front of services, Prometheus as the scrape layer and Grafana as the visualization surface, the release removes one moving part instead of adding one.
Before this module existed, there was effectively one mainstream option for collecting NGINX statistics and metrics: NGINX Prometheus Exporter, a fully featured Prometheus exporter. Categorica's objection was architectural rather than functional — the question raised in the write-up is simply why the metrics endpoint must run its own web-server.
In production terms, that is not a blocker. The exporter can be deployed as a systemd service, and therefore as a regular binary, but NGINX Prometheus Exporter is more commonly run through Docker side-car containers. That side-car pattern adds complexity the maintainers did not consider necessary, and it reintroduces the recursion problem of monitoring: metrics collection itself evades metrics collection, in the manner of “who watches the watchmen”.
The ideological indignation did not survive the free evenings and weekends available to the project. The resulting NGINX plugin became the team's preferred statistics-collection mechanism, and As of today it is published for anyone to build and audit.
nginx-stats-prometheus, published by Categorica; further details sit in the Github repository's README.md.load_module, serving a Prometheus-compatible route from the NGINX process itself.server/upstream directives, prefixes longer than 255 characters ignored, 1 megabyte of default shared memory for statistics.Command-line comfort is assumed. A basic understanding of the GNU “configure, make, make install” dance is useful, though not strictly necessary. Building NGINX from source is recommended so the required features are compiled in, because a package manager's build of NGINX may differ from the project's; it is still not mandatory.
Makefile can perform this step.--with-compat and --with-http_stub_status_module.Skip this step if an appropriate NGINX version is already installed. In a terminal, use:
cd path-to-nginx-source;# we'll use the path to the NGINX source whilst building the plugin.export NGINX_SRC=`pwd`;./configure --with-compat --with-http_stub_status_module;make -j;sudo make install;
This builds and installs NGINX, typically with the /usr/local prefix. For more details about building NGINX, consult their documentation.
If an appropriate NGINX version is already installed, the supplied Makefile determines its version by invoking nginx -v, then downloads the NGINX sources needed to build the module.
If that behavior is not wanted, set the path to the NGINX sources with the environment variable NGINX_SRC; this skips the version check and source download.
Afterward, build the plugin with:
make modulesudo make install
This builds the plugin and installs it to the correct plugin location. More information can be found on the Github repository's README.md.
After building the plugin, configuration must describe the route where statistics should be exposed.
load_module modules/ngx_http_stats_prom_module.so;http {# Optional: set shared memory size (default 1m)# prometheus_metrics_shm_size 2m; server { # standard NGINX server directives apply here listen 9100; location /metrics { prometheus_metrics; } }}
This configuration exports statistics suitable for Prometheus ingestion at http://hostname:9100/metrics. Assuming the NGINX instance runs locally, the endpoint can be queried with curl:
curl http://localhost:9100/metrics
Sample response:
# HELP nginx_up Whether the nginx process is up
# TYPE nginx_up gauge
nginx_up 1
# HELP nginx_process_pid PID of the worker process (serving metrics)
# TYPE nginx_process_pid gauge
nginx_process_pid 2985885
# HELP nginx_http_requests_total Total number of HTTP requests
# TYPE nginx_http_requests_total counter
nginx_http_requests_total 434488
# HELP nginx_http_responses_total HTTP responses by status class
# TYPE nginx_http_responses_total counter
nginx_http_responses_total{status="1xx"} 0
nginx_http_responses_total{status="2xx"} 42598
nginx_http_responses_total{status="3xx"} 771
nginx_http_responses_total{status="4xx"} 77
nginx_http_responses_total{status="5xx"} 1
# etc.
Read as a verification procedure, this output tells an engineer three checkable things: the scrape surface is live inside the web server itself (nginx_up reporting 1), the metrics are attached to a specific worker (nginx_process_pid showing 2985885), and the traffic shape is already aggregated by status class — 434488 requests with 42598, 771, 77 and 1 in the 2xx, 3xx, 4xx and 5xx buckets, and 0 in the 1xx bucket. The deployment limits are equally visible: the commented prometheus_metrics_shm_size line defaults to 1m and shows 2m as the tuning knob, while port 9100 has to be reserved on the host.
Once the process is running, NGINX metrics should be visible on the route specified in the configuration. Once started, the available metrics are:
| Metric | Type | Labels |
|---|---|---|
nginx_up | gauge | |
nginx_process_pid | gauge | |
nginx_http_requests_total | counter | |
nginx_http_responses_total | counter | status (1xx – 5xx) |
nginx_http_requests_by_method_total | counter | method (GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH, other) |
nginx_http_request_bytes_total | counter | |
nginx_http_response_bytes_total | counter | |
nginx_http_version_total | counter | version (1.0, 1.1, 2.0, 3.0) |
nginx_http_request_duration_seconds | histogram | (0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10) |
nginx_connections_active | gauge | |
nginx_connections_reading | gauge | |
nginx_connections_writing | gauge | |
nginx_connections_waiting | gauge | |
nginx_connections_accepted_total | counter | |
nginx_connections_handled_total | counter |
These are available as soon as the process launches, and are measured from process start.
In addition, basic metrics exist for each server or upstream directive defined in nginx.conf. These appear after the first received request on that prefix.
| Metric | Type | Labels |
|---|---|---|
| <prefix>_requests_total | counter | server / upstream |
| <prefix>_response_total | counter | server / upstream, status (1xx – 5xx) |
| <prefix>_request_bytes_total | counter | server / upstream |
| <prefix>_response_bytes_total | counter | server / upstream |
| Dimension | nginx-stats-prometheus | nginx-prometheus-exporter |
|---|---|---|
| Process model | Runs inside NGINX; no separate exporter process | Separate endpoint that runs its own web-server |
| Typical deployment | Compiled module loaded with load_module | systemd service / regular binary, or more commonly Docker side-car containers |
| Metric coverage | Significantly fewer statistics are collected | Fully featured set |
| Version coupling | Tied to the NGINX version it is built against; updating NGINX typically requires rebuilding | Not described as tied to a build |
| Maturity | Significantly less battle tested; less mature and less used | The established option to date |
| Licence | MIT | Not stated in the source article |
| Support | None offered by the maintainers | Not stated in the source article |
At most, 512 server or upstream directives are supported. Prefix lengths greater than 255 characters are ignored. Only 1 megabyte of memory is allocated for statistics. If this is exhausted, statistics collection is silently skipped. This can be configured in NGINX with prometheus_metrics_shm_size, whose default is 1m and which the sample configuration illustrates with a 2m value.
nginx-prometheus-exporter; significantly fewer statistics are collected.--with-compat and --with-http_stub_status_module.nginx-stats-prometheus is released under an MIT licence. Pull requests are welcome, with one condition: a human must have at least reviewed the patch-set before submission. On the support side, the maintainers currently offer no support and cannot guarantee help with any issues that may arise from its use — so any adoption decision is effectively an internal ownership decision.
--with-compat --with-http_stub_status_module when your distribution build is unknown; otherwise verify both flags are present.curl http://localhost:9100/metrics before wiring Prometheus scrape config, and check nginx_up, nginx_process_pid and the status-class counters against known traffic.prometheus_metrics_shm_size (default 1m) against your actual number of server/upstream blocks; treat 512 directives and 255-character prefixes as design limits.nginx-prometheus-exporter for panels that depend on richer coverage, since fewer statistics are collected here.For a reader whose job is to judge whether an open-source project is usable rather than merely interesting, this release is best filed under “narrow, inspectable, and build-dependent”: the value is architectural — one fewer HTTP service between you and your metrics — and the cost is equally architectural, in the form of version pinning, reduced coverage and an absent support channel. If you want to run NGINX without containerisation, are looking to simplify your deployment configuration, or just prefer running statistics collection from a plugin, try nginx-stats-prometheus — then decide, from the verified endpoint output, whether the trade is worth owning.