Overview
Most uptime monitoring services charge per monitor. I have twelve things I want to check, and somewhere around the fourth monitor the pricing starts to feel absurd for what is, technically, an HTTP request every sixty seconds. Uptime Kuma runs in a container, costs nothing, and takes about five minutes to set up.
It's not as polished as Pingdom. It doesn't need to be.
What you get
- HTTP(S), TCP, ping, DNS, and keyword monitors
- Push monitors — your service calls in, useful for cron jobs and backups
- Notification via email, Slack, Discord, Telegram, ntfy, webhook, and about sixty others
- Status pages you can publish publicly
- A clean dashboard that isn't hiding features behind a paywall
The push monitor type is the one people overlook. It's how you answer "did my nightly backup actually run?" — your backup script pings a URL on success, and Uptime Kuma alerts if it doesn't hear from you by the expected time.
Running it
Docker Compose is the sane way:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- "3001:3001"
volumes:
- ./data:/app/data
environment:
- UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN=1
docker compose up -d
Navigate to http://your-server:3001 and create an admin account. Do this immediately — the first-run setup is open to anyone who can reach the port.
The image reference is from the Uptime Kuma Docker Hub page.
Adding monitors
The defaults are reasonable. A few things I'd change:
| Setting | Default | What I use | Why |
|---|---|---|---|
| Heartbeat interval | 60s | 60s for public, 300s for internal | Your own server doesn't need to be checked every minute |
| Retries | 0 | 2 | Avoids alerting on a single blip |
| Heartbeat retry interval | 60s | 20s | Confirms the failure faster after a retry |
| Accepted status codes | 200–299 | Add 301, 302 for redirects | Otherwise a redirecting homepage shows as down |
That retry setting matters more than people think. Without retries, a single dropped packet during a deploy triggers a "site is down" notification. With two retries at twenty seconds, you get one alert after a minute of real downtime instead of three false ones.
Notifications
Pick one channel and configure it properly. Splitting alerts across email and Slack means you'll ignore both.
For email, Uptime Kuma supports SMTP directly but wants to hit a real mail server. If you don't have one, a transactional provider works fine. For anything else, ntfy is the path of least resistance — self-hosted push notifications with an Android app, no account required.
The webhook notification is the escape hatch. If your provider isn't in the list, it probably accepts a POST somewhere:
{
"service": "{{ msg }}",
"status": "{{ status }}",
"url": "{{ monitorJSON['url'] }}",
"time": "{{ heartbeatJSON['time'] }}"
}
That's the template variable format. It's under-documented but works.
Status pages
You can publish a public status page at https://your-domain/status/your-page. Two things to know:
- Add a custom domain or reverse proxy it. Exposing
yourserver:3001publicly means your admin panel is one path traversal away from the internet. - Only include monitors you want the world to see. The default is everything.
For a private status page, there's a setting per page that requires login. Useful for internal dashboards.
Where it falls short
Honest list of limitations, because the enthusiasm for Uptime Kuma online is a bit uncritical:
- No distributed checks. All monitors run from one instance. If that instance loses connectivity, everything looks down. For real uptime monitoring, you want probes in multiple regions — that's what the commercial services actually sell.
- No SLA reporting. You can see uptime percentages, but there's no proper report generation with monthly breakdowns you can hand to a client.
- SQLite by default. Fine for a personal setup, slow once you have a year of heartbeat data and thirty monitors. Migrating to Postgres or MariaDB is supported but fiddly.
- No scheduled maintenance windows with automatic notification suppression at the time of writing. You have to remember to disable notifications manually.
None of these are dealbreakers for a personal or small-business setup. If you're monitoring on behalf of paying customers with an SLA, use a commercial service and let them worry about multi-region probes.
What I actually monitor
For what it's worth, the list I settled on:
- Homepage of each public site, HTTP monitor, 60s
- Health endpoint of each API, HTTP with keyword check, 60s
- Postgres port from outside the network, TCP, 300s
- Certificate expiry — actually Uptime Kuma doesn't check this directly, so I use a separate cron script that pings a push monitor if any cert is within 14 days of expiry
- Nightly backup job — push monitor with a 25-hour timeout
- Disk usage on each VPS — same pattern, a cron script that pushes only when usage is above 85%
The push monitors are the underrated part. They turn Uptime Kuma from "is it up" into "did the thing happen" which is usually the more useful question.
