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:

SettingDefaultWhat I useWhy
Heartbeat interval60s60s for public, 300s for internalYour own server doesn't need to be checked every minute
Retries02Avoids alerting on a single blip
Heartbeat retry interval60s20sConfirms the failure faster after a retry
Accepted status codes200–299Add 301, 302 for redirectsOtherwise 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:3001 publicly 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:

  1. Homepage of each public site, HTTP monitor, 60s
  2. Health endpoint of each API, HTTP with keyword check, 60s
  3. Postgres port from outside the network, TCP, 300s
  4. 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
  5. Nightly backup job — push monitor with a 25-hour timeout
  6. 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.