Was ist im Heimnetz erreichbar? Eine eigene Statusseite mit Prometheus-Metriken
Meine kleine Statusanwendung prüft Dienste und Geräte im Heimnetz, zeigt Erreichbarkeit in einer HTML-Tabelle und stellt Messwerte für Prometheus bereit.
Eine Übersicht für viele kleine Dienste
Mit zunehmendem HomeLab wachsen nicht nur die Server, sondern auch die Fragen: Ist der Router erreichbar? Läuft Grafana? Ist der Proxmox-Host online? Antwortet der Integrationsdienst? Mit meiner eigenen Statusseite bündele ich diese Informationen auf einer einfachen Oberfläche.
Meine aktuelle HTML-Ausgabe listet Hostnamen, Zieladresse, Port und Status. Die Seite aktualisiert sich automatisch und verweist zusätzlich auf Prometheus-Metriken.
Erreichbarkeit ist ein bewusst kleiner Prüfbegriff
Meine Statusseite beantwortet zunächst eine begrenzte Frage: Kann ein definierter Endpunkt erreicht werden? Das ist nicht dasselbe wie „die Anwendung funktioniert vollständig“. Ein HTTP-Endpunkt kann antworten, obwohl eine Datenbank, ein Hintergrundjob oder ein fachlicher Prozess gestört ist.
Diese Begrenzung ist eine Stärke, wenn sie sichtbar bleibt. Die Tabelle liefert einen schnellen Infrastrukturindikator und ersetzt keine tiefere Anwendungsprüfung.
HTML für Menschen, Metriken für Maschinen
Die menschenlesbare Seite nutzt farbige Statuszeilen für erreichbare und nicht erreichbare Ziele. Parallel stellt das Projekt Metriken bereit, die von Prometheus verarbeitet werden können. Damit lässt sich aus einer einfachen Übersicht später ein Verlauf von Ausfällen und Wiederherstellungen entwickeln.
Eine Statusseite wird dadurch zu einem kleinen Baustein einer größeren Observability-Kette: Probe, Speicherung, Visualisierung und Alarmierung haben jeweils eigene Aufgaben.
Der sichtbare Status als Nachweis
In der vorhandenen Ausgabe waren 17 Ziele erreichbar und ein Ziel nicht erreichbar. Das ist ein konkreter Momentzustand und kein dauerhafter Qualitätsnachweis. Für einen späteren Artikel über die Überarbeitung der Statuslogik sind deshalb Zeitreihen, Zustandsänderungen und die Behandlung eines ausgefallenen PVE-Hosts besonders wichtig.
Quellcode und Datenfluss der Statusseite
Die Dateien bilden keine einzelne Statusseite, sondern eine kleine Messkette. Ein Probe-Prozess schreibt den aktuellen Zustand in status.json, erzeugt Prometheus-Metriken und protokolliert Zustandswechsel in events.jsonl. Die serverseitige index.html ist eine tabellarische Momentaufnahme; Status.html lädt die JSON-Daten dagegen regelmäßig über den fest verdrahteten PHP-Proxy.
flowchart LR A[Probe-Prozess] --> B[status.json] A --> C[metrics] A --> D[events.jsonl] B --> E[index.html] B --> F[status-proxy.php] F --> G[Status.html] C --> H[Prometheus]
Ablauf eines Statusabrufs
Der Browser erhält die Statusdaten nicht direkt von einer frei wählbaren Zieladresse. Der Proxy verwendet eine feste Quelle, deaktiviert Weiterleitungen, setzt ein Timeout und antwortet bei Problemen mit HTTP 502. Die Oberfläche lädt die Daten ohne Cache und wiederholt die Prüfung im Minutenintervall.
sequenceDiagram participant Browser participant UI as Status.html participant Proxy as status-proxy.php participant Source as status.json Browser->>UI: Seite laden UI->>Proxy: GET ?t=timestamp Proxy->>Source: Fester JSON-Endpunkt Source-->>Proxy: results[] Proxy-->>UI: JSON ohne Cache UI->>UI: Dienste gruppieren und rendern UI->>Proxy: erneuter Abruf nach 60 s
Bereinigtes PHP-Snippet des Proxys
Der relevante Sicherheits- und Fehlerbehandlungsteil aus status-proxy.php:
$source = 'https://example.example.app/status/status.json';
$ch = curl_init($source);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
]);
$body = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($body === false || $httpCode < 200 || $httpCode >= 300) {
http_response_code(502);
echo json_encode(['error' => 'Statusquelle nicht erreichbar']);
exit;
}
echo json_encode(json_decode($body, true), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
JavaScript: Gruppieren und regelmäßig aktualisieren
Status.html gruppiert mehrere Prüfzeilen desselben Dienstes über die Servicebezeichnung. Dadurch können Domainprüfung und interner Endpunkt gemeinsam dargestellt werden, ohne dass die Rohdaten verloren gehen.
const STATUS_URL = './status-proxy.php';
async function loadStatus() {
const response = await fetch(STATUS_URL + '?t=' + Date.now(), {
cache: 'no-store'
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const data = await response.json();
const rows = Array.isArray(data.results) ? data.results : [];
const byService = new Map();
for (const row of rows) {
const names = Array.isArray(row.service) && row.service.length
? row.service : [row.host];
for (const name of names) {
if (name !== 'on') byService.set(name, { ...row, service: name });
}
}
renderServices([...byService.values()]);
}
loadStatus();
setInterval(loadStatus, 60000);
Prometheus-Schnittstelle
Die Metrik ist bewusst ein einfacher Up/Down-Indikator. Im geprüften Datensatz vom 29.10.2026 waren 30 von 31 Ergebnissen erreichbar; pve.example.app auf 192.168.130.x:8006 war nicht erreichbar. Die HTML-Momentaufnahme weist 17 von 18 aggregierten Domainzeilen als erreichbar aus, weil JSON zusätzlich interne HTTP-Prüfungen enthält.
# HELP example_service_up 1 if service endpoint is up
# TYPE example_service_up gauge
example_service_up{service="prometheus.example.app",endpoint="192.168.130.xx",port="9090"} 1
example_service_up{service="pve.example.app",endpoint="192.168.130.xx",port="8006"} 0
Was die Ereignisse zusätzlich zeigen
events.jsonl enthält 23 Zustandswechsel. Besonders auffällig sind wiederkehrende kurze Unterbrechungen bei Grafana, Loki und Prometheus sowie einzelne Wechsel bei Home Assistant und n8n. Das ist ein guter Ansatz für eine spätere Zeitreihenanalyse, aber noch kein Beweis für die Ursache der Ausfälle: Die Ereignisse zeigen Korrelationen und Zeitpunkte, nicht automatisch den auslösenden Prozess.
