An xOneRS node serves two health endpoints next to /metrics: /livez answers 200 while the process runs, and /readyz answers 200 only when the node has a fresh head, is not syncing and has no fault. Point a load balancer or a Kubernetes readiness probe at /readyz, and it sends RPC traffic only to nodes that can answer at the tip. This post gives the rules from the source and a load-balancer example. One limit we found, a fixed 60-second head age equal to the XDPoS v2 timeout period (issue #1064), was fixed the same day: the limit is now configurable and defaults to 180 seconds.
Where are the health endpoints?
On the metrics listener, so the node must run with --metrics. The default address is 127.0.0.1:6060 (--metrics.addr, --metrics.port). The listener runs on its own thread, serves at most 8 connections at once and gives each request 2 seconds, so a slow prober cannot stall the node (observe/metrics.rs).
| Path | 200 when | Otherwise |
|---|---|---|
/livez |
the process runs: body ok |
no answer |
/readyz |
every check passes: body ready |
503, body not ready: <reasons> |
/metrics |
always: Prometheus text |
Any other path answers 404. On one of our net2040 xOneRS nodes on 2026-11-15, /readyz answered 200 ready and /livez answered ok.
When is a node not ready?
/readyz lists every reason at once, in this order:
| Reason | Meaning |
|---|---|
no_head |
The node has no head block yet. |
stale_head |
The head block's timestamp is older than --metrics.readyz.maxheadage (default 180 seconds; 60 seconds before PR #1075). |
syncing |
A header-only or pivot sync is running. |
degraded |
A durability or signer-fencing fault. |
shutting_down |
Shutdown has started. The listeners still run, so a load balancer sees the 503 before the RPC closes. |
For example: not ready: stale_head syncing. The reason names are stable identifiers (#718), so an alert or a script can match them.
How do I use /readyz behind a load balancer?
Check /readyz on the metrics port and send traffic to the RPC port. An HAProxy backend, as an example:
backend xone_rpc
option httpchk GET /readyz
http-check expect status 200
server node1 10.0.0.11:8545 check port 6060 inter 10s fall 3 rise 2
- The metrics listener must be reachable from the load balancer. Bind it to a private address, not to a public one: the same port serves
/metrics. See exposing RPC safely. - Use
fall 3or more, so one late probe does not remove a node.
The xOne files already use these endpoints: the Helm chart's liveness probe is /livez every 20 s and its readiness probe /readyz every 10 s; the Stereum file names both; xone wait-ready --readyz <url> waits for a 200; and ops/xone-health-watch.sh posts a webhook when /readyz changes. See installing xOne and the CLI tools.
Why did every node turn not ready at the same time?
Until PR #1075 (2026-11-15), the head-age limit was a fixed 60 seconds. The XDPoS v2 timeout period is also 60 seconds on net2040 and on mainnet's current row. When one round times out, the next block comes about 61 seconds after the last one. On net2040, block 836,183 came at 12:35:48 UTC on 2026-11-14 and block 836,184 at 12:36:49 UTC (reading a skipped round). For about a second before the next block, every node at the tip has a head older than 60 seconds and answers stale_head.
A probe with fall 3 at 10-second intervals does not remove a node for one such second. Two or more timeout rounds in a row would. PR #1075 made the limit configurable with --metrics.readyz.maxheadage and set the default to 180 seconds, above one timeout period plus a mine period. A head 61 seconds old is now ready. Builds older than that PR, including v0.1.0, keep the 60-second limit. We did not see a load balancer drop a node; this is what the code and the block times say.
How does this compare with other clients?
| Client | Health endpoint | Rule, as documented |
|---|---|---|
| xOneRS | /livez, /readyz on the metrics port |
head age at most --metrics.readyz.maxheadage (180 s), not syncing, not degraded, not shutting down; reasons listed |
| nethermind | /health |
503 when the node has no peers, per the Nethermind health check docs |
xOne's /readyz does not check the peer count. A node with no peers stops getting blocks and turns stale_head when its head passes the age limit.
Frequently asked questions
Which endpoint should a Kubernetes readiness probe use?
/readyz on the metrics port. Use /livez for the liveness probe, so a syncing node is not restarted.
Why does /readyz answer 404?
The path is wrong, or another program owns the port. The node answers only /metrics, /livez and /readyz.
Why is my node not ready while it syncs?
syncing is one of the reasons. The node turns ready when sync ends and the head is younger than the age limit.
Can I change the head-age limit?
Yes, with --metrics.readyz.maxheadage <seconds> on builds after PR #1075. The default is 180 seconds.
Sources
This article describes the source and evidence at its review date. Check the linked repository for current implementation and qualification status.
Explore the engineering evidenceEdit this page on GitHub opens the Markdown source (content/blog/xone-health-checks/index.md). GitHub may ask you to sign in. Report a correction opens a prefilled GitHub issue for this page.