Guides / Monitoring
Monitoring

How to Monitor a JSON-RPC or Blockchain Node's Uptime

Last updated June 2026 · ~9 min read

Here is the trap with monitoring a blockchain node: the obvious check passes when the node is broken.

You point a standard uptime monitor at your node's RPC endpoint. It sends a request, gets back HTTP 200, and reports the node as "up." Green dashboard, no alerts. Meanwhile the node has been stuck on the same block for two hours, is still syncing and returning stale data, or is answering every call with an error object buried inside a perfectly valid-looking JSON response. Everything depending on that node is failing, and your monitoring is happy.

This guide is about closing that gap: why a status-code check is misleading for RPC endpoints, what you actually need to verify, and how to monitor a JSON-RPC or blockchain node so that "up" means usable.

Why HTTP 200 lies about node health

A JSON-RPC endpoint, whether it's an Ethereum execution client, a Solana validator's RPC, a Bitcoin Core node, or any other chain, is an HTTP server sitting in front of the node software. A plain uptime check only talks to that HTTP layer. And the HTTP layer will cheerfully return 200 OK in all of these broken states:

In every case, the surface check ("did the server respond 200?") gives the wrong answer. The information you need is inside the JSON, and you only get it by sending a real RPC call and reading the response.

The core principle. Monitoring a blockchain node is not a connectivity check, it's a correctness check. "Is the port open" is necessary but nowhere near sufficient. The real question is "is this node returning current, valid data right now," and answering it means inspecting the body of a genuine JSON-RPC response, not just its status code.

What to actually check

Three checks, in increasing order of how much they tell you. A good node monitor does all three.

1. The RPC call succeeds and the body is valid

Send a real JSON-RPC request, a POST with a proper JSON body, and confirm the response is well-formed JSON containing a result, not an error object. This alone is a big step up from a status-code check: it catches malformed responses and JSON-RPC-level errors that a 200 would hide.

A minimal Ethereum-style request to fetch the current block number looks like this:

# POST to the RPC endpoint, JSON body: { "jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1 } # Healthy response — note result is a hex string: { "jsonrpc": "2.0", "id": 1, "result": "0x14a2f3c" } # Unhealthy — HTTP 200, but an error object in the body: { "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": "..." } }

Different chains use different methods. Solana's RPC has getSlot and getHealth, Bitcoin Core has getblockchaininfo; the shape stays the same regardless: send a real call, require a result, reject an error.

2. The block height is advancing

This is the check that catches a stalled node, and it's the one almost nothing does by default. A single call tells you the node responded; it can't tell you the node is keeping up. To know that, you have to compare across time: record the block height (or slot) on each check and confirm it has increased since the previous one.

Most uptime tools can't do this. They evaluate each check in isolation: they can assert that a value in the response sits above a fixed threshold, but they don't remember the previous check's value to compare against it. A height that's below a number you set gets caught; a height that's simply frozen while still above that number sails through. For a long time the only fix was a small script you wrote yourself, a cron job that stored the height and compared it on each run.

failover.io does the comparison natively. You give a monitor a tracked value path (the JSON path to the height, like result for eth_blockNumber) and a stall window in seconds. On every check the worker extracts the value, decodes hex if the chain returns it, and compares it to the last one it saw. The moment the value sits unchanged longer than the window, the check fails and the alert chain fires, even though the endpoint is still answering 200 in 30 milliseconds. When we tested this, the check at 119 seconds of stall passed and the check at 179 seconds failed against a 120-second window; the failure reason in the check log read Tracked value stalled: result = 1 unchanged for 179s. If you'd rather keep the comparison in your own code, the script-plus-heartbeat approach still works and failover.io's heartbeat monitors make a fine alert target for it.

3. The node doesn't report itself as syncing

Most clients expose their own sync state. Ethereum execution clients answer eth_syncing, which returns false when fully synced or an object describing progress when not. Solana has getHealth, and Bitcoin Core reports verification progress in getblockchaininfo. Querying the node's own view of itself catches the "up but serving stale data because it's still catching up" case directly.

CheckCatchesMisses if skipped
HTTP 200 onlyServer / port downEverything below
Valid RPC resultJSON-RPC errors, malformed responsesStalled & syncing nodes
Height vs absolute thresholdNode fallen badly behindA frozen height still above the threshold
Height delta (tracked value)Stalled node, lost consensus
Sync-state checkNode serving stale data while catching up

The practical requirements for your monitoring tool

To run the checks above, a monitoring tool has to be able to do things a basic uptime checker often can't:

How failover.io handles RPC monitoring. failover.io is built for this kind of check rather than bending a plain HTTP monitor to fit. A monitor can use POST with a dedicated request-body field, up to 20 custom headers for authenticated endpoints, and body assertions that fail on a JSON-RPC error object or a missing result. For the stalled-node case there's native value tracking: point it at the height field, set a stall window, and the monitor compares each check against the last and goes down when the value freezes. Presets fill in the right call and path for Bitcoin Core, EVM chains, Solana, Tendermint/Cosmos, Polkadot, XRPL and NEAR. A failing node then triggers the same escalation climb to SMS and voice call as any other outage. The free plan covers 5 monitors; start free.

A sensible monitoring setup for a node

Putting it together, a solid setup for a single RPC node looks like this:

  1. A POST monitor hitting the RPC endpoint with a real call (eth_blockNumber, getSlot, or your chain's equivalent), including any required auth headers.
  2. A body assertion that requires a result field and fails on an error object.
  3. A short check interval, frequent enough that a frozen block height shows up within minutes.
  4. Value tracking on the height field with a stall window sized to your chain's block time, so a stalled-but-responding node goes down and alerts instead of sitting green for hours.
  5. An escalation chain on the monitor so a real problem reaches a human. See the companion guide on alert escalation for solo developers and small teams.
  6. If you run more than one node, monitor each independently, since a load balancer in front can mask one bad node behind the healthy ones.

The short version

A blockchain node can return HTTP 200 and still be useless: stalled, syncing, or returning a JSON-RPC error inside a valid-looking response. Monitoring a node properly means treating it as a correctness check, not a connectivity check: send a real RPC call, require a valid result in the body, track the block height across checks so a frozen value fails the monitor, and read the node's own sync state. That requires a monitor that can POST a custom JSON body, send auth headers, assert on the response, and remember the last value it saw, plus an escalation path so that when the node degrades at 3 a.m., a human actually finds out.


Monitor your node properly.

failover.io: first-class JSON-RPC checks, body assertions, and escalation that climbs until someone acknowledges. Free plan, no credit card.

Start monitoring free →