Monitoring and alerting
Twenty-two lessons from building an alerting system after the day four failures went unnoticed.
This theme starts on 20260906, the day Node0 found two hosts hung for ten hours, four switches minutes from an automatic thermal shutdown, a disk dead for a day and a half, and a rack that had rebooted itself, all at once and all by accident. Nothing paged anyone. The whole monitoring stack was built in the weeks after that, and these lessons are what was learned while building it, mostly by getting it wrong first.
They fall into three groups, and they arrived roughly in that order. The first group is about signals that exist but are not being read: the hidden device-statistics log on the fleet’s most common drive model, the firmware event log on every server, the flow-control counters on the switch ports. Nearly every failure of 20260906 had already announced itself somewhere; the founding brief was written by listing those announcements rather than designing from a blank page.
The second group is about monitors that are themselves broken, which turned out to be the larger problem. Four monitors written in one week all had defects, and the silent ones were the dangerous ones. A monitor that polls over a login session faulted the very thing it was watching. An alert keyed to a device path paged seven times for drives that had not changed. An alert built on a wrong mental model reported a healthy standby as a fault, continuously. In every case, reading the check for correctness was not enough; only causing the condition proved anything.
The third group is about the alerting path as a system in its own right, with its own failure modes: a folder permission that silenced everything for 98 minutes, a quorum threshold that a fourth cluster member made silently wrong, rules shipped before the thing they watch existed, a maintenance silence that muted almost nothing, and the discovery that no monitor inside a network can tell a closed port from an exposed one at that network’s own edge.
The two later additions, from July and August, predate the build and were folded in because the rest of the theme kept arriving at their conclusions independently.
Declined #
- The habit of reading fire-and-resolve notification pairs together as a single incident (20260912): a change in how someone reads a channel, not a change to a system, tool or runbook. Folded into the dead-man’s-switch lesson as context.
- A dashboard-embedding quirk in the storage cluster’s Grafana folder (20260911): a one-time documentation note about an exception to a convention, with no incident and no cost attached.
- Two permanently unreachable scrape targets removed from monitoring (20260910): a clean, uneventful correction with no cost and no near-miss.
- Windows event logs not yet flowing into the log aggregation system (open as of 20260920): a known gap, not yet a dated event with a cost and a fix.
- The Ceph maintenance-mode procedure lapsing a second time, on a different host, after having been fixed once: an adherence lapse rather than a defect in the monitoring system. The check that catches it is procedural discipline, not a new alert.
The lessons, in order
- 2.1The day four failures went unnoticed, and none of them paged anyone20260906after redaction
- 2.2A drive can be physically gone for nine minutes and every health check stays green20260907as-is
- 2.3Four monitor bugs in one week, and the silent ones were the dangerous half20260907 to 20260908as-is
- 2.4A UPS running its own self-test looks exactly like a real power event, and nothing was watching UPS state at all20260907 evening, found 20260908 04:00as-is
- 2.5Building a dead-man's switch that only proves itself by being killed20260906 to 20260911as-is
- 2.6A monitor that logs into the thing it watches becomes part of the fault it reports20260909as-is
- 2.7A monitoring tool with zero monitors configured20260907 to 20260909after redaction
- 2.8The hard drives were hiding their worst counters, and a reboot's letter shuffle paged seven false alarms20260910after redaction
- 2.9An alert fired continuously on the normal state of a design it misunderstood, and was removed rather than tuned20260911as-is
- 2.10An alert redesigned by hand three times before it survived independent review20260911after redaction
- 2.11A folder permission left from a partial deploy silenced the entire alerting path for 98 minutes20260911as-is
- 2.12A written-down cluster quorum threshold became wrong, in the dangerous direction, the day a fourth member joined20260913as-is
- 2.13Shipping an alert before the thing it watches exists pages a human about your own unfinished work20260913as-is
- 2.14Creating an inventory record for a new host starts paging about it before it is built20260915as-is
- 2.15An abandoned wait loop locked the only Grafana account out for a full day, by re-arming its own lockout20260915as-is
- 2.16Silencing a host by name during maintenance muted almost nothing the maintenance actually caused20260917as-is
- 2.17Forty-one of three hundred and forty-seven alerting rules carried no instruction for whether to act20260920as-is
- 2.18A monitoring runbook's own numbers were stale prose, not live truth20260920as-is
- 2.19A capacity alarm that compared totals paged every night, and a notifier that did not log its own failures went silently blind20260729 to 20260801after redaction
- 2.20Backrest's own error API caps at 21 entries, and an operator chased a fix that had already worked20260819as-is
- 2.21You cannot see your own front door from inside your own home20260820 to 20260907as-is
- 2.22Two measuring tools lied about a healthy network during a live firewall upgrade20260911as-is
Source: node0 lessons v0.1, section 2. Sanitized: checklist v0.1, 20260921; voice pass 20260921. Part of oznog.com/node0.
