Einfaches Server-Monitoring mit Bordmitteln einrichten

Veröffentlicht am 18 Min. Lesezeit

Ein Prüfskript, ein systemd-Timer und ein getesteter Benachrichtigungsweg reichen für einen einzelnen Root Server. Diese Anleitung zeigt, was zu überwachen ist, wie Sie jeden Schritt nachprüfen und ab wann sich der große Werkzeugkasten lohnt.

Ein Server meldet sich nicht von selbst, wenn etwas schiefgeht. Er läuft weiter, bis er nicht mehr weiterläuft, und die erste Rückmeldung kommt von einem Kunden oder von Ihnen, wenn Sie zufällig hinsehen. Diese Anleitung baut die kleinste Überwachung, die das beendet: ein Prüfskript, ein systemd-Timer, ein Benachrichtigungsweg. Keine Zeitreihendatenbank, kein Dashboard, kein zusätzlicher offener Port.

Bezugssysteme sind Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS und Ubuntu 22.04 LTS. Wo sich die vier unterscheiden, steht es ausdrücklich dabei. Alle Befehle sind für den Betrieb als root geschrieben; als normaler Benutzer stellen Sie jedem Befehl ein sudo voran. Diese Anleitung vertieft Schritt 8 der Checkliste für einen neuen Rootserver.

Was Sie überhaupt überwachen sollten

Der häufigste Fehler beim ersten Monitoring ist nicht, zu wenig zu messen, sondern zu viel. Wer 40 Kennzahlen sammelt, sieht keine davon an. Sinnvoll ist nur, was den Betrieb beenden kann und worauf Sie reagieren können. Es bleiben sieben Punkte.

KennzahlWarum sie auf die Liste gehörtWoher der Wert kommtSinnvolle Schwelle
PlattenplatzHäufigste Ausfallursache, die niemand kommen siehtdf --output=pcent,targetab 85 Prozent
InodesPlatte scheinbar frei, trotzdem "No space left on device"df --output=ipcent,targetab 85 Prozent
Freier ArbeitsspeicherDer OOM-Killer trifft selten den Prozess, den Sie opfern würdenMemAvailable in /proc/meminfounter 200 MB
SystemlastZeigt Stau an, unabhängig davon ob CPU oder Platte die Ursache istdrittes Feld in /proc/loadavg15-Minuten-Mittel über zweifacher Kernzahl
Fehlgeschlagene DiensteEin Dienst, der nachts stirbt, bleibt sonst bis zum Morgen totsystemctl is-system-runningalles außer running
ZertifikatsablaufSperrt schlagartig jeden Besucher aus, nicht nur einen Teilopenssl x509 -checkendunter 21 Tagen Restlaufzeit
Erreichbarkeit von außenBeantwortet als einzige Prüfung, ob der Server noch da istzweiter Host, curlzwei Fehlschläge in Folge

Nicht auf der Liste steht die CPU-Auslastung in Prozent: Ein Server, der 100 Prozent zieht, weil ein Videokodierer läuft, arbeitet wie vorgesehen. Netzwerkdurchsatz und Prozessanzahl fehlen aus demselben Grund. Beides hilft bei der Ursachensuche, taugt aber nicht als Alarm, weil es keinen Wert gibt, ab dem Sie zwingend handeln müssten.

Der Rückweg, bevor die erste Datei entsteht

Überwachung ist ein lesender Vorgang und kann im Normalfall nichts kaputtmachen. Drei Dinge können es trotzdem.

Ein Skript, das repariert statt zu melden. Der Gedanke ist verführerisch: Wenn nginx tot ist, soll das Skript ihn eben neu starten. Daraus wird ein Dienst, der alle zehn Minuten startet, eine halbe Sekunde läuft und die Ursache verdeckt. Und ein Skript, das bei voller Platte selbstständig löscht, löscht irgendwann etwas, das gebraucht wurde. Die erste Fassung liest ausschließlich und ruft weder systemctl restart noch rm noch kill auf.

Offene Netzwerkschnittstellen. Metrik-Exporter, die auf allen Adressen lauschen, sind eine der häufigsten unbeabsichtigten Datenveröffentlichungen auf Einzelservern. Das Verfahren hier öffnet keinen Port und braucht keine Firewall-Regel.

Der Alarmweg selbst. Eine Überwachung, deren Benachrichtigung nie getestet wurde, ist keine Überwachung, sondern ein gutes Gefühl. Der Test dafür steht weiter unten und ist nicht optional.

Wie Sie ohne SSH auf den Server kommen

KVM-Rootserver und Dedicated Server haben weder IPMI noch iDRAC. Der Zugang, wenn SSH nicht mehr antwortet, ist die VNC-Konsole im Kundenbereich. Sie hängt nicht am Netzwerkstack des Gastsystems, eine Firewall-Regel oder ein überlasteter SSH-Dienst können sie also nicht blockieren. Melden Sie sich dort einmal vorher an und vergewissern Sie sich, dass Sie das root-Passwort kennen.

Der Ausschalter

Falls die Überwachung selbst zum Problem wird, etwa weil sie im Minutentakt Alarme verschickt, brauchen Sie zwei Befehle. Merken Sie sich diese, bevor Sie anfangen:

systemctl disable --now kh-monitor.timer
systemctl mask kh-monitor.service

Der erste beendet den Timer sofort und verhindert, dass er beim nächsten Start wiederkommt. Der zweite ist die Notbremse: Ein maskierter Dienst lässt sich auch nicht mehr versehentlich von Hand starten, zurückgenommen mit systemctl unmask kh-monitor.service. Risikoarm ist das Verfahren vor allem deshalb, weil ausschließlich neue Dateien entstehen; der Rückbau besteht aus dem Löschen dieser Dateien. Halten Sie trotzdem eine zweite SSH-Sitzung offen, solange Sie am System arbeiten.

Die Kennzahlen erst von Hand prüfen

Bevor ein Skript etwas auswertet, sollten Sie jeden Wert einmal selbst gesehen haben. Sonst wissen Sie später nicht, ob ein Alarm berechtigt ist oder ob Ihre Schwelle Unsinn ist.

Plattenplatz und Inodes

df -h
df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay
df -i

Die Ausschlüsse sind notwendig. Unter Ubuntu 22.04 und 24.04 bindet snap seine Pakete als schreibgeschützte squashfs-Abbilder ein, und die stehen dauerhaft bei 100 Prozent. Ohne -x squashfs meldet Ihre Überwachung ab dem ersten Lauf eine volle Platte, jeden Tag, für immer. Für die overlay-Einhängepunkte von Docker gilt dasselbe.

Zwei Eigenheiten sollten Sie kennen. df akzeptiert -P und --output nicht gemeinsam und bricht mit einer Meldung über sich gegenseitig ausschließende Optionen ab. Und auf ext4 sind ab Werk fünf Prozent für root reserviert, weshalb df bereits 100 Prozent meldet, während root noch schreiben kann. Was nach dem Alarm zu tun ist, steht in Festplatte voll unter Linux.

Die Inode-Prüfung ist kein Randthema. Ein Verzeichnis mit Millionen winziger Sitzungs- oder Cache-Dateien kann alle Inodes verbrauchen, während df -h reichlich freien Platz zeigt. Schreibvorgänge scheitern dann mit No space left on device, und die naheliegende Erklärung ist die falsche.

Arbeitsspeicher

free -m
awk '/^MemAvailable:/ { printf "%d MB\n", $2 / 1024 }' /proc/meminfo

Die Spalte free ist auf einem gesunden Linux-System fast immer klein, weil der Kernel ungenutzten Speicher als Dateicache verwendet. Belastbar ist nur available beziehungsweise MemAvailable: die Menge Speicher, die eine neue Anwendung bekommen kann, ohne dass etwas ausgelagert wird. Alarmieren Sie auf diesen Wert, nie auf free.

Ob es früher schon eng war, verrät das Kernel-Log:

journalctl -k -b --grep "Out of memory"

Jede Fundstelle ist ein Prozess, den der Kernel beendet hat, weil der Speicher ausging. Wie Sie reagieren, ohne blind Swap anzulegen, steht in Out of Memory und Swap richtig einrichten.

Systemlast

nproc
cat /proc/loadavg
uptime

Die ersten drei Felder in /proc/loadavg sind die Mittelwerte über eine, fünf und fünfzehn Minuten. Zwei Dinge werden regelmäßig missverstanden. Erstens ist die Last unter Linux keine reine CPU-Größe: Prozesse, die auf Plattenzugriffe warten, zählen mit. Eine Last von 20 auf vier Kernen kann heißen, dass die CPU brennt, oder dass ein Datenträger klemmt. Zweitens taugt der Ein-Minuten-Wert nicht für Alarme, weil jeder Sicherungslauf ihn kurz nach oben treibt. Nehmen Sie das 15-Minuten-Mittel und die Schwelle relativ zur Kernzahl.

Bringt Ihr Kernel die Stau-Statistik mit, ist sie aussagekräftiger, weil sie CPU, Ein- und Ausgabe sowie Speicher getrennt ausweist. Vorhanden ist sie nicht überall:

test -d /proc/pressure && cat /proc/pressure/io || echo "keine Stau-Statistik in diesem Kernel"

Dienste

systemctl is-system-running
systemctl --failed --no-pager
systemctl is-active nginx

systemctl is-system-running ist die kürzeste sinnvolle Gesamtprüfung. Sie gibt running aus, wenn keine einzige Unit im Fehlerzustand ist, und degraded, sobald eine es ist. Der Rückgabewert ist entsprechend 0 oder ungleich 0.

Zwei Fallstricke. Der Zustand degraded bleibt bestehen, bis Sie ihn nach der Reparatur mit systemctl reset-failed zurücksetzen; eine einmal fehlgeschlagene Aufgabe hält die Meldung sonst wochenlang aufrecht. Und beim Prüfen einzelner Dienste unterscheiden sich die Bezugssysteme: Unter Ubuntu 24.04 wird SSH per Socket-Aktivierung gestartet, ssh.service ist dort im Ruhezustand inactive, obwohl SSH einwandfrei erreichbar ist. Wer dort ssh.service überwacht, bekommt einen Dauerfehlalarm. Zu überwachen ist auf Ubuntu 24.04 ssh.socket, auf Debian 12, Debian 13 und Ubuntu 22.04 dagegen ssh.service.

Zertifikatsablauf

openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/fullchain.pem
openssl x509 -checkend 1814400 -noout -in /etc/letsencrypt/live/example.com/fullchain.pem

Der zweite Befehl ist der interessante. -checkend erwartet eine Anzahl Sekunden, 1814400 sind 21 Tage. Läuft das Zertifikat innerhalb dieser Frist ab, gibt der Befehl Certificate will expire aus und liefert den Rückgabewert 1, andernfalls Certificate will not expire und 0. /etc/letsencrypt/live und /etc/letsencrypt/archive sind nur für root lesbar.

Die Prüfung hat eine Lücke, die viele Anleitungen unterschlagen: Sie prüft die Datei auf der Platte, nicht das Zertifikat, das Ihr Webserver ausliefert. Läuft die Verlängerung durch, scheitert aber der Reload des Webservers, ist die Datei neu und der ausgelieferte Schlüssel alt. Die Dateiprüfung meldet nichts, während Besucher bereits eine Zertifikatswarnung sehen. Nur der Blick von außen fängt diesen Fall:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate

-servername ist nicht optional, sobald mehrere Zertifikate auf einer IP-Adresse liegen. Ohne diesen Zusatz bekommen Sie das Standardzertifikat des Servers und prüfen die falsche Domain.

Das Prüfskript

Alle Prüfungen zusammen ergeben ein Skript, das nichts weiter tut als lesen, vergleichen und im Fehlerfall melden. Es braucht curl und, für den Webhook-Weg, jq. Auf minimalen Debian-Installationen fehlt beides:

apt update
apt install -y curl jq
cat > /usr/local/sbin/kh-monitor <<'EOF'
#!/bin/bash
set -u

DISK_WARN="${DISK_WARN:-85}"
INODE_WARN="${INODE_WARN:-85}"
MEM_MIN_MB="${MEM_MIN_MB:-200}"
LOAD_FACTOR="${LOAD_FACTOR:-2}"
CERT_DAYS="${CERT_DAYS:-21}"
UNITS="${UNITS:-ssh nginx}"
STATE_DIR="${STATE_DIRECTORY:-/var/lib/kh-monitor}"

problems=""
add() { problems+="- ${1}"$'\n'; }

notify() {
  printf '%s | %s\n' "$1" "$(printf '%s' "$2" | tr '\n' ' ')"
  if [ -n "${WEBHOOK_URL:-}" ]; then
    printf '%s\n%s' "$1" "$2" | jq -Rs '{text: .}' \
      | curl -fsS -m 10 -o /dev/null -H 'Content-Type: application/json' \
             --data-binary @- "$WEBHOOK_URL"
  fi
  if [ -n "${MAILTO:-}" ]; then
    printf '%s\n' "$2" | mail -s "$1" "$MAILTO"
  fi
}

while read -r pcent target; do
  pcent="${pcent%\%}"
  case "$pcent" in ''|*[!0-9]*) continue ;; esac
  [ "$pcent" -ge "$DISK_WARN" ] && add "Platte ${target} zu ${pcent} Prozent belegt"
done < <(df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)

while read -r ipcent target; do
  ipcent="${ipcent%\%}"
  case "$ipcent" in ''|*[!0-9]*) continue ;; esac
  [ "$ipcent" -ge "$INODE_WARN" ] && add "Inodes auf ${target} zu ${ipcent} Prozent belegt"
done < <(df --output=ipcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)

mem_avail=$(awk '/^MemAvailable:/ { printf "%d", $2 / 1024 }' /proc/meminfo)
[ "${mem_avail:-0}" -lt "$MEM_MIN_MB" ] && add "nur ${mem_avail} MB Arbeitsspeicher verfügbar"

cores=$(nproc)
load15=$(awk '{ print $3 }' /proc/loadavg)
awk -v l="$load15" -v c="$cores" -v f="$LOAD_FACTOR" 'BEGIN { exit !(l > c * f) }' \
  && add "Last im 15-Minuten-Mittel ${load15} bei ${cores} Kernen"

sysstate=$(systemctl is-system-running)
[ "$sysstate" = "running" ] || add "systemd meldet Zustand ${sysstate}"

for unit in $UNITS; do
  systemctl is-active --quiet "$unit" || add "Dienst ${unit} ist $(systemctl is-active "$unit")"
done

for cert in /etc/letsencrypt/live/*/fullchain.pem; do
  [ -r "$cert" ] || continue
  openssl x509 -checkend $(( CERT_DAYS * 86400 )) -noout -in "$cert" >/dev/null 2>&1 \
    || add "Zertifikat ${cert} läuft in weniger als ${CERT_DAYS} Tagen ab"
done

mkdir -p "$STATE_DIR"
now=$(printf '%s' "$problems" | sha256sum | cut -d' ' -f1)
before=$(cat "${STATE_DIR}/last" 2>/dev/null || true)
printf '%s' "$now" > "${STATE_DIR}/last"

if [ -z "$problems" ]; then
  [ -n "${HEARTBEAT_URL:-}" ] && curl -fsS -m 10 -o /dev/null "$HEARTBEAT_URL"
  [ -n "$before" ] && [ "$now" != "$before" ] \
    && notify "Entwarnung $(hostname -s)" "Alle Prüfungen wieder ohne Befund."
  exit 0
fi

[ "$now" = "$before" ] && exit 0
notify "Warnung $(hostname -s)" "$problems"
EOF

Vier Stellen verdienen eine Erklärung.

  • Die Schleifen lesen aus < <( ... ), nicht aus einer Pipe. Eine Pipe verlagert die Schleife in eine Subshell, die dort gesammelten Meldungen wären nach dem done verschwunden. Der Fehler ist tückisch, weil das Skript fehlerfrei durchläuft und nur nie etwas meldet.
  • Die Schwellen lesen sich aus der Umgebung. Sie können jede Grenze für einen einzelnen Aufruf überschreiben. Darauf beruht der Alarmtest weiter unten.
  • Der Zustand wird als Prüfsumme abgelegt. Gemeldet wird nur, wenn sich die Liste der Probleme geändert hat. Sonst schickt Ihnen eine volle Platte alle zehn Minuten dieselbe Nachricht, und Sie schalten die Überwachung nach zwei Tagen ab.
  • Die Zertifikatsschleife läuft ins Leere, wenn kein Let's-Encrypt-Verzeichnis existiert. Das Muster bleibt unaufgelöst, [ -r "$cert" ] schlägt fehl, der Durchlauf wird übersprungen.

Prüfen Sie jetzt, bevor systemd ins Spiel kommt:

chmod 700 /usr/local/sbin/kh-monitor
bash -n /usr/local/sbin/kh-monitor && echo "Syntax ok"
/usr/local/sbin/kh-monitor; echo "Rückgabewert $?"

Auf einem gesunden Server gibt der dritte Befehl außer Rückgabewert 0 nichts aus. Sehen Sie eine Meldung, ist entweder etwas nicht in Ordnung, oder eine Schwelle passt nicht, etwa weil in UNITS ein Dienst steht, den es hier nicht gibt.

Der systemd-Timer

Ein Cronjob täte es auch. Ein Timer hat aber vier konkrete Vorteile: Er startet keine zweite Instanz, solange die erste läuft, er holt einen verpassten Lauf nach einem Neustart nach, seine Ausgabe landet im Journal, und er lässt sich mit einem Befehl abschalten. Die Dienst-Unit braucht keinen [Install]-Abschnitt, weil sie nicht beim Systemstart, sondern vom Timer aktiviert wird.

cat > /etc/systemd/system/kh-monitor.service <<'EOF'
[Unit]
Description=Kurze Zustandspruefung des Servers
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/kh-monitor
EnvironmentFile=-/etc/default/kh-monitor
StateDirectory=kh-monitor
SyslogIdentifier=kh-monitor
Nice=10
IOSchedulingClass=idle
EOF
cat > /etc/systemd/system/kh-monitor.timer <<'EOF'
[Unit]
Description=Fuehrt kh-monitor regelmaessig aus

[Timer]
OnCalendar=*:0/10
RandomizedDelaySec=60
Persistent=true

[Install]
WantedBy=timers.target
EOF

StateDirectory=kh-monitor legt /var/lib/kh-monitor mit passenden Rechten an und setzt die Variable STATE_DIRECTORY, auf die das Skript zurückgreift. Das vorangestellte Minuszeichen in EnvironmentFile=-/etc/default/kh-monitor bedeutet, dass eine fehlende Datei kein Fehler ist. RandomizedDelaySec=60 streut den Startzeitpunkt, Persistent=true holt einen während einer Abschaltung verpassten Lauf beim Hochfahren nach.

Der Timer aktiviert den gleichnamigen Dienst automatisch, eine Unit=-Zeile ist nicht nötig:

systemd-analyze verify /etc/systemd/system/kh-monitor.service
systemd-analyze calendar '*:0/10'
systemctl daemon-reload
systemctl start kh-monitor.service
systemctl enable --now kh-monitor.timer

Erfolgskontrolle:

systemctl list-timers kh-monitor.timer --no-pager
journalctl -u kh-monitor.service -n 20 --no-pager

Die Liste muss eine Zeile mit dem nächsten Ausführungszeitpunkt zeigen. Bleibt sie leer, ist der Timer nicht aktiv. systemd-analyze calendar findet Tippfehler im Zeitausdruck: Es rechnet ihn in eine Normalform um und nennt den nächsten Termin. Einzelheiten zu Unit-Dateien und ihren Fehlerbildern stehen in systemd-Service erstellen.

Der Benachrichtigungsweg

Hier scheitern die meisten selbstgebauten Überwachungen. Der Melder läuft auf dem Server, den er überwacht, fällt also mit ihm aus. Damit meldet er alles zuverlässig außer dem einen Fall, der wirklich zählt.

Die Antwort heißt Totmannschalter: Der Server meldet sich nach jedem erfolgreichen Lauf bei einer Gegenstelle, und bleibt die Meldung aus, schlägt die Gegenstelle Alarm. Das Skript oben tut das, wenn HEARTBEAT_URL gesetzt ist. Netzausfall, hängendes Dateisystem und abgestürztes System sehen für die Gegenstelle gleich aus, und über alle drei wollen Sie Bescheid wissen.

Die Zugangsdaten gehören in eine eigene Datei, nicht ins Skript. Eine Webhook-Adresse ist ein Geheimnis, wer sie hat, kann in Ihrem Namen Nachrichten schicken:

cat > /etc/default/kh-monitor <<'EOF'
WEBHOOK_URL=https://beispiel.example/hooks/xxxxxxxx
HEARTBEAT_URL=https://beispiel.example/heartbeat/xxxxxxxx
UNITS="ssh nginx"
EOF
chmod 600 /etc/default/kh-monitor

Die Anführungszeichen um UNITS sind wichtig: systemd käme auch ohne sie zurecht, aber beim Testlauf weiter unten wird die Datei von der Shell eingelesen, und dort würde nginx ohne Anführungszeichen als Befehl aufgefasst. Tragen Sie nur Units ein, die es auf diesem System gibt, auf Ubuntu 24.04 also ssh.socket statt ssh.

Wer E-Mail bevorzugt, braucht einen Versandweg. Ein vollwertiger Mailserver ist überdimensioniert, ein reiner Weiterleiter genügt:

apt install -y msmtp msmtp-mta bsd-mailx

Konfiguriert wird das in /etc/msmtprc mit den Zugangsdaten eines vorhandenen Postfachs. Die Datei enthält ein Passwort und gehört auf chmod 600, sonst verweigert msmtp den Dienst mit einem Hinweis auf die Dateirechte. Zwei ehrliche Einschränkungen: E-Mail von einer frisch vergebenen Server-IP-Adresse landet häufig im Spam-Ordner oder wird abgewiesen, und eine Nachricht, die Sie erst beim nächsten Blick ins Postfach sehen, ist bei einem Ausfall zu langsam. Für Alarme ist Push-Zustellung die praktischere Wahl.

Den Alarm einmal auslösen

Dieser Schritt ist nicht optional. Erzwingen Sie eine Meldung, indem Sie eine Schwelle für einen einzelnen Aufruf unsinnig setzen:

set -a; . /etc/default/kh-monitor; set +a
DISK_WARN=0 /usr/local/sbin/kh-monitor
rm -f /var/lib/kh-monitor/last

Die Nachricht muss jetzt tatsächlich bei Ihnen ankommen, nicht nur im Journal stehen. Die dritte Zeile löscht den gespeicherten Zustand, damit der Testalarm den nächsten echten Lauf nicht unterdrückt. Ein Nebeneffekt ist Absicht: Schlägt die Zustellung fehl, endet kh-monitor.service mit einem Fehler und taucht in systemctl --failed auf. Ein stiller Benachrichtigungsweg wäre der schlimmste denkbare Fehler in einer Überwachung.

Die Sicht von außen

Der Totmannschalter sagt Ihnen, dass der Server lebt. Er sagt nicht, dass Ihre Website antwortet. Dafür braucht es eine Prüfung von einem anderen Standort aus, mit demselben Muster aus Skript und Timer, nur auf einem zweiten Host:

curl -fsS -m 10 -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com/

Vier Punkte entscheiden über den Wert dieser Prüfung. Erstens: Prüfen Sie den echten Dienst, nicht nur ICMP. Ein Server, der auf Ping antwortet, während der Webserver in einer Endlosschleife hängt, gilt bei einer Ping-Prüfung als gesund. Zweitens: -f lässt curl bei HTTP-Fehlercodes fehlschlagen, ohne diesen Schalter gilt auch eine Fehlerseite als Erfolg. Drittens: Alarmieren Sie erst nach zwei Fehlschlägen in Folge, sonst meldet jede kurze Netzstörung einen Ausfall. Viertens: Ein Aufruf im Minutentakt von derselben Adresse kann in einer Ratenbegrenzung landen oder von einer Sperrsoftware als Angriff gewertet werden; tragen Sie die Adresse des prüfenden Hosts als Ausnahme ein.

Ohne zweiten Server bleibt ein gehosteter Prüfdienst. Kostenlose Angebote prüfen meist im Fünf-Minuten-Takt, Sie erfahren von einem Ausfall also entsprechend verzögert. Für einen einzelnen Server reicht das, und es ist besser als die Alternative, es gar nicht zu erfahren.

Häufige Fehler und Lösungen

Failed to start kh-monitor.service: Unit kh-monitor.service not found.: Nach dem Anlegen oder Ändern einer Unit fehlt systemctl daemon-reload. Zweithäufigste Ursache ist ein falsches Verzeichnis; eigene Units gehören nach /etc/systemd/system/.

The unit files have no installation config: Sie haben systemctl enable kh-monitor.service statt kh-monitor.timer aufgerufen. Der Dienst hat bewusst keinen [Install]-Abschnitt, aktiviert wird der Timer.

code=exited, status=203/EXEC: systemd konnte die Datei nicht ausführen. Entweder stimmt der Pfad in ExecStart nicht, oder chmod 700 fehlt, oder die Datei wurde unter Windows bearbeitet und trägt Zeilenenden mit Wagenrücklauf. Dagegen hilft sed -i 's/\r$//' /usr/local/sbin/kh-monitor.

Syntax error: redirection unexpected: Das Skript wurde mit sh statt bash gestartet. Unter Debian und Ubuntu ist /bin/sh die Shell dash, und die kennt weder < <( ... ) noch += bei Zeichenketten. Die erste Zeile muss #!/bin/bash lauten.

bash: mail: command not found: Es ist kein Mailprogramm installiert. Der Befehl mail stammt je nach System aus bsd-mailx oder mailutils, und die beiden unterscheiden sich in ihren Schaltern. Bleiben Sie bei -s für den Betreff.

curl: (22) The requested URL returned error: 404: Die Webhook-Adresse ist falsch oder auf der Gegenstelle gelöscht. Ohne -f hätte curl den Fehler stillschweigend geschluckt.

curl: (60) SSL certificate problem: certificate has expired: Bei der Prüfung von außen kein Werkzeugfehler, sondern der gesuchte Befund. Gegen den eigenen Webhook deutet dieselbe Meldung auf eine falsche Uhrzeit des prüfenden Servers hin.

Certificate will expire: Reguläre Ausgabe von openssl x509 -checkend mit Rückgabewert 1. Prüfen Sie, ob die Verlängerung noch läuft und der Webserver danach neu geladen wird.

Failed to parse calendar specification: Der Ausdruck hinter OnCalendar= ist ungültig. Testen Sie ihn einzeln mit systemd-analyze calendar, bevor er in die Unit wandert.

Warning: Stopping kh-monitor.service, but it can still be activated by: kh-monitor.timer: Sie haben den Dienst gestoppt statt den Timer. Der Dienst läuft ohnehin nur Sekunden, abzuschalten ist der Timer.

Alarm bei jedem Lauf, obwohl sich nichts geändert hat: Der Zustand wird nicht gespeichert. Prüfen Sie, ob StateDirectory=kh-monitor in der Unit steht und ob /var/lib/kh-monitor/last existiert und beschreibbar ist.

Kein Alarm, obwohl offensichtlich etwas kaputt ist: Lösen Sie die erzwungene Meldung von oben aus. Bleibt auch die aus, liegt es am Zustellweg, nicht an den Prüfungen.

Unterschiede zwischen den vier Systemen

SystemSSH-Unit, die zu überwachen istsquashfs-EinhängepunkteWo die Meldungen landen
Debian 13 (trixie)ssh.service, einzelne Abbilder nutzen ssh.socketin der Regel keinenur Journal, rsyslog fehlt in minimalen Installationen
Debian 12 (bookworm)ssh.servicein der Regel keineJournal, rsyslog je nach Installationsvariante
Ubuntu 24.04 LTSssh.socketmeist vorhanden, Ausschluss nötigJournal und rsyslog
Ubuntu 22.04 LTSssh.servicemeist vorhanden, Ausschluss nötigJournal und rsyslog

Gleich ist auf allen vier Systemen: systemd-analyze, StateDirectory= und RandomizedDelaySec= sind vorhanden, Skript und Unit-Dateien laufen unverändert.

Wann sich der große Werkzeugkasten lohnt

Was Sie jetzt haben, hat klare Grenzen. Es speichert keinen Verlauf, Sie können also nicht nachsehen, ob der Speicherverbrauch seit drei Wochen steigt. Es kennt keine Korrelation über mehrere Hosts. Und es hat weder Eskalation noch Bereitschaftsregeln noch eine Möglichkeit, einen bekannten Alarm für zwei Stunden stummzuschalten.

Genau diese Punkte beantworten die Frage nach dem Umstieg. Ein Metrik- und Dashboard-Aufbau lohnt sich, sobald einer davon zutrifft:

  • Sie betreiben mehr als eine Handvoll Server und wollen sie nebeneinander sehen.
  • Sie brauchen Verlauf und Trends, etwa für Kapazitätsplanung oder um einer Beschwerde über Langsamkeit mit Zahlen zu begegnen.
  • Mehrere Personen teilen sich die Bereitschaft, es braucht also Eskalationsstufen und Stummschaltung.
  • Sie müssen Verfügbarkeit gegenüber Dritten belegen.

Trifft nichts davon zu, ist der Aufbau für einen einzelnen Server meist ein schlechtes Geschäft. Sammelstelle, Datenbank, Dashboard und Exporter belegen zusammen leicht mehrere hundert Megabyte Arbeitsspeicher auf genau der Maschine, deren freien Speicher sie überwachen sollen. Dazu kommen ein weiterer offener Port und eine zweite Software, die aktualisiert werden will. Der entscheidende Punkt bleibt unverändert: Läuft die Sammelstelle auf demselben Server, meldet sie dessen Ausfall genauso wenig wie ein Skript. Beim Umstieg gehört die Sammelstelle auf einen anderen Host, und der Exporter bindet sich an 127.0.0.1 oder ist per Firewall auf deren Adresse beschränkt.

Der Mittelweg funktioniert gut: Timer und Skript bleiben, weil sie den Alarmfall abdecken, der Metrik-Aufbau kommt dazu, wenn Sie Verläufe brauchen. Beides schließt sich nicht aus.

Rückbau

Falls Sie das Verfahren wieder loswerden wollen, sind es fünf Zeilen:

systemctl disable --now kh-monitor.timer
rm -f /etc/systemd/system/kh-monitor.timer /etc/systemd/system/kh-monitor.service
rm -f /usr/local/sbin/kh-monitor /etc/default/kh-monitor
rm -rf /var/lib/kh-monitor
systemctl daemon-reload

Die Abschlusskontrolle

Sechs Prüfungen, die den tatsächlichen Zustand zeigen und nicht den erhofften:

  1. systemctl list-timers kh-monitor.timer --no-pager nennt einen nächsten Ausführungszeitpunkt.
  2. journalctl -u kh-monitor.service --since "1 hour ago" --no-pager zeigt Läufe im erwarteten Abstand.
  3. Ein erzwungener Alarm über DISK_WARN=0 kommt bei Ihnen an, nicht nur im Journal.
  4. Nach einem reboot läuft der Timer weiter, ohne dass Sie etwas tun.
  5. Der Totmannschalter meldet sich: Timer stoppen und abwarten, ob die Gegenstelle Alarm schlägt.
  6. systemctl --failed --no-pager listet nichts auf, insbesondere nicht kh-monitor.service.

Punkt fünf ist der unbequemste und der wichtigste. Eine Überwachung, deren Alarmfall nie eingetreten ist, ist eine Vermutung. Erst wenn Sie den Ausfall absichtlich herbeigeführt und die Nachricht erhalten haben, ist die Kette vom Server bis zu Ihrem Telefon nachweislich vollständig.

Häufige Fragen

Brauche ich für einen einzelnen Root Server wirklich einen Metrikserver mit Dashboard?
In den meisten Fällen nicht. Sammelstelle, Datenbank, Dashboard und Exporter belegen zusammen leicht mehrere hundert Megabyte Arbeitsspeicher auf genau der Maschine, deren freien Speicher sie überwachen sollen, und bringen einen weiteren offenen Port sowie eine zweite zu aktualisierende Software mit. Ein Prüfskript mit systemd-Timer deckt den Alarmfall vollständig ab. Sinnvoll wird der große Aufbau, sobald Sie mehrere Server nebeneinander sehen wollen, Verläufe für die Kapazitätsplanung brauchen, sich mehrere Personen die Bereitschaft teilen oder Sie Verfügbarkeit gegenüber Dritten belegen müssen.
Warum ein systemd-Timer und kein Cronjob?
Beides funktioniert. Der Timer hat vier praktische Vorteile: Er startet keine zweite Instanz, solange die erste noch läuft, er holt mit Persistent=true einen während einer Abschaltung verpassten Lauf beim Hochfahren nach, seine Ausgabe landet über SyslogIdentifier automatisch im Journal, und er lässt sich mit systemctl disable --now kh-monitor.timer in einem Befehl abschalten. Aktiviert wird immer der Timer, nicht der Dienst: Die Dienst-Unit hat bewusst keinen [Install]-Abschnitt.
Meine Überwachung meldet dauerhaft eine volle Platte, obwohl genug frei ist. Woran liegt das?
Fast immer an den Einhängepunkten von snap. Unter Ubuntu 22.04 und 24.04 werden snap-Pakete als schreibgeschützte squashfs-Abbilder eingebunden, und die stehen naturgemäß dauerhaft bei 100 Prozent. Schließen Sie sie aus, also df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay. Dasselbe gilt für die overlay-Einhängepunkte von Docker. Zweite mögliche Ursache: Auf ext4 sind ab Werk fünf Prozent für root reserviert, weshalb df bereits 100 Prozent melden kann, während root noch schreiben kann.
Wie erfahre ich, dass der Server komplett ausgefallen ist?
Nicht über ein Skript auf diesem Server, denn das fällt mit aus. Dafür gibt es zwei Wege, die sich ergänzen. Der Totmannschalter: Der Server meldet sich nach jedem erfolgreichen Lauf bei einer Gegenstelle, und bleibt die Meldung aus, schlägt die Gegenstelle Alarm. Und die Prüfung von außen: ein zweiter Host oder ein gehosteter Prüfdienst, der den echten Dienst abruft, nicht nur ICMP. Kostenlose Prüfdienste arbeiten meist im Fünf-Minuten-Takt, Sie erfahren von einem Ausfall also mit entsprechender Verzögerung.
Welche SSH-Unit muss ich überwachen?
Das unterscheidet sich zwischen den Distributionen. Unter Ubuntu 24.04 wird SSH per Socket-Aktivierung gestartet, dort ist ssh.service im Ruhezustand inactive, obwohl SSH einwandfrei erreichbar ist. Wer dort ssh.service überwacht, bekommt einen Dauerfehlalarm; zu überwachen ist ssh.socket. Auf Debian 12, Debian 13 und Ubuntu 22.04 ist es umgekehrt, dort gilt ssh.service. Einzelne Debian-13-Abbilder haben allerdings ebenfalls ssh.socket aktiv, prüfen Sie das mit systemctl is-enabled ssh.socket.
Wie verhindere ich, dass ich alle zehn Minuten dieselbe Warnung bekomme?
Über eine Zustandsdatei. Das Skript bildet aus der Liste der gefundenen Probleme eine Prüfsumme, legt sie unter /var/lib/kh-monitor/last ab und meldet nur, wenn sich diese Prüfsumme gegenüber dem letzten Lauf geändert hat. Verschwindet ein Problem, kommt einmal eine Entwarnung. Bekommen Sie trotzdem bei jedem Lauf eine Nachricht, fehlt in der Unit die Zeile StateDirectory=kh-monitor, oder die Datei ist nicht beschreibbar.
Das Zertifikat auf der Platte ist gültig, Besucher sehen trotzdem eine Warnung. Wie kann das sein?
Weil die Dateiprüfung und das ausgelieferte Zertifikat zwei verschiedene Dinge sind. Läuft die automatische Verlängerung durch, scheitert aber der Reload des Webservers, ist die Datei neu und der ausgelieferte Schlüssel alt. openssl x509 -checkend meldet dann nichts. Prüfen Sie deshalb zusätzlich von außen mit openssl s_client -connect example.com:443 -servername example.com. Der Zusatz -servername ist nicht optional, sobald mehrere Zertifikate auf einer IP-Adresse liegen.
Wie schalte ich die Überwachung schnell wieder ab, wenn sie stört?
Mit systemctl disable --now kh-monitor.timer, das beendet den Timer sofort und verhindert, dass er beim nächsten Start wiederkommt. Als Notbremse verhindert systemctl mask kh-monitor.service zusätzlich jeden Start von Hand, zurückgenommen mit systemctl unmask. Vollständig entfernt wird das Verfahren durch Löschen der beiden Unit-Dateien, des Skripts unter /usr/local/sbin/, der Datei /etc/default/kh-monitor und des Verzeichnisses /var/lib/kh-monitor, gefolgt von systemctl daemon-reload. Bestehende Konfiguration wird dabei nicht angefasst, weil ausschließlich neue Dateien angelegt wurden.

Monitoring systemd Linux Debian Ubuntu Root Server Serverüberwachung Bash