Monitoring and alerting
Most validator outages are found too late: the process is running, its health check says ok, and the node isn’t doing its job. This page lists the signals that catch real problems early, and the false alarms that train people to ignore pages.
Principles
Section titled “Principles”- Alert on causes, not on the node’s opinion of itself.
getHealthand similar checks are computed from data the node may not be receiving. A node that has lost all inbound traffic can look “healthy” for a long time, because nothing new tells it otherwise. “Zero inbound packets while the process is up” is the signal that catches it. - Separate “the process is up” from “the process is healthy”. Whether the log is still being written tells a dead or hung process apart from a live one that is isolated or stuck. Most stall alerts should only fire while the log is still active, or they fire on every shutdown.
- Treat a restart as an episode, not as falling behind. A freshly started node is behind by definition. Page if it hasn’t caught up within N minutes of starting, not on “behind” during that window. Then send one “back in sync after X minutes” message.
- Edge-trigger and debounce. Send one message when a condition starts, one when it clears, and at most a periodic reminder in between. Persist that state, so restarting the monitor doesn’t re-send every alert.
- Watch the watcher. Have the monitor announce itself when it starts, which proves the alert path works. Add a second, independent check from outside the host, so the monitor dying, or the whole host dying, still pages someone.
Alert conditions
Section titled “Alert conditions”Severity: page means wake someone up, warn means look today, and info means dashboard only.
Liveness
Section titled “Liveness”| Condition | Signal | Starting threshold | Severity |
|---|---|---|---|
| Validator unreachable | getSlot against the node’s RPC fails, checked from another host |
2 min | page |
| Not confirming | The node’s confirmed slot isn’t advancing | 2 min | page |
| Not rooting | The node’s finalized/root slot isn’t advancing. Agave logs <identity>: new root <slot> from votor. |
5 min | page |
| Network isolated | Inbound turbine and gossip traffic both zero while the log is still active. In Agave, the shred_sigverify and gossip_receiver datapoints. |
30 s | page |
| Restart loop | Repeated process starts (Agave logs listen_for_signals() has started once per start) |
≥ 3 in 15 min | page |
| Restart not catching up | Time since start without catching up | ~20 min | warn |
| Behind the cluster | The node’s latest replayed slot versus the cluster tip as seen from peers, not /health |
sustained gap of more than a few slots | warn |
- Not rooting: a stuck root also stops ledger cleanup, so the disk will eventually fill. It can happen while replay looks fine.
- Network isolated: require both turbine and gossip to be silent, because one path dropping out alone is noise. Leave repair traffic out: a trickle continues even when the node is isolated.
- Restart loop: usual causes are a bad flag, a full disk, or a slot that crashes replay.
- Restart not catching up: include the gap, the replay rate and an ETA in the message.
Consensus and block production
Section titled “Consensus and block production”| Condition | Signal | Starting threshold | Severity |
|---|---|---|---|
| Voter not participating | The consensus_pool_service datapoint’s certificates_skipped_unstaked counter is non-zero, or certificates_sent isn’t advancing |
any, on the node that should be voting | page |
| BLS key mismatch | Agave: No authorized voter keypair matches rank-map BLS key for vote account …. Firedancer: … unable to vote. |
any | page |
| Vote account underfunded | Vote-account balance against the admission minimum (rent-exempt plus the per-epoch VAT, see Key setup) | warn well ahead of the next epoch boundary | warn → page |
| Leader slots skipped | Your leader slots that end without a confirmed block | 0 produced in your last N rotations, bounded in time | page |
| Dead slots | Agave replay-stage-mark_dead_slot datapoint |
several in ~10 min, excluding expected reasons | warn |
| Bank-hash divergence | Your frozen hash for a slot differs from the cluster’s confirmed hash | any | warn; page if the root stays stuck behind it |
| Vote egress saturated | Agave error alpenglow transport egress channel full; dropping votes/certs! |
any sustained rate | warn |
| Identity balance | Identity account balance, if your block-revenue collector is the identity | below your sweep threshold | info |
- Voter not participating: the node skips sending certificates when its identity isn’t in the staked set. That’s expected on a hot spare, an RPC node or a new validator. On your active voter it means you’re caught up but not taking part.
- BLS key mismatch: the node runs but casts no votes. See Key setup.
- Leader slots skipped: count in rotations rather than slots. A low-stake validator may lead only a few times per epoch, so “last 8 slots” can span days.
- Dead slots: a single dead slot is background noise, and some reasons are expected. A leader abandoning its own block shows up as
BlockAborted. Count only unexpected reasons. - Bank-hash divergence: this usually heals itself, because the node dumps and repairs the block. Rooting past the diverged slot is the definitive sign it has healed.
Host-level concerns
Section titled “Host-level concerns”Memory
- Alert on
MemAvailable, neverMemFree. On a validator almost all free RAM is page cache, soMemFreesits near zero on a healthy machine. - Use two triggers, each sustained for about a minute:
- a level trigger, for example below 15% warn and below 5% page;
- a trend trigger: fit the last ~5 minutes and warn when exhaustion is projected within 30 minutes, page within 15.
- On a large-RAM machine the trend trigger buys the most time, because the level threshold is often crossed only minutes before the out-of-memory kill.
- Only evaluate the trend below roughly 40% available. Otherwise memory settling onto its normal plateau after a restart extrapolates to zero, and pages with half the RAM free.
- Put the rate in the message (“falling 3 GB/min, empty in ~29 min”). A level alone isn’t something anyone can act on.
Disk
- Watch capacity on every volume: ledger, accounts, snapshots, logs, and the monitor’s own data. For example, warn at 85% and page at 95%.
- In summaries, rank “disk full” above “stalled” or “behind”: a full disk is usually the cause of a stall, not a symptom of one.
- Common causes of a filling disk:
- a root that isn’t advancing, since ledger cleanup only purges below the root;
- snapshots piling up;
- logs without rotation;
- monitoring data kept with no retention limit.
- Graph I/O rates, but alert on capacity.
Network
- Graph receive and transmit traffic and drops per interface. When you sum interfaces, don’t double-count bond or bridge member interfaces, or tunnels whose traffic also appears on the physical NIC.
- Alert on inbound silence (see “Network isolated” above). NIC drops belong on a graph, not a pager.
Clock
- Keep NTP or chrony synchronized (validator-ansible: time sync), and alert when it’s unsynchronized or the offset goes beyond ~100 ms.
- If you measure delivery latency per leader from local data, compare each leader against the window’s median. Your own clock offset then cancels out.
CPU and limits
- Watch for frequency throttling (CPU governor not set to
performance, or thermal limits) and for saturated replay or scheduler threads. These are graphs for investigating, not pages. - Check the file-descriptor limit,
vm.max_map_countand the UDP buffer sysctls once at startup. Agave logs warnings for several of these.
Host tuning itself (governor, sysctls, limits) is covered by the validator-ansible template.
False alarms to design out
Section titled “False alarms to design out”Each of these has paged real operators.
/healthsaysokright after a restart while far behind. Agave compares against the newest confirmed slot stored in its own blockstore, which survives the restart, so the reference point is where the cluster was before shutdown. Don’t page on, or announce,/healthchanges while the node is starting up.- “Behind by 0” right after a start. If “caught up” requires the gap to stay low for a while, the node reads “not caught up” at gap 0 during that time. Page “behind” only once the gap itself exceeds the threshold.
- A balance that reads 0 now and then.
getBalancewith default commitment answers from the finalized bank. On a node that’s catching up, that bank can be old enough to return 0. Checkcontext.slotin the response, discard readings from a bank far behind the tip, and debounce. - Dead slots that are normal protocol behavior. Leaders abandoning their own blocks produce dead slots with expected reasons. Exclude those.
- “Isolated” during startup. “Last packet seen” timestamps carried over from before a restart make a node loading its snapshot look isolated. Reset them on every start, and stay quiet until the first packet arrives.
- The memory trend extrapolating a plateau. See the 40% gate above.
- Fake divergences from half-written log lines. A log tailer that treats an unterminated last line as complete parses truncated hashes. Hold partial lines until their newline arrives.
- A healed divergence escalated again later. Only escalate a divergence while the root hasn’t passed it.
- Planned restarts paging the external check. Either accept the page, or grade hot spares and secondary nodes as warn: a spare being down reduces redundancy, but it isn’t an outage. Silencing windows are easy to forget to close.
- “Not receiving” on a feed the host never subscribed to. For opt-in feeds (for example multicast shred feeds), alert on no traffic only if the host is configured to receive.
Sources
- Agave votor consensus pool stats (votor/src/consensus_pool_service/stats.rs @ v4.4.0)
- Agave certificates skipped when unstaked (votor/src/consensus_pool_service.rs @ v4.4.0)
- Agave votor egress warning (votor/src/voting_service.rs @ v4.4.0)
- Agave votor root log line (votor/src/root_utils.rs @ v4.4.0)
- Agave dead-slot reasons (core/src/replay_stage/dead_slots.rs @ v4.4.0)
- Agave votor BLS key matching (votor/src/voting_utils.rs @ v4.4.0)
- Firedancer votor BLS key check (fd_votor_tile.c @ v26.10.4)
- Firedancer docs, Monitoring
- Anza docs, Monitoring best practices (agave-watchtower)
- Linux kernel docs, /proc/meminfo (MemAvailable)
Content CC BY 4.0 · code snippets CC0 · site code MIT ·contribute· maintained by Vortex