---
title: "You Don't Need Uptime Kuma for One Site"
description: "Every self-hosting thread points at Uptime Kuma. For monitoring exactly one site with exactly one notification channel I actually check, a systemd timer and ntfy turned out to be the whole stack."
url: "https://mynerdylife.com/blog/you-dont-need-uptime-kuma-for-one-site/"
author: "Jared"
published: "2026-08-01"
category: "Raspberry Pi"
tags: ["ntfy","monitoring","systemd","self-hosted"]
---

# You Don't Need Uptime Kuma for One Site

Every self-hosting subreddit thread about monitoring eventually points at Uptime Kuma, and it's a genuinely good tool — a whole dashboard, dozens of notification integrations, a nice UI. But I already had exactly one thing I cared about monitoring (this site) and exactly one notification channel I actually check (ntfy, which I already push boot/shutdown alerts to from this same Pi). Standing up a whole extra Docker container and dashboard for one URL felt like the wrong amount of infrastructure for the problem.

So here's the whole stack instead: a systemd timer, a shell script, and a topic on ntfy.sh.

## The uptime check

```bash
#!/bin/bash
set -uo pipefail

URL="https://mynerdylife.com/"
STATE_FILE="/var/lib/site-uptime-check/state"
mkdir -p "$(dirname "$STATE_FILE")"

PREV_STATE="unknown"
[ -f "$STATE_FILE" ] && PREV_STATE="$(cat "$STATE_FILE")"

HTTP_CODE="$(curl -fsS -o /dev/null -w '%{http_code}' --max-time 15 "$URL" 2>/dev/null)"
CURR_STATE="up"; [ "$HTTP_CODE" = "200" ] || CURR_STATE="down"

if [ "$CURR_STATE" != "$PREV_STATE" ]; then
    if [ "$CURR_STATE" = "down" ]; then
        ntfy-notify.sh "mynerdylife.com is down" "Got HTTP ${HTTP_CODE:-no response}" "rotating_light,uptime" urgent
    elif [ "$PREV_STATE" = "down" ]; then
        ntfy-notify.sh "mynerdylife.com is back up" "Recovered — HTTP 200" "white_check_mark,uptime" default
    fi
fi

echo -n "$CURR_STATE" > "$STATE_FILE"
```

The state-file check is the only part that isn't completely obvious, and it's the part that matters: without it, a timer running every 5 minutes sends a "down" alert every 5 minutes for as long as the outage lasts, which is worse than useless — it trains you to ignore the topic. Comparing against the last known state means you get exactly two notifications per incident: one when it goes down, one when it recovers.

Wired to a systemd timer:

```ini
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=30s
```

One deliberate choice: it checks the live public URL through Cloudflare, not `localhost` on the Pi itself. Checking locally would tell me nginx is fine, which is a different (and less useful) fact than "the internet can actually reach my site" — a Cloudflare-side outage or a DNS problem would look completely healthy from inside the house.

## The other half: catching a silent monitoring pipeline

I'd separately built a Lighthouse CI pipeline that audits the site daily and stores performance/SEO/accessibility scores — and then, embarrassingly, forgotten it existed for a few days, because it ran and stored data but never told me anything. Same fix, same pattern: a small script parses the day's scores, and if performance drops below 70 or accessibility/best-practices/SEO drop below 90, it fires the same `ntfy-notify.sh` helper. If the audit run itself fails outright — which on a Pi with no mail server configured would otherwise fail completely silently — that gets its own alert too.

## The actual point

The pattern here isn't "use ntfy specifically" — it's that a notification channel you already trust and already check is worth more than a fancier dashboard you'll forget to open. I have one topic, a handful of scripts that all shell out to the same `ntfy-notify.sh` helper, and zero new services to keep updated. For one site, that's the right amount of monitoring.

(That same `ntfy-notify.sh` helper turned out to have more range than I expected — I wrote up [what its query-string API can actually do](https://mynerdylife.com/blog/ntfy-query-string-api/) after using it for something unrelated.)
