Uptime Kuma is the "is it down?" half of monitoring, and it's the one I check first when my phone buzzes. It doesn't graph CPU or RAM — that's Netdata's job — it pings your services every 60 seconds and shouts on Telegram the moment one stops answering. One container, a five-minute setup, and you stop finding out your server's down from an angry player.
TL;DR:
docker run -d --restart=unless-stopped \
-p 3001:3001 \
-v uptime-kuma:/app/data \
--name uptime-kuma \
louislam/uptime-kuma:1
# open http://<server-ip>:3001 and create the admin account
Details below.
Run it with Docker
Kuma ships as one image and stores everything in a single volume. The quick docker run above works, but I keep it in a compose file so it's documented and easy to update:
# /opt/uptime-kuma/docker-compose.yml
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001" # bind to localhost, proxy in front
volumes:
- ./data:/app/data
cd /opt/uptime-kuma
docker compose up -d
docker compose logs -f
Note the 127.0.0.1:3001 bind — I don't expose the app port to the internet directly, I put a reverse proxy with TLS in front (below). If you just want it on your LAN, 3001:3001 is fine. Need Docker first? /docker-install-ubuntu/.
The first time you open it you create the admin user right there in the browser. There's no default password to change — it's set on first run.
Add your first monitors
A "monitor" is one thing to watch. The types you'll actually use:
- HTTP(s) — for websites and APIs. Kuma follows redirects and checks the status code. Turn on keyword matching to assert a string is present in the body, so a 200 that serves an error page still alerts.
- TCP Port — for game servers, databases, anything that isn't HTTP. It just checks the port accepts a connection. Perfect for "is my Minecraft/Hytale server listening on its port".
- Ping — ICMP, "is the host even up".
- Push — Kuma gives you a URL; your own cron/script curls it on success. If the ping doesn't arrive in time, Kuma alerts. This is how you monitor a backup job: it pings Kuma when it finishes, and you get told when it silently stops running.
Set the check interval (60s is the default and fine for most things), retries before it's declared down (2-3 stops flapping on a single blip), and you're monitoring.
Notifications that reach your phone
A monitor with no notification is a dashboard you'll never look at. Set up a channel under Settings → Notifications, then attach it to your monitors. The two I use:
Telegram — make a bot with @BotFather, grab the token, get your chat ID (message the bot, then read https://api.telegram.org/bot<token>/getUpdates). Paste both into Kuma. Done.
Discord — Server Settings → Integrations → Webhooks → New Webhook, copy the URL, paste into Kuma. Good for a team channel.
There's also email/SMTP, and a long list of others. Whatever you pick, send a test notification from the settings page before you trust it — a misconfigured webhook fails silently exactly when you need it.
A public status page
Kuma can publish a status page so users check that instead of asking you. Create one under Status Pages, add your monitors, and it's live at /status/<slug>. Drop the incident banner on it during maintenance so nobody panics.
You can group monitors ("Game Servers", "Web"), set a custom domain, and it stays readable even when your main services are down because it's served by Kuma, not by them.
Reverse proxy with TLS
Don't serve Kuma over plain HTTP on a random port. Put it behind a reverse proxy on a real hostname with a certificate. With Caddy it's two lines and the cert is automatic:
# /etc/caddy/Caddyfile
status.example.com {
reverse_proxy 127.0.0.1:3001
}
Kuma uses websockets, and Caddy proxies those transparently — nothing extra to configure. Why I reach for Caddy here over nginx: /caddy-vs-nginx-auto-https/. Prefer nginx? The proxy block needs the websocket upgrade headers — see /nginx-reverse-proxy-setup/.
Back it up
Everything — monitors, history, settings, notification configs — lives in that one data volume. Backing up Kuma is backing up that directory:
docker compose stop
tar -czf kuma-$(date +%F).tar.gz -C /opt/uptime-kuma data
docker compose start
Stop the container first so the SQLite DB isn't mid-write. Wire it into a systemd timer and it looks after itself.
Gotchas
- Monitoring from the same box. If Kuma runs on the same server as the things it watches, a full outage takes the monitor down too — and you get no alert because the alerter is also dead. Run Kuma somewhere separate (a cheap VPS, a Pi at home) so it can actually tell you the main box vanished.
- Websockets through the proxy. Kuma's UI is realtime over websockets. A reverse proxy that doesn't forward the upgrade headers gives you a UI that loads but never updates. Caddy handles it for free; nginx needs
Upgrade/Connectionheaders. - Notification never tested. Set it up, click "Test", confirm it lands. The worst time to discover a broken webhook is during a real outage.
- Check interval too aggressive. 20-second checks against someone else's API can look like abuse and get you rate-limited or blocked. 60s is plenty for almost everything.
- Pin the major tag. Use
louislam/uptime-kuma:1, not:latest, so a major version jump doesn't surprise you on the nextpull.
This is the alerting layer. Pair it with Netdata for the per-second metrics and you've got both halves: Kuma tells you that something broke, Netdata helps you see why.