Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

12.4 stub_status & Live Metrics

Logs are history; stub_status is now. One location, seven counters, zero dependencies — the OSS answer to "is the front door alive and how hard is it working?"

Levels: Advanced ~25 min read

Advanced

What you will learn

  1. Enabling and reading stub_status — every line of it.

  2. Protecting it (it's an endpoint like any other) and scoping it (why the example below gives it its own server).

  3. What each counter is good for — and the math that turns them into rates.

  4. The honest limits: why you still need logs and a metrics system (and what Plus's extended status adds).


1. The endpoint

nginx
server {
    listen      9401;
    server_name status.example.test;

    allow  127.0.0.1;      # an endpoint like any other: protect it
    deny   all;

    location = /status {
        stub_status;
    }
}

stub_status ships in the standard build ( --with-http_stub_status_module — your Lesson 1.3 nginx -V receipt has it) but is off until you declare a location for it. The example above gives it a dedicated server on its own port — the common production pattern is either that, or a location on the main server ACL'd to the monitoring VLAN. What you should NOT do is expose it publicly: it leaks topology and load levels.

The output (real capture):

text
Active connections: 1
server accepts handled requests
 62 62 62
Reading: 0 Writing: 1 Waiting: 0

2. Reading it, line by line

Line

Field

Meaning

1

Active connections

currently open connections (clients + upstreams counted separately — this is client-side)

2–3

accepts

total connections accepted since start

handled

total handled (equal to accepts unless resource limits kicked in)

requests

total HTTP requests — the counter your rate math feeds on

4

Reading

workers currently reading request headers/bodies

Writing

workers currently returning responses

Waiting

idle keep-alive connections waiting for the next request

The accepts == handled equality is itself a signal: divergence means the kernel accepted connections nginx couldn't serve (listener queue draining, worker_connections ceiling) — early overload evidence that predates errors.

3. Counters → rates → alerts

All three big numbers are monotonic counters; they only make sense as rates over time (Prometheus, etc. do this natively with rate(); a cron'd curl + diff does it in a pinch):

text
requests:  62 62 62        at T0
requests:  66 66 66        at T0+3s (after 3 probes + 1 status poll)
                           -> ~1.3 rps

That's the receipt of the simplest possible check — the counter advanced by exactly the requests sent — which is also your smoke test that the exporter you deploy reads the right thing.

What earns an alert:

Metric

Alert shape

Active connections

trending to worker_processes × worker_connections — the ceiling from Lesson 9.1, now observable

Waiting vs Writing

thousands of Waiting + low Writing = mostly-idle keep-alives; fine, unless FD pressure says otherwise

Reading sustained > 0

slowloris-shaped clients or giant bodies (Module 7 territory)

request rate collapse

traffic vanished — often the FIRST public symptom of a DNS/cert/edge failure

4. The honest limits (and Plus)

stub_status is a snapshot of seven numbers. It cannot tell you:

  • latency — no timers here; that's the log's job (12.3),

  • per-upstream health — Module 5's error log is richer,

  • per-route anything — no labels, no dimensions,

  • history — it's "now," not "the last hour."

The pipeline reality: exporters scrape stub_status every N seconds for the time-series; logs carry the forensic truth; the two corroborate. When the graph says p95 spiked at 14:02, the log lines at 14:02 say which upstream, which URIs, which request_ids. That pairing — coarse continuous metrics + fine discrete logs — is the whole observability architecture.

NGINX Plus's extended status (the Module 1 comparison's "built-in dashboard" row) adds exactly the missing dimensions: per-upstream queues, per-zone traffic, per-server request counts in JSON at /status. Same philosophy, more labels — and OSS closes the gap the way this module does: logs as data + a metrics scraper, at the cost of one more moving part.

Try it yourself

  • curl -s http://127.0.0.1:9401/status — poll it, generate some traffic, poll again: the request counter advances with load (62 → 66 in the receipt above).

  • Load it: for i in $(seq 1 200); do curl -s -o /dev/null http://127.0.0.1:9401/ & done; wait; curl -s http://127.0.0.1:9401/status — watch Active/Reading/Writing move (and Waiting accumulate with keep-alive clients).

  • Break the ACL: change allow 127.0.0.1 to a bogus range, reload, curl — 403. Metrics endpoints get scanned too.

Q&A

Q: Why stub_status and not just export /health with sidecar metrics? A: /health says "alive." stub_status says "alive AND how hard" — capacity signals you want before the outage. Also: it's built-in, zero-dependency, and every monitoring stack on earth has a collector for it.

Q: Where does QUIC/HTTP/3 traffic show up? A: Same counters — h3 sessions are connections and requests. (QUIC's connection-migration can keep one "connection" alive across network changes; counters still track it as one.)

Q: Multiple workers — is this per-worker? A: The counters are process-global (shared memory), summed across workers. Active connections likewise. No per-worker split in OSS — Module 16's process tools cover that depth when you truly need it.

Takeaways

  • stub_status = seven live counters behind one directive; ACL it like any endpoint, scope it to its own server/port.

  • Monotonic counters only matter as rates; request-rate collapse is often the first public symptom of edge failure.

  • accepts == handled divergence and Active approaching the Lesson 9.1 ceiling are your capacity alarms — visible here, in seven numbers, before any error is thrown.

  • It's a snapshot, not history: metrics (scraped) for continuous signals, logs (12.3) for forensics — the pairing is the architecture.

Was this page helpful?

Mark this learning resource complete to track your learning progress.