Uptime monitoring in 2026: revisit Uptime Kuma, Gatus and the Blackbox Exporter before a customer tells you the site is down
Take a travel company in Udaipur that runs an online booking site for about 140 heritage hotels, lake-side guest houses and homestays in Udaipur, Kumbhalgarh and Mount Abu (an invented example, not a real client). Guests book rooms on the website, hotel owners confirm and block dates on a partner portal, a payment gateway calls back to confirm each payment, and a WhatsApp message goes to the guest once the booking is final. Two application VMs sit behind a load balancer at one cloud provider. Prometheus and Grafana run on a third VM in the same network and watch CPU, memory, request rates and error rates. The team is four developers and one person who looks after infrastructure.
In the wedding season, a change to the load balancer sent the booking domain to a default web server page. That page answered HTTP 200, so the one health check the team had, a curl from the Prometheus VM to the application’s /health path, stayed green. The application servers were healthy, and Grafana showed nothing unusual except that bookings had stopped. A travel agent in Ahmedabad phoned three hours later to ask why the site was “showing a blank welcome page”. A month earlier, the Prometheus VM itself had been stopped by mistake during a resize, and for a whole afternoon nothing alerted, because the thing that sends alerts was the thing that had stopped.
This post compares Uptime Kuma, Gatus and the Prometheus Blackbox Exporter (with Prometheus and Alertmanager) as of 20 October 2026. It looks at what each one can check, how fast and how loudly it tells you, where it keeps its data, how it handles flapping and maintenance, how it is configured, and how you find out when the monitor itself is down. Hosted and other open options such as Grafana Cloud Synthetic Monitoring, Upptime and OpenStatus get a short note where their own documentation supports it.
The facts come from primary material: the projects’ READMEs, wikis, configuration references and changelogs, GitHub release data, the Prometheus documentation, and GitHub advisory, NVD and OSV records. The drill ran Uptime Kuma 2.5.6, Gatus 5.37.0, Blackbox Exporter 0.29.0, Prometheus 3.15.0 and Alertmanager 0.34.1 side by side on one shared box, on loopback only, against a small target I could break on purpose. When I could not find a primary source, I write “Not documented”. What I say about licences is my own reading, not legal advice.
The short version
- Inside metrics and outside checks answer different questions. Metrics tell you how the servers feel. An outside check tells you whether a customer can get a correct answer. The Gatus README makes the same point: metrics depend on traffic, so if nothing reaches the application, error rates do not move.
- A check is only as good as its assertion. A status-code check passed happily on a maintenance page in the drill. Checking the body, the response time, the certificate and the DNS answer is where the value is.
- Uptime Kuma 2.5.6 is the friendliest, and it lives in a database. Everything is set up in a web UI, with dozens of check types, more than 90 notification services and public status pages. Configuration sits in SQLite or MariaDB, not in a file you can review.
- Gatus 5.37.0 is checks as code. One YAML file (or a folder) holds endpoints, conditions on status, body, latency, certificate and DNS, alerts and maintenance windows. It rereads the file every 30 seconds, and in the drill a broken edit made it exit, as its README warns.
- The Blackbox Exporter 0.29.0 fits teams that already run Prometheus. It is stateless; Prometheus decides how often to probe, rules decide what is an incident, and Alertmanager decides who hears about it. That is the most flexible path and the slowest to page with common settings: 1 minute 41 seconds in the drill with
for: 1m, against 2.5 to 47 seconds for the other two, depending on thresholds and retries. - Someone must watch the watcher. In the drill, killing any of the three monitors produced no alert from that monitor, by design. Only a second, independent checker or a heartbeat that stops arriving catches it, and in the drill the heartbeat kept arriving for about four minutes after Prometheus died.
Where things stand on 20 October 2026
All times in this section are Indian Standard Time; GitHub gives release stamps in UTC, and I converted them. Star counts, read on 20 October 2026, measure attention and say nothing about quality.
Uptime Kuma
- Latest: 2.5.6 (9 October 2026, 9:16 PM), a small release with a badge fix and dependency updates. 2.5.4 (11 September 2026, 3:39 PM) is the first release that fixes the high-severity badge advisory described under security.
- The 2.x line: 2.0.0 arrived on 20 October 2025 at 6:10 PM, after five betas that started in October 2024. It added MariaDB (external or embedded) beside SQLite, requires Node.js 20.4 or later, removed JSON backup and restore, and changed the default retries for new monitors from 1 to 0. The migration guide warns that converting the heartbeat table can take hours and must not be interrupted.
- The 1.x line: 1.23.17 (20 October 2025, 9:24 PM) was the last 1.x release; the Docker tags
1andlatestare now marked deprecated in the security policy. - Pace and attention: 17 stable releases and 3 betas dated 2026 so far, the 2.5 series alone since 1 August 2026. MIT licence, copyright 2021 Louis Lam. 92,672 stars.
Gatus
- Latest: v5.37.0 (24 September 2026, 5:30 AM). Earlier 2026 releases: v5.34.0 (3 January), v5.35.0 (21 February) and v5.36.0 (20 May).
- Shape of the project: a single Go binary or container image, first published on GitHub on 4 September 2019 in the personal account of its author, TwiN. The releases page carries no binaries; the README offers container images and
go install. - Pace and attention: 4 releases dated 2026; Apache-2.0; 12,362 stars. A hosted service is advertised at gatus.io; this post only covers the open source project.
Blackbox Exporter, Prometheus and Alertmanager
- Blackbox Exporter: v0.29.0 (7 October 2026, 3:10 PM), the only release in 2026. It added a WebSocket prober, a probe timeout metric and CRL checks. v0.28.0 (6 December 2025, 7:02 PM) added HTTP/3, a Unix socket prober and optional automatic config reload, and reworked probe logging.
- Prometheus: 3.15.0 (25 September 2026, 2:26 PM) is the newest minor release. The long-term support line is 3.13, supported until 31 July 2027; its latest patch is 3.13.4 (2 October 2026, 8:16 PM). A new minor release cycle starts every six weeks.
- Alertmanager: v0.34.1 (17 September 2026, 1:42 PM); 11 releases dated 2026.
- Governance and attention: all three are Apache-2.0 under the Prometheus organisation. Prometheus joined the CNCF on 9 May 2016 and graduated on 9 August 2018. Stars: Blackbox Exporter 5,906, Prometheus 66,673, Alertmanager 8,665.
Other options, briefly
- Grafana Cloud Synthetic Monitoring is a hosted service with HTTP, ping, DNS, TCP, traceroute, MultiHTTP, k6 scripted and k6 browser checks run from public probe locations or from private probes. Private probes run the open source Synthetic Monitoring Agent (Apache-2.0). Its docs say scripted and browser checks run at most once every 60 seconds and most other types at most once every 10 seconds.
- Upptime (MIT) runs checks as a scheduled GitHub Actions workflow, “as often as every 5 minutes”, opens GitHub Issues for incidents and publishes a status page on GitHub Pages. It is useful when a five-minute resolution is enough and you want nothing to host.
- OpenStatus (AGPL-3.0) describes itself as a status page, incident and monitoring platform with monitors declared in YAML or Terraform, offered as a managed service or self-hosted. Its README claims checks from 28 regions; that is the vendor’s statement, not something I tested.
First question: what is checked, from where, and who is woken?
A dashboard of green dots answers none of these on its own. Before you compare tools, write down three things for each service.
- What exactly is checked. Does the name resolve to the right address? Does the TLS handshake succeed with a certificate that is not about to expire? Is the HTTP status right? Does the body contain something only the real application would return, such as a JSON field or a product name? Is the answer fast enough to be useful? A check that tests only the status code passes on a parked domain, a default web server page and a CDN error page dressed up as 200.
- From where. A probe on the same VM, the same network or the same cloud account shares the failures you most need to see: the load balancer, the DNS provider, the firewall, the account’s billing status. At least one probe must sit somewhere a customer sits, outside your own network. Two or more locations let you tell “the site is down” from “one probe’s internet is down”.
- Who is woken, and when. How many failures in a row make an incident? How long between checks? Does a notification go out on recovery? What happens during a planned maintenance window? What happens when the target flaps every 30 seconds? And who notices when the monitor itself stops?
So the honest promise for a small team is not “we have uptime monitoring”:
“If our site stops giving customers a correct answer, a checker outside our network notices within two minutes, one person’s phone rings once with a useful message, and if that checker dies, a second one tells us within five minutes.”
If any part of that sentence is untested, that is the part to work on before buying another dashboard. The earlier post on TLS certificate automation closed with a “prove” step, which is this kind of outside check for certificates alone; here the same idea covers the whole path a customer takes.
What each one actually is
Uptime Kuma 2.5.6
Uptime Kuma is a Node.js server with a Vue web interface. You add monitors in the browser, and the browser talks to the server over Socket.IO; there is no configuration file for monitors, and the wiki’s “API Documentation” link leads to the wiki home page, so a documented configuration API is: Not documented. Monitor types include HTTP(S), HTTP keyword, JSON query, TCP port, ping, DNS, push (the monitored job calls Kuma), Docker container, databases (PostgreSQL, MySQL, SQL Server, MongoDB, Redis, Oracle), MQTT, Kafka producer, RabbitMQ, gRPC keyword, SMTP, SNMP, NTP (added in 2.5.0), a real browser check through Chromium, and Globalping, which runs checks from that public probe network. Each monitor has an interval, a retry count, a retry interval, a resend interval for repeat notifications, and a timeout that defaults to 80 percent of the interval.
Data lives in the data directory: SQLite by default, or MariaDB, embedded or external, since 2.0. The install guide warns that the SQLite file needs a filesystem with POSIX locks and that NFS is not supported. The data directory is the backup; the JSON export was removed in 2.0. Status pages, maintenance windows (one-off, recurring or cron-style), certificate expiry notifications, badges, two-factor login and a Prometheus /metrics endpoint are built in. Clustering or high availability is: Not documented.
Gatus 5.37.0
Gatus is a single Go binary with a dashboard. Everything is in YAML: endpoints with a URL, interval and a list of conditions; alert providers; maintenance windows; storage; security. The conditions are the heart of it: [STATUS], [BODY] with JSONPath, [RESPONSE_TIME], [IP], [CONNECTED], [CERTIFICATE_EXPIRATION], [DOMAIN_EXPIRATION] and [DNS_RCODE], with len, has, pat and any helpers. Endpoint types include HTTP, TCP, UDP, SCTP, ICMP, DNS, SSH, STARTTLS, TLS, WebSocket and gRPC, plus “external endpoints” that another process pushes results into, and an alpha “suites” feature for multi-step flows.
Storage is memory by default, so history is lost on restart, or sqlite or postgres. Alerts have a failure-threshold (default 3 failures in a row), a success-threshold (default 2), optional send-on-resolved (default off) and optional reminders. The README lists more than 40 alert providers plus a custom HTTP call. Gatus looks at its configuration file every 30 seconds and reloads it when it has changed; an invalid file makes it exit unless skip-invalid-config-update is set. A concurrency setting (default 3) caps how many checks run at once, so a few slow endpoints can delay others. A connectivity checker can pause all checks when Gatus’s own internet link is down, and a “remote instances” feature that merges another Gatus’s results is marked experimental.
Blackbox Exporter 0.29.0 with Prometheus and Alertmanager
The Blackbox Exporter does nothing on a schedule. Prometheus scrapes /probe?module=...&target=..., the exporter runs one probe right then and returns metrics such as probe_success, probe_duration_seconds, probe_http_status_code, probe_ssl_earliest_cert_expiry and probe_dns_answer_rrs. Modules in its YAML define the prober (HTTP, TCP, ICMP, DNS, gRPC, WebSocket, Unix socket) and what counts as success: status codes, regular expressions on body or headers, CEL expressions on a JSON body (since 0.27.0), TLS settings, DNS answer validation. The probe timeout comes from the Prometheus scrape timeout minus a small margin, limited by the module’s own timeout.
Prometheus stores the results, alerting rules with for and keep_firing_for decide when a failure becomes an alert, and Alertmanager groups, delays, silences, inhibits and routes notifications. Alertmanager’s defaults are group_wait 30 seconds, group_interval 5 minutes and repeat_interval 4 hours. Alertmanager has a built-in cluster mode for high availability; Prometheus high availability means running two identical servers. Status pages are not part of the stack.
Side by side
If a project’s own pages are silent on a point, the bullet says “Not documented”. Anything labelled as a drill result was measured once, on loopback, on the shared test box covered later in the post.
Licence and governance
- Uptime Kuma: MIT, in the personal GitHub account of Louis Lam, who also writes the security policy and triages reports. Funded through sponsors and Open Collective, per the README.
- Gatus: Apache-2.0, in the personal account of its author, TwiN, with outside contributors. A hosted service exists alongside the open source project.
- Blackbox Exporter, Prometheus, Alertmanager: Apache-2.0, in the Prometheus organisation, a CNCF graduated project with a written governance document and several maintainers.
- In practice: all three are free to run. The question that matters more is who fixes the next security bug in your monitor, and how quickly you will install it. My reading, not legal advice.
Architecture and where state lives
- Uptime Kuma: one Node.js process with the web UI, the scheduler and the notifier together. Monitors, history, users and notification secrets live in SQLite (default) or MariaDB. The
datadirectory is the whole state. - Gatus: one Go process with dashboard, scheduler and alerting. Configuration is a file; results go to
memory(default, lost on restart), SQLite or PostgreSQL. Default limits keep 100 results and 50 events per endpoint. - Blackbox Exporter: stateless; it keeps nothing between probes. Prometheus holds the time series, rule files hold the alert logic, Alertmanager holds silences and notification state. Three processes and three configuration files.
What a check can assert
- Uptime Kuma: status code ranges, keyword present (or absent with “upside down” mode), JSON query results, certificate expiry reminders at chosen day counts, DNS record conditions, and many protocol-specific checks. In the 2.5.6 source I found no response-time limit for HTTP monitors; a slow answer counts as up until the timeout.
- Gatus: any combination of status, body (with JSONPath), response time, IP, certificate expiry, domain expiry and DNS response code in one list of conditions, all of which must pass.
- Blackbox Exporter: valid status codes, regular expressions on body and headers, CEL on JSON bodies, TLS version and verification, DNS answer, authority and additional records, and timing metrics per phase (DNS lookup, connect, TLS, processing, transfer). Thresholds on time or certificate age live in PromQL rules, not in the probe.
- Drill: a target that returned HTTP 200 with a “Site under maintenance” page stayed green on every status-only check in all three tools; only the body checks went red.
Alert timing, retries and flapping
- Uptime Kuma: interval (60 seconds by default in the 2.5.6 form), retries (0 by default since 2.0) and a separate retry interval. With 0 retries, the first failed check notifies.
- Gatus: interval (default 60 seconds),
failure-threshold(default 3) andsuccess-threshold(default 2). Worst case to the first alert is roughly interval times threshold. - Prometheus path: scrape interval plus rule evaluation plus the rule’s
forplus Alertmanager’sgroup_wait. Resolved messages wait for the nextgroup_intervaltick, 5 minutes by default. - Flapping: none of the three has a flap detector as such; thresholds, retries,
forandkeep_firing_forare the tools. Drill: a target failing 30 seconds in every minute produced 8 messages in 4 minutes from each Gatus endpoint and each Kuma monitor with 0 retries, none from Kuma with 2 retries, and none from the Prometheus rule withfor: 1m. - Drill, first alert after the target went down: Gatus 2.5 seconds (threshold 1) to 22.3 seconds (default 3); Kuma 6.6 seconds (0 retries) to 46.6 seconds (2 retries); Prometheus 41 seconds without
forand 1 minute 41 seconds withfor: 1m.
Notifications and on-call routing
- Uptime Kuma: “90+ notification services” per the README, set per monitor, with a resend interval for reminders. No grouping or routing tree.
- Gatus: more than 40 providers plus a
customHTTP call with placeholders; per-alert and per-group overrides, and reminders no more often than every 5 minutes. - Alertmanager: many receivers plus a routing tree by labels, grouping, inhibition (a “site down” alert can mute “certificate check failed” for the same site) and silences that expire. The most complete on-call layer of the three.
Maintenance windows and silences
- Uptime Kuma: maintenance objects in the UI, one-off, recurring or cron-style, attached to monitors or status pages; affected monitors show “maintenance” instead of “down”.
- Gatus: global or per-endpoint windows with a start time, duration, time zone and weekdays. The time zone defaults to UTC, so an IST team must set
Asia/Kolkata. Ad hoc maintenance means a config change. - Alertmanager: silences by label matchers, from the UI or
amtool, with an end time, plustime_intervalsfor recurring mute periods on routes.
Status pages
- Uptime Kuma: multiple public status pages, each with chosen monitors, custom domain mapping, incident messages, and badges.
- Gatus: the dashboard is the status page, with groups, announcements and badges; anything you do not want public must be hidden or protected.
- Blackbox Exporter path: none. Grafana dashboards or a separate status page product.
Multi-location checks and high availability
- Uptime Kuma: one instance checks from where it runs; the Globalping monitor type borrows that public probe network. Clustering: Not documented, so two instances means configuring both by hand.
- Gatus: run one instance per location with the same YAML from Git. External endpoints let other agents push results in; “remote instances” for a combined view is experimental.
- Prometheus path: run Blackbox Exporters in several places and label results by location; alert when, say, two of three locations fail. For HA, run two identical Prometheus servers and an Alertmanager cluster, which is on by default and deduplicates notifications.
Configuration as code or UI
- Uptime Kuma: UI first. I scripted the drill set-up with the Socket.IO events the UI sends, which is not a documented interface.
- Gatus: code first. One file or a directory of YAML files merged together, environment variables expanded, reload on change. Drill: an invalid edit made the running process exit, as the README says it will by default; with
skip-invalid-config-update: truethe process stayed up but stopped checking. - Prometheus path: code first. Scrape configs, file-based service discovery, rule files and Alertmanager routes are all YAML, with
promtoolandamtoolto check them before reload. Drill: invalid configs sent to the reload endpoints were refused, and the old configs kept running.
Resource use
- Uptime Kuma: the heaviest of the three in the drill, about 172 to 176 MiB of memory, and 2.5 percent of one core with 57 HTTP monitors at 20-second intervals.
- Gatus: about 42 MiB and under 0.5 percent of one core with 58 endpoints.
- Blackbox path: the exporter about 35 MiB and 0.45 percent with 50 extra targets, plus Prometheus (87 to 96 MiB here, growing with the number of series and the retention you keep) and Alertmanager (about 35 MiB). All of these are small next to the applications they watch; disk for Prometheus history is the item to plan.
Release rhythm and upgrade risk
- Uptime Kuma: frequent point releases (17 stable in 2026), several with security fixes. The 1.x to 2.x migration rewrites the heartbeat data and must not be interrupted; take a copy of the
datadirectory first. - Gatus: a minor release every one to four months (4 in 2026), within the v5 major version since 2022.
- Blackbox Exporter: about one or two releases a year, with occasional breaking changes in logging and flags (0.28.0). Prometheus offers an LTS line (3.13 until 31 July 2027) for teams that want fewer upgrades.
Worked example for the Udaipur scenario
The Udaipur team’s problem was not a missing tool. It had a check that asked the wrong question from the wrong place, and nothing watching the thing that sends alerts.
Step 1: list what a customer actually does. Open the booking page and see hotels; search dates through the JSON API; a hotel owner logs in to the partner portal; the payment gateway calls back. Each one gets a name, a URL, an owner and an expected answer. The payment callback is a POST from the gateway, so the outside check covers only what a GET can prove (the route answers and the certificate is good); the rest comes from application metrics on callback counts.
Step 2: write the assertion, not just the URL. The booking page must return 200, contain the company name and a known hotel, and answer within two seconds. The search API must return JSON with a non-empty hotels array. The certificate must have more than 21 days left. The booking domain must resolve to the load balancer’s address through a public resolver.
Step 3: put the checker where the customers are. A small VM at a different cloud provider runs Gatus 5.37.0 from a Git repository, with SQLite storage on a local disk. Nothing on it depends on the main provider: not DNS, not the account, not the network.
Step 4: decide who is woken, and when. Checks every 30 seconds, failure-threshold: 2, success-threshold: 2, recovery messages on. The worst case to the first page is about 60 seconds plus the request timeout, which the team accepts. Certificate and DNS failures go to the team chat during working hours; booking page and search failures page the on-call phone.
Step 5: declare maintenance, in IST. The Sunday 6:00 to 6:30 AM deployment slot is a maintenance window with timezone: Asia/Kolkata; everything else pages.
# gatus/config.yaml (Udaipur, invented)
storage: {type: sqlite, path: /var/lib/gatus/data.db}
metrics: true
security:
basic: {username: ops, password-bcrypt-base64: "${GATUS_ADMIN_HASH}"}
alerting:
pagerduty: {integration-key: "${PD_KEY}"}
telegram: {token: "${TG_TOKEN}", id: "${TG_CHAT}"}
maintenance: {start: "06:00", duration: 30m, timezone: "Asia/Kolkata", every: [Sunday]}
endpoints:
- name: booking-page
url: "https://book.example.in/"
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY] == pat(*Lake View Haveli*)"
- "[RESPONSE_TIME] < 2000"
- "[CERTIFICATE_EXPIRATION] > 504h"
alerts:
- {type: pagerduty, failure-threshold: 2, success-threshold: 2, send-on-resolved: true}
- name: search-api
url: "https://book.example.in/api/search?city=udaipur&probe=1"
interval: 30s
conditions: ["[STATUS] == 200", "len([BODY].hotels) > 0"]
alerts: [{type: pagerduty, failure-threshold: 2, send-on-resolved: true}]
- name: booking-dns
url: "1.1.1.1"
interval: 2m
dns: {query-name: "book.example.in", query-type: "A"}
conditions: ["[DNS_RCODE] == NOERROR", "[BODY] == 203.0.113.10"]
alerts: [{type: telegram, failure-threshold: 2}]
- name: prometheus-alive
url: "https://prom.internal.example.in/-/healthy"
interval: 1m
conditions: ["[STATUS] == 200"]
alerts: [{type: telegram, failure-threshold: 3}]
# prometheus rules on the main side: the other half of "watch the watcher"
- alert: OutsideCheckerDown
expr: up{job="gatus"} == 0
for: 2m
- alert: Watchdog # always firing; routed to a dead man's switch elsewhere
expr: vector(1)
Step 6: watch the watcher, both ways. Prometheus scrapes the Gatus /metrics endpoint over a private link and pages if it disappears for two minutes. Gatus checks that Prometheus and Alertmanager answer. Alertmanager sends an always-firing Watchdog alert every few minutes to a heartbeat service at a third place, which counts only firing messages and pages if they stop for ten minutes; Alertmanager keeps an alert alive for a few minutes after Prometheus stops sending it, so a tighter limit only adds false pages. Every arrow now has a second arrow coming back.
Step 7: practise it. Once a month, in a quiet hour, the team serves a maintenance page on a staging copy of the booking domain, points a test name at a wrong address, and stops the Gatus VM, and writes down how many seconds each page took. The internal Blackbox Exporter, already on the Prometheus VM, adds certificate expiry metrics and probes for internal services that the outside checker cannot reach.
Security and hygiene
A monitoring tool is a machine that makes requests to any address you type in, holds tokens for your chat and paging tools, and often publishes a status page to the internet. Here is what the advisory records show, read on 20 October 2026.
Uptime Kuma. The repository lists 19 published advisories since February 2023, so it is worth reading the recent ones.
- GHSA-wf2j-5mc7-5c4w (9 October 2026, 8:00 AM; high, CVSS 7.5; no CVE yet): an unauthenticated request to the ping badge endpoint with arbitrary monitor IDs could exhaust resources. Affects 2.5.3 and earlier; fixed in 2.5.4.
- CVE-2026-71285 (NVD published 5 August 2026, 6:54 PM; CVSS 8.1 high from the reporting CNA; NVD status “Deferred”): the Matomo analytics setting is placed unquoted into a script on public status pages, so a value saved by an admin or editor runs as JavaScript for every visitor. The record names no fixed version, and I found no matching repository advisory; if you do not use Matomo on status pages, leave it empty.
- GHSA-v832-4r73-wx5j, CVE-2026-33130 (16 March 2026, 11:16 AM; medium, 6.5): server-side template injection in notification templates allowed reading files on the server, because the earlier fix (GHSA-vffh-c9pq-4crh, 20 October 2025, 8:03 PM) was incomplete. Affects 1.23.0 through 2.2.0; fixed in 2.2.1.
- GHSA-c7hf-c5p5-5g6h, CVE-2026-32230 (11 March 2026, 12:22 PM; medium, 5.3): the ping badge endpoint skipped the “public” check and leaked response times of private monitors. 2.0.0 to 2.1.3; fixed in 2.2.0.
- GHSA-qjxc-h5jf-c7rj (13 October 2025, 6:49 PM; medium): a logged-in user can add a keyword monitor pointed at a cloud metadata endpoint and read the answer. The maintainer classifies it as a platform issue and will not fix it in Uptime Kuma, and the security policy says SSRF reports are not accepted. In plain words: anyone who can add a monitor can make the server fetch internal URLs.
Gatus. No repository security advisories and no OSV entries for the Go module. NVD has CVE-2026-11956 (11 June 2026, 6:46 PM; scored by VulDB as CVSS 3.1 3.7 low and CVSS 4.0 6.3 medium; NVD status “Deferred”): the OIDC session cookie in 5.36.0 lacks the Secure attribute. The linked GitHub issue was closed as “not planned”. Serve the dashboard only over HTTPS if you use OIDC.
Blackbox Exporter. No repository advisories. NVD has CVE-2023-26735 (26 April 2023, 5:45 AM; NVD 7.5 high), which says the /probe interface of v0.23.0 lets anyone who can reach it scan internal ports and fetch resources; the record itself notes the claim is disputed because authentication can be configured. The lesson is the same as for Uptime Kuma: an open /probe endpoint is a request-forwarding service for whoever finds it.
Prometheus and Alertmanager. Alertmanager GHSA-v86x-5fm3-5p7j, CVE-2023-40577 (23 August 2023, 8:56 PM; high, 7.5): stored XSS in the UI, fixed in 0.25.1. Prometheus published four advisories in April 2026, among them CVE-2026-42151 (27 April 2026, 8:21 PM; high), a remote-write OAuth secret visible through the config API, fixed in 3.5.3 and 3.11.3; 3.13 LTS and 3.15 include all the fixes.
Habits that matter more than the version number
- Put the monitor’s web interface behind a login and a private network or VPN, and publish only the status page. In Gatus, turn on basic auth or OIDC; for the Blackbox Exporter, use
--web.config.filefor TLS and basic auth and never expose/probeto the internet. - Treat “who can add a monitor” as “who can make this server fetch any URL”. Block the cloud metadata address (169.254.169.254) from the monitor host with a firewall rule, and keep monitor editors to a small group.
- Keep alert tokens (Slack, Telegram, PagerDuty and SMTP passwords) out of Git: Gatus expands
$VARIABLESin its YAML, so pass secrets as environment variables. For Uptime Kuma, the tokens live in the database, so the backup of thedatadirectory is a secret too. - Do not let probes ignore TLS errors by default.
ignoreTls,insecure: trueandinsecure_skip_verifyare for self-signed internal targets, not for your public site. - Subscribe to release notes; Uptime Kuma ships security fixes in point releases, and its Docker tags
1andlatestare deprecated. - Back up the monitor’s own state and configuration: the Kuma
datadirectory, the Gatus YAML in Git and its SQLite or PostgreSQL store, and the Prometheus rules and Alertmanager routes in Git.
Decision guide
Lean towards Uptime Kuma if
- The people who will add and change checks prefer a web form over a YAML file, and you want public status pages, maintenance windows and dozens of notification channels without building anything.
- You need check types the others lack out of the box, such as database logins, MQTT, SNMP, Docker containers or a real browser.
- You can keep one instance updated every few weeks, back up its
datadirectory, and accept that there is no documented HA mode and no configuration in Git.
Lean towards Gatus if
- You want checks reviewed in pull requests like any other code, with assertions on body, latency, certificate expiry and DNS in one line each.
- One small binary per location is enough, and you are fine with the dashboard as your status page.
- You will run it with
sqliteorpostgresstorage (not the defaultmemory) and validate every config change before it reaches the running instance.
Lean towards the Blackbox Exporter if
- Prometheus and Alertmanager already run well in your team, and on-call routing, silences and inhibition already live in Alertmanager.
- You want probe results as metrics next to everything else, with PromQL for SLOs and long-term storage through the same pipeline.
- You can tune the delay: scrape interval, the rule’s
for, andgroup_waitadd up, and the defaults favour fewer notifications over speed.
Look at hosted or other options if
- You need many probe locations without running servers: Grafana Cloud Synthetic Monitoring or a similar hosted service.
- Five-minute resolution is enough and you want no server at all: Upptime.
- You want status page, incidents and monitors as code in one product: OpenStatus, keeping its AGPL-3.0 licence in mind.
Udaipur (invented) pick. Gatus 5.37.0 on a small VM at a different cloud provider, checks in Git, SQLite storage, a body check on the booking page, a JSON check on the partner portal API, a certificate expiry condition and a DNS answer condition, paging after two failures at 30-second intervals. The Blackbox Exporter on the existing Prometheus for internal probes and certificate metrics, with Alertmanager routing to the same paging tool. Each side watches the other: Gatus checks Prometheus and Alertmanager health, and Prometheus scrapes Gatus and alerts when it disappears, with a Watchdog heartbeat to a dead man’s switch. Uptime Kuma is a fair alternative to Gatus if the team would rather click than commit.
Checklist
- Every customer-facing name has an outside check that asserts on the body, not only on the status code.
- At least one checker runs outside your own cloud account and network, and preferably two in different places.
- Certificate expiry and DNS answers are checked from outside, with thresholds in days and an expected address.
- Each alert has a written failure threshold and interval, and you know the worst-case delay to the first page.
- Recovery notifications are on, and a flapping target produces a small, bounded number of messages.
- Planned maintenance is declared in the tool (maintenance window or silence), not handled by muting the channel.
- The monitor is watched by something else, and that path has been tested by stopping the monitor.
- Monitor configuration is backed up or in Git, and a restore or rebuild has been tried once.
- The monitor UI and probe endpoints are not open to the internet; only status pages are public.
- Someone reads the release notes and advisories of the monitor itself every month.
Common mistakes
- Checking
/healthfrom the same VM or network as the application and calling it uptime monitoring. - Accepting any 200 as success, so a parked domain, a default welcome page or a maintenance page passes.
- Running Gatus with the default
memorystorage and losing all history and incident state on every restart. - Leaving Uptime Kuma’s new-monitor default of 0 retries and getting paged for every single dropped request, or raising retries so far that a real outage pages minutes late.
- Copying Prometheus examples without adding up scrape interval,
for, evaluation interval andgroup_wait, then wondering why a page arrives after two minutes. - Muting the alert channel during maintenance and forgetting to unmute it.
- Editing the Gatus YAML on the server directly, without a syntax check, and finding the monitor gone.
- Exposing the Blackbox Exporter
/probeendpoint or the Kuma dashboard to the internet, or settingignoreTlsorinsecure_skip_verifyon public sites, which hides exactly the certificate problems you want to see. - Writing a “less than” status condition such as
[STATUS] < 400in Gatus: a refused connection has status 0 and passes. Add[CONNECTED] == trueor use an exact range. - Having no answer to “what tells us the monitor is down?”, or a dead man’s switch that counts a “resolved” Watchdog message as a heartbeat.
Drill on this box, 20 October 2026
Setting: a shared Debian 13 virtual machine with 8 vCPUs and 15 GiB of memory, between about 10:18 PM and 11:02 PM IST, on 127.0.0.1 and unprivileged ports only. Every number is a single run on loopback, with no network delay: the shape of each tool’s behaviour, not a benchmark.
Install. No container runtime was available. Uptime Kuma 2.5.6 ran from its Git tag on Node.js 20.19.2 with SQLite; Gatus 5.37.0 was built with go install (Go 1.27.2), since its releases carry no binaries; Blackbox Exporter 0.29.0, Prometheus 3.15.0 and Alertmanager 0.34.1 were release binaries. A small Python program was the victim: an HTTP target I could switch between healthy, HTTP 503, a 3-second delay, a 200 “Site under maintenance” page and a 30-second flapping cycle; an HTTPS copy with a self-signed certificate valid for 2 days; a DNS server for shop.example.test; and a webhook receiver that logged when each notification arrived.
Settings. Uptime Kuma: 20-second interval, 16-second timeout, 0 retries (the 2.x default) or 2 retries 20 seconds apart. Gatus: 10-second interval (30 seconds for TLS), failure-threshold of 1, 2 or the default 3, send-on-resolved on. Prometheus: 15-second scrape and evaluation; rules with for: 1m, with for: 30s and with no for; Alertmanager on its defaults (group_wait 30 seconds, group_interval 5 minutes). The core pieces:
GATUS_CONFIG_PATH=gatus/config.yaml gatus # web 127.0.0.1:18680, storage sqlite
- name: g-body conditions: ["[STATUS] == 200", "[BODY].status == UP"]
- name: g-dns url: "127.0.0.1:18653" conditions: ["[DNS_RCODE] == NOERROR", "[BODY] == 10.20.30.40"]
UPTIME_KUMA_HOST=127.0.0.1 UPTIME_KUMA_PORT=18631 UPTIME_KUMA_DB_TYPE=sqlite node server/server.js
node kuma-setup.js add # same Socket.IO events the web UI sends: setup, login, addNotification, add
blackbox_exporter --config.file=prom/blackbox.yml --web.listen-address=127.0.0.1:19115
http_body: fail_if_body_not_matches_regexp: ['"status":"UP"']
prometheus --config.file=prom/prometheus.yml --web.listen-address=127.0.0.1:19090 --web.enable-lifecycle
- alert: ProbeDown expr: probe_success == 0 for: 1m
alertmanager --config.file=prom/alertmanager.yml --cluster.listen-address= # webhook to 127.0.0.1:18690
curl 127.0.0.1:18600/mode/down # then slow/3, wrongbody, dns/10.99.99.99, flap/30, up
Target down (HTTP 503 from 10:24:08 PM). Time to the first notification: Gatus with a threshold of 1, 2.5 seconds; Uptime Kuma with 0 retries, 6.6 seconds; Gatus with a threshold of 2, 13.0 seconds; Gatus with the default threshold of 3, 22.3 seconds; the Prometheus rule without for, 41.0 seconds; Uptime Kuma with 2 retries, 46.6 seconds; the Prometheus rule with for: 1m, 1 minute 41 seconds. The Kuma and Gatus numbers depend partly on where the target failed within their check cycles; the Prometheus numbers are mostly the sum of scrape, evaluation, for and group_wait.
Recovery (from 10:27:08 PM). Gatus with a threshold of 1 sent “resolved” after 2.5 seconds and Kuma after 6.5 seconds; Gatus with the default success-threshold of 2 after 12.3 seconds. Alertmanager sent its resolved messages at 10:29:49 and 10:30:49 PM, 2 minutes 41 seconds and 3 minutes 41 seconds after the target was healthy, on the next 5-minute group_interval tick of each group.
Slow answers (3-second delay from 10:28:38 PM). Gatus’s [RESPONSE_TIME] < 1000 condition notified after 18.3 seconds; the Prometheus rule probe_duration_seconds > 1 with for: 30s after 71 seconds. Uptime Kuma sent nothing, as expected: 3 seconds is inside its 16-second timeout, and it has no response-time limit for HTTP monitors.
Wrong body (HTTP 200 maintenance page from 10:31:53 PM). Every status-only check in all three tools stayed green. The Kuma keyword monitor notified after 1.6 seconds (“200 - OK, but keyword is not in [Site under maintenance]”), the Gatus body condition after 17.7 seconds, and the Blackbox body regex with for: 1m after 1 minute 41 seconds.
Wrong DNS answer (from 10:35:08 PM). Kuma’s DNS monitor with a record condition notified after 6.6 seconds (“Records: 10.99.99.99”), Gatus’s [BODY] == condition after 13.3 seconds, and the Blackbox DNS module with validate_answer_rrs and for: 30s after 1 minute 26 seconds.
Certificate with 2 days left. Gatus’s [CERTIFICATE_EXPIRATION] > 168h alerted on its first check, about a second after start. The Prometheus rule probe_ssl_earliest_cert_expiry - time() < 7 * 86400 fired 44 seconds after start. Uptime Kuma sent nothing for the monitor with “ignore TLS errors” on, which a self-signed certificate needs; the 2.5.6 source skips expiry notifications when TLS errors are ignored. With verification on, that monitor simply went down with “self-signed certificate”. Kuma’s 7, 14 and 21-day reminders for a certificate from a real CA were not tested.
Flapping (30 seconds up, 30 seconds down, 10:38:08 to 10:42:08 PM). Each Gatus endpoint, whatever its threshold, sent 8 messages (4 triggered, 4 resolved), and so did each Kuma monitor with 0 retries. Kuma with 2 retries sent none. The Prometheus rule without for sent one firing message; the rule with for: 1m never fired.
Resource use, 60-second windows, RSS and CPU as a percentage of one core. With the small set of checks: Uptime Kuma 171.6 MiB and 0.63 percent; Gatus 42.9 MiB and 0.15 percent; Blackbox Exporter 35.4 MiB and 0.10 percent; Prometheus 87.3 MiB and 0.23 percent; Alertmanager 35.3 MiB and 0.02 percent. After adding 50 more HTTP checks every 20 seconds to each: Kuma 176.0 MiB and 2.47 percent; Gatus 42.3 MiB and 0.48 percent; Blackbox Exporter 34.6 MiB and 0.45 percent; Prometheus 95.5 MiB and 0.37 percent. After about 40 minutes, the Kuma SQLite file was 0.75 MB and the Gatus one 0.29 MB, each with a WAL file of about 4 MB.
Configuration changes. The 50 new Kuma monitors were probing within 1.3 seconds of being added; the 50 new Blackbox targets through file-based discovery within 2.1 seconds; the 50 new Gatus endpoints 16.1 seconds after the file was saved, because Gatus looks at the file every 30 seconds. An invalid Blackbox config sent to /-/reload returned HTTP 500 (“prober ‘htp’ is not valid”) and probing carried on with the old config; a broken Prometheus rule file was refused the same way. Gatus on its defaults behaved as its README says: 14 seconds after I saved invalid YAML, it logged “Configuration file has been modified” and exited with a panic. A second Gatus with skip-invalid-config-update: true stayed alive and logged “The old configuration will continue being used”, but for the 19 seconds I watched it made no checks and its API did not answer. Reading main.go for 5.37.0, the running checks are stopped before the new file is validated, and this path does not start them again.
Killing the monitors. Gatus, killed at 10:47:34 PM: the Kuma monitor watching it notified after 0.8 seconds (its next check happened to be due), and Prometheus’s up{job="gatus"} == 0 rule with for: 30s after 75 seconds. Gatus came back 0.2 seconds after restart and reloaded one triggered alert from SQLite. Uptime Kuma, killed at 10:50:44 PM: my own Gatus watcher never alerted, because I had written [STATUS] < 400, and a refused connection has status 0, which is less than 400; the log showed success=true; errors=1 every 10 seconds. The Blackbox probe of Kuma’s page with for: 1m caught it after 1 minute 50 seconds. Prometheus, killed at 10:53:56 PM: Gatus noticed after 19.6 seconds. The Watchdog heartbeat, configured to repeat every minute but arriving every 2 minutes, kept arriving at 10:55:04 and 10:57:04 PM, because Alertmanager keeps an alert alive until its end time, and then came as a “resolved” message at 10:58:04 PM. A dead man’s switch that treats any message as a heartbeat would never have noticed.
Clean-up. All drill processes were stopped by about 11:02 PM and the binaries, Node modules and databases (about 860 MB) deleted; the local web server on port 8766 was not touched.
What this box could not test: real probe locations across the internet; real notification providers (Slack, Telegram, PagerDuty, SMS) and their delays; ICMP, which needs extra privileges; Uptime Kuma with MariaDB, status pages, maintenance windows, real-browser and Globalping checks, or a 1.x to 2.x migration; Gatus with PostgreSQL, OIDC, external endpoints, suites or remote instances; an Alertmanager cluster or paired Prometheus servers; memory growth over days; and the hosted or other options, none of which I ran.
Unlearn
- Unlearn that a green internal dashboard means customers can use the site.
- Unlearn that HTTP 200 means the right page was served.
- Unlearn that a faster alert is always a better alert; without a threshold, it is just more noise.
- Unlearn that a monitor will tell you when it stops; silence from a dead monitor looks exactly like a quiet night.
- Unlearn that monitoring configuration is something you click once and forget.
Re-learn
- Re-learn uptime checks as small tests with assertions: name, certificate, status, body, time.
- Re-learn alert delay as a sum you can calculate: interval times threshold, plus rule and grouping delays.
- Re-learn the outside view: at least one probe where customers are, not where servers are.
- Re-learn heartbeats and dead man’s switches as the way to watch the watcher.
- Re-learn to practise outages on purpose, in a drill, before a customer does it for you.
Closing
An outside check is a very small piece of software, and it is still easy to get wrong. It must ask the question a customer would ask, from somewhere a customer would be, and it must wake one person at the right moment without crying wolf. Uptime Kuma 2.5.6 makes that easy to set up from a browser, with the most check types and notification channels, at the price of state in a database and a steady stream of security fixes to apply. Gatus 5.37.0 puts the same checks in a reviewed YAML file with precise conditions, and needs care with storage and config edits. The Blackbox Exporter 0.29.0 turns probes into Prometheus metrics, so routing, silences and history come from tools you may already run, at the cost of more moving parts and a slower default path to a page. Whichever you pick, give it a partner that notices when it stops. If Prometheus is part of that picture, the post on Prometheus long-term storage with Thanos, Mimir and VictoriaMetrics covers keeping probe history beyond a few weeks, and the post on self-hosted error tracking with Sentry, GlitchTip and Bugsink covers the inside view of what broke once the outside check has told you that something did. For certificates alone, the TLS certificate automation post goes deeper. Learn what each check really proves. Unlearn the status-code-only probe. Re-learn alert delay as arithmetic. Practise stopping your own monitor. Apply it before a customer tells you the site is down.
Sources
Uptime Kuma
- louislam/uptime-kuma on GitHub
- Uptime Kuma 2.5.6 release
- Uptime Kuma 2.5.0 release
- Uptime Kuma 2.0.0 release
- Uptime Kuma wiki: migration from v1 to v2
- Uptime Kuma wiki: how to install
- Uptime Kuma wiki: environment variables
- Uptime Kuma wiki: Prometheus integration
- Uptime Kuma security policy
- Uptime Kuma licence file
Gatus
- TwiN/gatus on GitHub (README with full configuration reference)
- Gatus v5.37.0 release
- Gatus licence file
- Gatus security page
Blackbox Exporter, Prometheus and Alertmanager
- prometheus/blackbox_exporter on GitHub
- Blackbox Exporter configuration reference
- Blackbox Exporter changelog
- Blackbox Exporter v0.29.0 release
- Prometheus 3.15.0 release
- Prometheus: release cycle and long-term support
- Prometheus: alerting rules
- Alertmanager: configuration
- Alertmanager: overview
- Alertmanager README: high availability
- Alertmanager v0.34.1 release
- Prometheus governance
- CNCF: Prometheus project page
- Prometheus Operator runbooks: Watchdog
Other options
- Grafana Cloud Synthetic Monitoring documentation
- Grafana Synthetic Monitoring: check types
- Grafana Synthetic Monitoring: private probes
- grafana/synthetic-monitoring-agent on GitHub
- upptime/upptime on GitHub
- openstatusHQ/openstatus on GitHub
Security records
- Uptime Kuma security advisories
- GHSA-wf2j-5mc7-5c4w (Uptime Kuma badge endpoint DoS)
- NVD: CVE-2026-71285 (Uptime Kuma Matomo setting)
- GHSA-v832-4r73-wx5j (Uptime Kuma template injection)
- GHSA-vffh-c9pq-4crh (Uptime Kuma template injection, first fix)
- GHSA-c7hf-c5p5-5g6h (Uptime Kuma ping badge)
- GHSA-qjxc-h5jf-c7rj (Uptime Kuma cloud metadata via monitors)
- NVD: CVE-2026-11956 (Gatus OIDC cookie)
- NVD: CVE-2023-26735 (Blackbox Exporter probe interface, disputed)
- GHSA-v86x-5fm3-5p7j (Alertmanager stored XSS)
- NVD: CVE-2026-42151 (Prometheus config API secret)
- Prometheus security advisories
Internal
Releted Posts
Self-hosted Git forges in 2026: revisit Gitea, Forgejo and GitLab CE before you move another repository
Picture a software company in Kochi that builds billing and export-documentation software for seafood exporters in Aroor, Alappuzha, Kollam and Thoothukudi (an invented example, not a real client).
Read moreLoad testing in 2026: revisit Grafana k6, Locust and Gatling before you run another performance test
Picture a company in Lucknow that hosts exam results and admit cards for several state boards and universities across Uttar Pradesh and Bihar (an invented example, not a real client).
Read moreContainer registries in 2026: revisit Harbor, Zot and CNCF Distribution before you push another image
Picture a product engineering team in Bengaluru that runs about forty microservices on Kubernetes for a B2B payments product used by NBFCs across Karnataka and Maharashtra (an invented example, not a real client).
Read more