Self-hosting a status page with Uptime Kuma vs Statping vs hand-rolled Bash + Pages

Your VPS is down. You don’t know it yet because nobody’s pinged you. The customer who emailed at 3 AM gets the “our server is unavailable” auto-responder, but it’s actually a real outage. By the time you wake up and check, three more customers have churned. You needed a status page that posts uptime/downtime in real time so you’d have noticed at 3:01 AM, and so customers can self-serve the “is your service down?” question instead of emailing you.

Three credible options for self-hosting a status page in 2026: Uptime Kuma, Statping (-NG), and the “just-write-it-yourself” bash + GitHub Pages combo. I’ve run the first two in production and the third for a personal side-project. Here’s the honest comparison.

Uptime Kuma — the good default

Uptime Kuma is the obvious choice for most people. Single docker-compose, beautiful UI, supports HTTP / TCP / Ping / DNS / push / Docker container / database / Steam / GameDig probes. Notification integrations: Slack, Discord, Telegram, generic webhooks, email, Gotify, Apprise — ~80 backends. Status pages are first-class.

  • What’s good. Looks great out of the box. Set up takes 5 minutes. Each monitor stores history; you can drill into any incident. Public status pages are clean and embeddable. Active development, healthy community.
  • What’s annoying. SQLite-based, single-node. If your homelab box dies, the status page goes with it — precisely when you need it. Not horizontally scalable. The mobile UI is OK but not great.
  • When it fits. Personal infra, small business, anything where one well-configured node is enough.
# /opt/uptime-kuma/docker-compose.yml
services:
  kuma:
    image: louislam/uptime-kuma:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:3001:3001"
    volumes:
      - ./data:/app/data

# In Caddy:
# status.example.com {
#     reverse_proxy 127.0.0.1:3001
# }

First-run wizard sets the admin user. Add monitors. Settings → Status Pages → New creates a public page at status.example.com/status/main.

Statping-NG — the “runs on Linux/Windows/Mac native” one

Statping is the older, statip-tradition tool. Statping-NG is a community fork that’s still maintained. Single Go binary, Postgres / MySQL / SQLite backend, public status page bundled in.

  • What’s good. Single binary install on bare metal. Works without Docker. Postgres backend means you can put the data on durable storage independent of the runtime host. Built-in plugins for Slack, email, AWS SNS, Twilio.
  • What’s annoying. The UI is dated compared to Kuma. Status page customization is more limited. Probe types are fewer.
  • When it fits. You’re allergic to Docker, you want a single Go binary, you have an existing Postgres you want to point at.
# Install (Linux amd64)
wget https://github.com/statping-ng/statping-ng/releases/latest/download/statping-linux-amd64.tar.gz
tar xzvf statping-linux-amd64.tar.gz
sudo mv statping /usr/local/bin/

# Run with sqlite (good for testing):
statping --location=/opt/statping --port=8080

The hand-rolled bash + GitHub Pages combo

The third option is the one nobody talks about: write 50 lines of bash that probe your endpoints, push the results as JSON to a GitHub Pages site, and let the static-site renderer turn JSON into a status page. Crucially, the page is hosted on GitHub’s CDN — not on your own infra. So when your VPS goes down, the status page still works.

  • What’s good. Trivial. The page is hosted on GitHub Pages, so it survives every outage of your own infra. Total cost: $0.
  • What’s annoying. No notifications out of the box (you’d have to add them). No history beyond what’s in your git repo. UI is whatever you build.
  • When it fits. Side projects, hobby infra, when you genuinely want the status page to outlast everything else.
#!/usr/bin/env bash
# /opt/status-probe/probe.sh — runs on a different host than the things being probed
set -euo pipefail
cd /opt/status-probe/repo

declare -A endpoints=(
  [api]="https://api.example.com/health"
  [web]="https://www.example.com/"
  [db]="https://db-health.example.com:8080"
)

ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo -n '{"updated":"'"$ts"'","services":[' > status.json.new
sep=""
for name in "${!endpoints[@]}"; do
  url="${endpoints[$name]}"
  status="up"
  curl -sf --max-time 10 "$url" >/dev/null || status="down"
  echo -n "$sep{\"name\":\"$name\",\"url\":\"$url\",\"status\":\"$status\",\"checked\":\"$ts\"}" \
    >> status.json.new
  sep=","
done
echo -n ']}' >> status.json.new
mv status.json.new status.json

git add status.json
git commit -m "status update $ts" || exit 0
git push origin main

The repo’s GitHub Pages renders an index.html that fetches and displays status.json in JS:

<!-- index.html (in repo root) -->
<script>
fetch('status.json').then(r => r.json()).then(d => {
  const html = d.services.map(s =>
    `<tr><td>${s.name}</td><td class="${s.status}">${s.status}</td></tr>`
  ).join('');
  document.getElementById('rows').innerHTML = html;
});
</script>

Cron runs the probe every 60 seconds. GitHub Pages serves the HTML and JSON. Public URL is https://yourname.github.io/status-page/. Outages on your VPS don’t affect this page at all.

The decision rule

  • You want notifications, history, drill-downs, and a polished UI — you’re OK with self-hosting. → Uptime Kuma. Genuinely the best choice for ~90% of people.
  • You’re allergic to Docker, want a single binary, have a Postgres handy. → Statping-NG.
  • The status page MUST survive when your infra goes down. → Hand-rolled + GitHub Pages. The whole point is host-elsewhere.
  • You’re paying for a SaaS already (Pingdom, Better Uptime, etc.). → Use that. Self-hosting a status page that you have to keep up is its own infra burden.

One trap to avoid: do not host your status page on the same VPS as the things being probed. A status page that goes dark every time your real services do is worse than nothing. Either run Kuma/Statping on a separate cheap box (Hetzner CX22 in another region, $4/month), or use the GitHub Pages trick for outage independence.

Photo: Laptop with an analytics dashboard by Goumbik on Pexels.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.