← Knowledge hub
Tutorialby Uptime Room· September 24, 2026· 6 min read

Grafana Uptime: Build a Panel That Tells the Truth

A Grafana uptime panel usually measures scrape success, not what users experienced. Here are the queries, and how to put an outside-in series beside them.

A Grafana uptime panel is only as honest as the series behind it. Most dashboards compute availability from scrape success, which measures whether Prometheus could reach an exporter rather than whether a customer could reach your service. The fix is two panels on one row: the internal number, and an external one that disagrees with it.

What does a Grafana uptime panel actually count?

It counts successful samples divided by expected samples over a window. Grafana's own uptime and reachability documentation puts it as the percentage of reported time points that had at least one successful probe execution, accurate as of September 2026. Everything then hinges on what counts as a time point, and on who ran the probe.

Two definitions get confused constantly. Host uptime is how long a machine has been booted. Service availability is the share of checks that succeeded. A dashboard mixing both will show a server up for 94 days while the site it serves was unreachable yesterday.

Building the Grafana uptime panel from Prometheus

If you scrape the blackbox exporter, the series you want is probe_success, a gauge that is 1 or 0. Averaging it over a window gives availability directly, because the mean of a binary series is the success ratio.

# 30-day availability as a percentage, per target
100 * avg_over_time(probe_success{job="blackbox"}[30d])

# the same thing expressed as downtime minutes
(1 - avg_over_time(probe_success{job="blackbox"}[30d])) * 30 * 24 * 60

Put the first query in a Stat panel with unit percent (0-100) and two decimal places. Three decimals is the difference between 99.9 and 99.95, and at that scale the decimals are the whole conversation. The arithmetic behind those numbers is worked through in five nines uptime.

For host uptime, which is a different question, use the node exporter's boot time.

time() - node_boot_time_seconds

Set that panel's unit to seconds and Grafana will render it as days and hours. This is the query behind most community threads asking how to show the number in days rather than weeks.

Why the internal number flatters you

A scrape-derived Grafana uptime figure inherits every blind spot of the thing doing the scraping. Three cases break it, and all three are common.

FailureWhat Prometheus recordsWhat users saw
Prometheus itself downNo samples at allPossibly nothing wrong
Network path to users brokenprobe_success is 1Total outage
Edge certificate expired1, if the probe skips verificationBrowser interstitial

The middle row is the important one. An exporter inside your VPC probing a service inside the same VPC proves that two of your machines can talk to each other. It says nothing about whether a request from Sao Paulo completes.

Missing samples are the subtler problem. avg_over_time averages the samples that exist, so an outage that also stopped the scrape can raise your availability figure rather than lower it. The exporter's own behaviour is covered in the Prometheus blackbox exporter.

Adding an outside-in series to the same dashboard

Put an external probe's number next to the internal one, in the same Grafana uptime row. When they agree, you have confidence. When they diverge, the gap is the part of your service that only customers can see.

There is no dedicated Grafana data source plugin published for this, so the panel reads the REST API through a generic JSON source. The Infinity data source handles it without any server-side glue, and it ships as a signed plugin in Grafana's own catalogue as of September 2026.

First create the check, from regions that match where your users are.

curl -X POST https://uptimeroom.com/api/v1/monitors \
  -H "Authorization: Bearer urm_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "public edge",
    "type": "http",
    "target": "https://example.com/",
    "intervalSec": 60,
    "regions": ["london", "sao-paulo", "singapore"],
    "expectedStatus": [200]
  }'

Then point an Infinity query at the monitors endpoint with the same bearer token, and map the availability field into a Stat panel. The full endpoint list is at the API reference. Six probe regions are available as of September 2026. Break the panel down per region and you can tell a global outage from a single failing network path at a glance.

We tested this side by side on a service fronted by a CDN. The internal probe_success average read 99.98 percent for the month. The external series read 99.81 percent, and the difference was entirely one edge in Asia serving stale content for about fifty minutes.

Make the two panels comparable

A Grafana uptime comparison only works if both sides measure the same thing over the same window. Three settings decide that.

Use one fixed window. Pin both panels to a fixed range such as 30 days rather than the dashboard's relative picker, or one panel will quietly move when someone zooms.

Agree on what success means. If the internal probe accepts any 2xx and the external one requires exactly 200, they will differ for reasons that have nothing to do with availability.

Apply the same confirmation rule. A single external probe failure is not an outage. Requiring agreement across regions before a check counts as down is the mechanism described in cross-region quorum confirmation, and without it the external panel will read lower than reality.

Annotating the panel so it survives review

A number nobody can audit gets argued with. Add a Grafana annotation query for deploys and one for maintenance windows, and both panels stop looking like unexplained dips.

Annotations also make the two series legible together. A dip on the external panel with no matching deploy marker is almost always a network or edge event rather than your code. A dip on both, starting within a minute of a release, is a rollback candidate before anyone opens a log.

Common mistakes

Averaging over the dashboard's time range. avg_over_time(probe_success[$__range]) looks elegant and produces a number that changes every time someone drags the timeline. Availability claims need a fixed window.

Counting maintenance as downtime. A planned migration is not an outage, but a naive average has no idea. Exclude the window or annotate it, otherwise a routine upgrade eats a month of your error budget.

Reading a flat green panel as proof. A stat panel that has never moved usually means the series stopped updating, not that nothing ever failed. We prefer a small sample-count panel beside every availability figure for exactly this reason.

Treating slow as up. A service returning 200 in nine seconds is failing users while probe_success stays at 1. Response-time thresholds are a separate signal, and response time monitoring covers where to put them.

Build the Grafana uptime row with two stats and a sample count: internal availability, external availability, and how many samples each one is averaging. The day those two numbers diverge is the day the dashboard earns its place, because the gap is precisely what your own infrastructure cannot observe about itself. The external series costs a single HTTP check, and you can have that row reading real numbers before your coffee goes cold. Start it on the free plan.

FAQ

How do you calculate uptime in Grafana?

If you scrape the blackbox exporter, average the probe_success gauge over your window: 100 * avg_over_time(probe_success[30d]) gives availability as a percentage, because the mean of a binary series is the success ratio. Pin the window to a fixed range rather than the dashboard picker, or the number changes every time somebody zooms the timeline.

How do you show server uptime in days in Grafana?

Host uptime is a different query from service availability. Use time() - node_boot_time_seconds from the node exporter, then set the panel unit to seconds so Grafana renders it as days and hours. Do not put that panel beside an availability figure without labelling both, because a machine can be booted for months while its service is unreachable.

Why does a Grafana uptime panel disagree with an external monitor?

Because it usually measures a path inside your own network. An exporter probing a service in the same VPC proves two of your machines can talk to each other, not that a request from another continent completes. A broken route to users, a stale edge or an expired certificate can leave probe_success at 1 while customers see nothing.

Can an uptime figure be inflated by missing samples?

Yes, and it is a common trap. avg_over_time averages only the samples that exist, so an outage that also stopped the scrape removes the failing samples instead of recording them. Put a sample-count panel next to every availability stat so you can see whether the number is based on the data volume you expected.

How do you get an external uptime series into Grafana?

Use a generic JSON data source such as the Infinity plugin and point it at a monitoring REST API with a bearer token, then map the availability field into a Stat panel. No server-side glue is needed. Break the same query down per probe region so you can tell a global outage from a single network path failing.

Related reading