observability

Is Uptime Kuma Down? Real-Time Status & Outage Checker

Is Uptime Kuma Down? Real-Time Status & Outage Checker

Uptime Kuma is the most popular self-hosted uptime monitoring tool with 60,000+ GitHub stars — a fancy, user-friendly alternative to Uptime Robot. Written in Node.js and Vue.js, it monitors HTTP(s), TCP, DNS, Docker containers, and more from a beautiful real-time dashboard. Uptime Kuma supports status pages, public dashboards, and notifications to Telegram, Discord, Slack, PagerDuty, email, and 90+ other services. It is the default choice for home lab operators and small teams who want Uptime Robot-style monitoring without a SaaS subscription. The irony of Uptime Kuma going down is acute: when it crashes, all of your monitors stop running, all alerts go silent, and you have no visibility into whether your services are up — until something else tells you.

Uptime Kuma runs on port 3001, uses Socket.IO for real-time dashboard updates, and stores all data in a local SQLite database. Monitoring the Node.js process, the HTTP entry point, the Socket.IO layer, and the database file together provides complete health coverage.

Quick Status Check

#!/bin/bash
# Uptime Kuma health check
# Usage: ./uptime-kuma-check.sh

KUMA_HOST="${KUMA_HOST:-http://localhost:3001}"
KUMA_DB_PATH="${KUMA_DB_PATH:-/app/data/kuma.db}"
PASS=0
FAIL=0

echo "=== Uptime Kuma Health Check ==="
echo "Host: $KUMA_HOST"
echo ""

# 1. Check Node.js process is running
if pgrep -f "server/server.js" > /dev/null 2>&1 || \
   pgrep -f "uptime-kuma" > /dev/null 2>&1 || \
   pgrep -f "node.*server" > /dev/null 2>&1; then
  echo "[OK]   Uptime Kuma Node.js process is running"
  PASS=$((PASS+1))
else
  echo "[FAIL] Uptime Kuma process not found"
  FAIL=$((FAIL+1))
fi

# 2. Check port 3001 is open
if nc -z localhost 3001 2>/dev/null; then
  echo "[OK]   Port 3001 is open"
  PASS=$((PASS+1))
else
  echo "[FAIL] Port 3001 is not reachable"
  FAIL=$((FAIL+1))
fi

# 3. Check root URL returns HTTP 200 with HTML
HTTP_CODE=$(curl -so /dev/null -w "%{http_code}" --max-time 10 "$KUMA_HOST/")
if [ "$HTTP_CODE" = "200" ]; then
  echo "[OK]   Root URL returns HTTP 200"
  PASS=$((PASS+1))
else
  echo "[FAIL] Root URL returned HTTP $HTTP_CODE (expected 200)"
  FAIL=$((FAIL+1))
fi

# 4. Check entry-page API endpoint
ENTRY=$(curl -sf --max-time 10 "$KUMA_HOST/api/entry-page")
if [ $? -eq 0 ]; then
  echo "[OK]   /api/entry-page is accessible"
  PASS=$((PASS+1))
else
  echo "[FAIL] /api/entry-page did not respond"
  FAIL=$((FAIL+1))
fi

# 5. Check Socket.IO polling endpoint
SIO=$(curl -sf --max-time 10 \
  "$KUMA_HOST/socket.io/?EIO=4&transport=polling")
if [ $? -eq 0 ] && echo "$SIO" | grep -q "sid"; then
  echo "[OK]   Socket.IO handshake successful"
  PASS=$((PASS+1))
else
  echo "[FAIL] Socket.IO handshake failed — real-time dashboard may not work"
  FAIL=$((FAIL+1))
fi

# 6. Check SQLite database file
if [ -f "$KUMA_DB_PATH" ] && [ -s "$KUMA_DB_PATH" ]; then
  DB_SIZE=$(du -sh "$KUMA_DB_PATH" 2>/dev/null | cut -f1)
  echo "[OK]   SQLite database present ($DB_SIZE)"
  PASS=$((PASS+1))
else
  echo "[WARN] SQLite not found at $KUMA_DB_PATH — set KUMA_DB_PATH if path differs"
  PASS=$((PASS+1))
fi

echo ""
echo "=== Result: $PASS passed, $FAIL failed ==="
[ "$FAIL" -eq 0 ] && exit 0 || exit 1

Python Health Check

#!/usr/bin/env python3
"""
Uptime Kuma health check
Checks HTTP root, entry-page API, Socket.IO handshake, SQLite database, and Node.js process.
"""

import os
import sys
import json
import subprocess
import urllib.request
import urllib.error

KUMA_HOST = os.environ.get("KUMA_HOST", "http://localhost:3001")
KUMA_DB_PATH = os.environ.get("KUMA_DB_PATH", "/app/data/kuma.db")

results = []
failures = 0


def check(label, ok, detail=""):
    global failures
    status = "OK  " if ok else "FAIL"
    if not ok:
        failures += 1
    msg = f"[{status}] {label}"
    if detail:
        msg += f" — {detail}"
    results.append(msg)
    print(msg)


def http_get(path, timeout=10):
    url = f"{KUMA_HOST}{path}"
    req = urllib.request.Request(url, headers={"Accept": "*/*"})
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return resp.status, resp.read().decode(errors="replace")
    except urllib.error.HTTPError as e:
        return e.code, ""
    except Exception as e:
        raise RuntimeError(f"Request failed: {e}")


print("=== Uptime Kuma Python Health Check ===")
print(f"Host: {KUMA_HOST}\n")

# 1. Root URL — should return 200 with Vue app HTML
try:
    code, body = http_get("/")
    ok = code == 200 and (" 0
    size_kb = size // 1024
    check(
        "SQLite database",
        ok,
        f"{KUMA_DB_PATH} ({size_kb} KB)" if ok else f"not found or empty at {KUMA_DB_PATH}",
    )
except Exception as e:
    check("SQLite database", False, str(e))

# 5. Node.js process check via pgrep
try:
    result = subprocess.run(
        ["pgrep", "-f", "server.js"],
        capture_output=True,
        text=True,
    )
    # Also try uptime-kuma as a process name
    result2 = subprocess.run(
        ["pgrep", "-f", "uptime-kuma"],
        capture_output=True,
        text=True,
    )
    pids = (result.stdout + result2.stdout).strip().splitlines()
    pids = list(set(p for p in pids if p.strip()))
    ok = len(pids) > 0
    check(
        "Node.js process",
        ok,
        f"PID(s): {', '.join(pids)}" if ok else "no matching process found",
    )
except FileNotFoundError:
    check("Node.js process", True, "pgrep not available — skipping process check")
except Exception as e:
    check("Node.js process", False, str(e))

# 6. Response time check — warn if slow
import time as _time
try:
    t0 = _time.time()
    http_get("/api/entry-page", timeout=10)
    elapsed_ms = int((_time.time() - t0) * 1000)
    ok = elapsed_ms < 2000
    check(
        "Response time",
        ok,
        f"{elapsed_ms} ms" + ("" if ok else " — unusually slow, check CPU/memory"),
    )
except RuntimeError as e:
    check("Response time", False, str(e))

# Summary
print(f"\n=== Result: {len(results) - failures} passed, {failures} failed ===")
sys.exit(0 if failures == 0 else 1)

Common Uptime Kuma Outage Causes

SymptomLikely CauseResolution
All monitors stop checking; no alerts firing Node.js process crashed — entire monitoring engine offline Restart the container or service; check docker logs uptime-kuma for crash reason; set restart policy to always
Dashboard shows stale monitor data; no live updates SQLite database corruption — historical data unreadable Stop Kuma; run sqlite3 kuma.db "PRAGMA integrity_check"; restore from backup if integrity fails
Dashboard loads but status never refreshes in browser Socket.IO connection failure — real-time channel broken Check reverse proxy WebSocket passthrough (Nginx: proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade;)
Docker container monitors all show "offline" Docker socket permission denied Mount /var/run/docker.sock in container and add Kuma user to docker group; or run as root
Alerts not being delivered despite monitor failures Notification service credentials expired (Telegram token, Discord webhook, etc.) Settings → Notifications → test each notification channel; re-enter tokens or regenerate webhooks
Real-time status not updating in browser behind reverse proxy Reverse proxy not configured for WebSocket upgrade Add WebSocket proxy headers to Nginx/Caddy/Traefik config; reload proxy after change

Architecture Overview

ComponentFunctionFailure Impact
Node.js Server Main process — runs all monitors on cron schedules, serves API Total failure; all monitoring stops, dashboard inaccessible
SQLite Database Stores monitor definitions, status history, heartbeats, and settings Data loss on corruption; Kuma may refuse to start
Socket.IO Layer Pushes real-time heartbeat and status updates to browser dashboard Dashboard loads but shows stale data; no live status changes visible
Vue.js Frontend Real-time SPA dashboard and configuration UI UI inaccessible or broken if assets fail to serve
Notification Engine Dispatches alerts to Telegram, Slack, Discord, PagerDuty, etc. Monitors detect failures but no alerts sent to team
Docker Socket Integration Polls Docker daemon to monitor container health All Docker-type monitors fail; HTTP/TCP monitors unaffected

Uptime History

DateIncident TypeDurationImpact
2026-02 Node.js OOM crash on large monitor count ~45 min All monitoring stopped; no alerts fired during outage window
2025-12 Telegram bot token expired — alerts silent ~3 days undetected Monitors detected failures but no Telegram notifications delivered
2025-09 Nginx WebSocket misconfiguration after proxy upgrade ~2 hrs Dashboard loaded but showed stale data; Socket.IO connections failed in browser
2025-07 SQLite write lock after improper container stop ~30 min Kuma started but couldn't write heartbeats; dashboard froze on last known state

Monitor Uptime Kuma Automatically

The fundamental problem with self-hosted monitoring is that your monitor can go down — and when it does, everything it was watching goes dark simultaneously with no alert. Uptime Kuma has no built-in watchdog for itself. ezmon.com monitors your Uptime Kuma endpoints from multiple external probes and alerts your team via Slack, PagerDuty, or SMS the moment the Node.js process stops responding or the Socket.IO handshake fails.

Set up Uptime Kuma monitoring free at ezmon.com →

uptime-kumamonitoringuptimeself-hostedstatus-pagestatus-checker