How to monitor Uptime Kuma from outside
Updated Aug 19, 2026
Uptime Kuma watches everything in your homelab except itself. If the container dies, the host loses power, or your ISP drops, Kuma goes quiet and nothing tells you. Here are two recipes that close the gap, including one that works with zero exposed ports.
Why a Kuma monitor pointed at Kuma does not work
The first instinct is to add a monitor inside Kuma targeting its own URL. That check runs on the same machine, in the same container stack, behind the same network. When Kuma stops, the check stops with it, and a check that is not running cannot fail. You need an observer that survives the failure you are trying to detect, which means it has to live somewhere else. There are two ways to arrange that, and they catch different things.
| Direction | How it works | Needs a port open? |
|---|---|---|
| Pull (active check) | An outside service requests something Kuma serves and asserts on the response | Yes, Kuma must be reachable from the internet |
| Push (heartbeat) | The Kuma box calls an external URL on a schedule; silence is the alert | No, outbound HTTPS only |
Direction 1: pull, an active check against Kuma
This applies only if Kuma is already published through a reverse proxy, a port forward, or a tunnel. If it is LAN-only, skip to the push recipe.
Do not point the check at Kuma's login page: a login page renders fine from a stale cache, a proxy error page, and a half-broken instance whose backend is down. Point it at a public status page and assert on text that appears only when Kuma is serving live data. In Undownable, that is a keyword monitor:
Type: Keyword
URL: https://kuma.example.com/status/homelab
Keyword: Operational
Interval: 60 seconds (Pro) or 300 seconds (Free)
Pick the keyword from your own status page: a group name, a monitor name, or your status banner wording all work, as long as the string is absent from your reverse proxy's 502 page. Verify by stopping the container and loading the URL yourself. Whatever you see then must not contain the keyword.
Kuma also exposes an unauthenticated JSON endpoint behind each published status page, returning recent heartbeats and rolling uptime per monitor:
curl -s https://kuma.example.com/api/status-page/heartbeat/homelab | head -c 400
You can hang a JSON check off that with dot-notation to assert on a specific monitor, but Uptime Kuma's own wiki labels these routes as internal and unsupported for third parties, so they can change between releases. The keyword check is the more durable choice. While you are here, add an SSL check on the same hostname: the certificate fronting Kuma usually fronts everything else you self-host.
Direction 2: push, a heartbeat with nothing exposed
This is the recipe most homelabs need, because most homelabs never publish Kuma at all. A heartbeat monitor in Undownable gives you a private ingest URL, https://undownable.com/ping/{id}, which accepts GET or POST. Every ping is a successful check. When pings stop for longer than the expected interval plus the grace period, the monitor goes down and your alert channels fire. The simplest version is a crontab line on the Kuma host:
# crontab -e on the machine running Uptime Kuma
*/5 * * * * /usr/bin/curl -fsS -m 10 --retry 3 https://undownable.com/ping/YOUR-MONITOR-ID > /dev/null
Two details matter. Use the absolute path to curl: cron runs with a minimal PATH, usually just /usr/bin and /bin, so a bare curl can work interactively and fail silently from cron. And never leave an unescaped percent sign in a crontab command; cron turns the first % into a newline and feeds the rest to the command as standard input, so a timestamped variant must be written as date +\%s.
That line proves the host is up with working outbound internet. It does not prove Kuma is running, which is the more interesting question. Make the ping conditional on Kuma answering over loopback:
# Only ping when the local Kuma is serving. Adjust the port to yours.
*/5 * * * * /usr/bin/curl -fsS -m 10 http://127.0.0.1:3001/ > /dev/null && /usr/bin/curl -fsS -m 10 --retry 3 https://undownable.com/ping/YOUR-MONITOR-ID > /dev/null
Now the heartbeat stops for a dead Kuma process as well as a dead host.
The no-cron variant: let Kuma send the heartbeat
You can skip cron entirely. Add a plain HTTP monitor inside Uptime Kuma whose URL is your heartbeat URL. Kuma requests it on its own heartbeat interval, which is exactly the ping you wanted, and the arrangement cancels itself in the right way: if Kuma stops running, it stops requesting, and the heartbeat goes silent.
In Uptime Kuma:
Monitor Type: HTTP(s)
Friendly Name: Undownable backstop ping
URL: https://undownable.com/ping/YOUR-MONITOR-ID
Heartbeat Interval: 60 seconds
In Undownable, the matching heartbeat monitor:
Expected ping interval: 300 seconds
Grace period: 120 seconds
Set the Undownable side looser than the Kuma side. With Kuma sending every minute against a five-minute expectation plus two minutes of grace, a run of half a dozen dropped requests passes without a word, which is the right slack for a home connection, while a genuinely stopped Kuma still surfaces in minutes.
One caveat: Kuma's notification providers, including the generic Webhook one, fire only on state changes and other important events, never on every heartbeat. A webhook notification pointed at a heartbeat URL will not keep a dead man's switch alive; it sends one request the next time a monitor flips. Useful for mirroring alerts into a second channel, but not a liveness signal.
What each direction actually catches
- Kuma process dead, host fine: the pull check and the conditional push recipe. A bare unconditional cron ping misses this, because the host is still happily curling.
- Host dead, LAN down, or ISP outage: both directions. That last case is where a second Kuma on the same site would also have gone quiet.
- Reverse proxy or certificate broken, Kuma fine: the pull check only. The box still has outbound internet, so heartbeats keep flowing.
- Kuma up but its notifications broken: neither, directly, which is why the backstop must alert over a channel Kuma does not share.
The last two are the argument for running both. Pull tells you the outside world can still reach you; push tells you the machine is alive. They fail independently.
A sane starting configuration
- One heartbeat monitor, pinged every minute by a Kuma HTTP monitor or every five minutes by cron, with the expected interval looser than the send rate.
- One keyword check against your public status page, if you publish one, plus an SSL check on that hostname.
- Alerts routed to a channel Kuma does not use. If Kuma notifies over your self-hosted ntfy, have the backstop use email or Telegram, so one dead container cannot silence both.
- A deliberate test: stop the Kuma container, wait out the interval plus grace, and confirm the alert lands on your phone.
Three monitors, about ten minutes, comfortably inside a free plan of ten monitors with five-minute checks and every alert channel included.
Can Uptime Kuma monitor itself?
Only in the narrow case where the web process hangs while the rest of the host keeps running, and even then the notification has to escape a partly broken machine. It cannot report a stopped container, a dead host, or a network outage: the check dies at the moment the thing it checks does.
Do I have to expose Uptime Kuma to the internet to monitor it externally?
No. The push direction needs only outbound HTTPS: the Kuma box calls a heartbeat URL on a schedule and the external service alerts when the calls stop. Nothing listens on a public port and no inbound firewall rule is required.
What is the Uptime Kuma push URL, and how is it different from this?
Kuma's own push monitors accept check-ins at /api/push/{token} with optional status, msg, and ping parameters, which is how a script reports into Kuma. This guide is the mirror image: Kuma reports out, so something outside notices when Kuma stops reporting.
How long until I get alerted after Kuma dies?
The expected ping interval plus the grace period sets when the ping counts as overdue, and the check that notices runs on the same cadence, so a five-minute interval with sixty seconds of grace alerts roughly six to eleven minutes after the last successful ping. Leave enough slack that one skipped cron run does not page you.
Should the backstop use the same notification channel as Kuma?
No. A shared channel means one broken credential or one dead notification container silences both your monitoring and the thing watching it. Prefer a path that does not route through hardware in your own rack.
Related reading
Monitoring that watches from the outside
Free plan with 10 monitors, plus a 14-day Pro trial. No credit card required.
Start free