Monitoring & alerts¶
How the lab notices that something is wrong, and how it tells me. Everything here is self-hosted and runs as Docker stacks on the servers, except the host-level checks (SMART, disk space, update reboots), which are systemd units on each host. Those are covered in Host maintenance.
| Tool | Job | Runs on |
|---|---|---|
| ntfy | The one place every alert is delivered: phone app + desktop notifications | control-host |
| Uptime Kuma | Is each service answering? Plus dead-man switches for nightly jobs | control-host |
| Beszel | Host metrics, container stats, disk usage and SMART per disk | hub on control-host, agent on every server |
| Dozzle | Live container logs for every host in one read-only UI | hub on control-host, agent on every server |
| Remote syslog | Keeps the firewall's logs for 90 days, off the firewall | control-host |
What alerts where¶
Every source publishes to ntfy. ntfy is the only thing that talks to my phone and desktop, so there's one app to configure and one place to look.
flowchart LR
subgraph sources[Publishers]
deploy[Deploy tool<br/>host/service down, failed deploys]
git[Git server webhooks<br/>update PRs, merges]
kuma[Uptime Kuma<br/>monitor DOWN/UP]
beszel[Beszel<br/>metric thresholds]
borg[borgmatic<br/>every backup run]
host[Host scripts<br/>SMART, disk space,<br/>reboots, apt, SnapRAID]
end
subgraph ntfy[ntfy]
alerts([alerts])
updates([updates])
backups([backups])
hosts([hosts])
end
deploy --> alerts
deploy --> updates
git --> updates
kuma --> alerts
beszel --> alerts
borg --> backups
borg --> alerts
host --> alerts
host --> hosts
host --> backups
host --> updates
alerts --> phone[Phone app]
updates --> phone
backups --> phone
hosts --> phone
alerts --> desk[Desktop client<br/>KDE notifications]
updates --> desk
backups --> desk
hosts --> desk
borg -. push .-> kuma
host -. push .-> kuma
The dotted lines are the dead-man switches: nightly jobs also push their result to an Uptime Kuma push monitor, and Kuma alerts when a push doesn't arrive. See Uptime Kuma.
Topic design¶
Four topics, split by what I need to do about the message, not by which tool sent it:
| Topic | What lands there | Phone setting |
|---|---|---|
alerts |
Something is broken or about to be: host/service down, failed deploys, Kuma DOWN, SMART problems, disk filling up, failed SnapRAID, a backup failing twice in a row | Max priority, always notify |
updates |
What changed: update PRs opened/merged, stacks redeployed, a Docker engine update waiting for a manual upgrade | Default / quiet |
backups |
Every backup run, OK (low priority) or FAILED (high), plus a daily summary | Quiet; FAILED still pops |
hosts |
Patching and housekeeping: "rebooting for updates", "back up", packages upgraded, SnapRAID OK | Quiet |
Why separate topics instead of one "homelab" topic:
- Notification settings per topic.
alertscan be set to urgent and break through do-not-disturb, whileupdatesandhostsstay silent and are just there when I look. With one topic, either everything is loud (and I start ignoring it) or nothing is. - Alert fatigue is the real enemy. "Upgraded 4 packages" in the same stream as "disk
failing" trains you to swipe both away. Keeping
alertsfor things that need action keeps it worth reading. - Least-privilege publishing. Each publisher gets a token that can only write, and
only to the topics it needs (Uptime Kuma →
alertsonly, borgmatic →backups+alerts). A leaked token can't read anyone's messages or spam topics it was never given. See ntfy. - Priorities still matter inside a topic. borgmatic's OK messages are priority
min, its FAILED oneshigh. Uptime Kuma sends DOWN at 5 and UP at 3.
Design rules that came out of things breaking¶
- Alert on the job, not only the service. A nightly sync once stopped for a month without anyone noticing, because nothing was down, it just never finished. Every nightly job now reports to a Kuma push monitor that goes DOWN after ~25 h of silence.
- Use LAN addresses for alert delivery. Publishers post to ntfy's LAN address (or over the shared Docker network) first, so an alert about DNS or the tunnel being down can still be delivered. Hostnames and the public URL are only the fallback.
- Silence planned work, but only exactly that. Maintenance windows in Uptime Kuma and the deploy tool cover the nightly parity sync for the services it pauses, and nothing else. A run that overruns the window alerts normally. (Lessons learned)
- Report level changes, not states. Disk-space and firewall checks alert when something goes from ok → warning → critical (and back), not every hour it stays bad.
- Alerting has a single point of failure. If the host running ntfy and Kuma dies, nothing reports it. An external dead-man switch (a free healthchecks.io check pinged every few minutes, or a second Kuma on separate hardware) closes that gap.