What it is
Uptime Kuma is a self-hosted uptime monitor. Each "monitor" is a periodic check — HTTP GET, ICMP ping, TCP port, DNS — against a target, and Uptime Kuma records the response over time. The dashboard shows green dots and uptime percentages; the status page exposes the same information to external observers.
Why I run it
I needed a binary "is it up?" signal across every service in the cluster, separate from Portainer (which tells me about containers) and Grafana (which tells me about metrics). Uptime Kuma is the right level of abstraction — it checks the actual service from the outside, the way a user would.
The other thing it gives me is a status page. The internal Homepage dashboard reads from a status-page slug, which means its "X services online / Y total" widget is fed by Uptime Kuma, not by some bespoke check. Two birds, one Kuma.
How I use it
Roughly 25 monitors, tagged by category (Infrastructure, Management, Cloud, Entertainment, Sandbox). Heartbeat 60 seconds, retry twice before flipping a state. The discipline I've settled on:
- Use direct IPs, not internal hostnames. If AdGuard goes down, hostname-based monitors would all go red for DNS reasons even when the underlying services are fine — noisy and misleading. Direct IPs give a clean signal: is the actual service up?
- Vaultwarden is the exception: monitored two ways. One via the proxied hostname (catches NPM, cert, and DNS problems plus tracks cert expiry), one via direct IP (decoupled from the proxy/DNS chain). Both green = everything works. Only direct green = NPM or cert issue. Both red = Vaultwarden itself is down.
- HTTP vs ping: pure-network devices (Tailscale gateway LXCs, the router) get pinged. Anything with a web UI gets HTTP'd.
- Prometheus scrape enabled: Uptime Kuma's
/metricsendpoint is scraped by the observability stack, so monitor state shows up in Grafana too.
Setup notes
- Host: a Docker container on the edge LXC, alongside NPM, AdGuard, and Homepage.
- DNS in compose: the container's DNS is pointed at AdGuard so it can resolve internal hostnames where I need them.
- Reverse proxy: yes, with websockets enabled (the dashboard uses them for live updates).
- Status page: a single status page called
homelablists every monitor, consumed by the internal Homepage widget. - API keys: enabled for the Prometheus scrape. The key is stored as a bcrypt hash in the SQLite DB; rotation is a few SQL statements documented in the Grafana service notes.
- Backups: PBS captures the LXC; the Uptime Kuma SQLite DB rides along.
- Update cadence: manual.
Runbook
- Healthy looks like: dashboard loads, every monitor is green, the internal Homepage "services up" widget shows the expected count.
- Homepage widget shows zeros: status-page slug mismatch. The widget references
slug: homelaband there has to be an actual status page with that exact slug, with all monitors added to it. - Monitor shows DOWN with a TLS error but the service is up: Uptime Kuma doesn't use the system trust store. Even for a perfectly valid cert, the monitor needs "Ignore TLS/SSL error" toggled on for self-signed certs.
- Monitor shows DOWN for a Windows host: Windows blocks ICMP by default. Switch from ping to HTTP if the host has a web service.
- One specific monitor is flapping: heartbeat interval too short for the service's response time, or the service genuinely is unstable. Bump the interval, or fix the upstream.
- Where logs live:
docker logs uptime-kumaon the host; the dashboard has its own per-monitor history view.