Inhaltsverzeichnis
- 1 Eine systematische Fehlersuche unter Linux
- 1.1 Das Symptom
- 1.2 Warum das schwer zu finden ist
- 1.3 Die Werkzeugkiste
- 1.3.1 1. ss – Momentaufnahme der Netzwerkverbindungen
- 1.3.2 2. Wiederholte Messung in eine Datei loggen
- 1.3.3 3. /proc/net/dev – Bandbreite ganz ohne Zusatzsoftware
- 1.3.4 4. nethogs – Bandbreite pro Prozess, live
- 1.3.5 5. tcpdump – der Blick auf die Pakete selbst
- 1.3.6 6. execsnoop (aus bcc-tools) – der eigentliche Durchbruch
- 1.3.7 7. journalctl – die Systemlogs
- 1.4 Der Weg zur Lösung – Schritt für Schritt
- 1.4.1 Runde 1: Der erste falsche Verdächtige
- 1.4.2 Runde 2: Bandbreite bestätigt das Muster objektiv
- 1.4.3 Runde 3: nethogs zeigt wechselnde PIDs desselben Musters
- 1.4.4 Runde 4: Den Elternprozess finden
- 1.4.5 Runde 5: Der zweite, hartnäckigere Verdächtige
- 1.4.6 Runde 6: execsnoop bringt die endgültige Klarheit
- 1.4.7 Die Auflösung
- 1.5 Die eigentliche Lösung
- 1.6 Checkliste: So gehst du bei periodischer CPU-/Netzwerklast vor
- 1.7 Randnotiz: Der Systemabsturz, der keiner war
- 1.8 Fazit
Eine systematische Fehlersuche unter Linux
Wie ich zwei Tage lang einem Geist auf meinem Server nachgejagt bin – und was jeder daraus lernen kann, der ein ähnliches Rätsel lösen will.
Das Symptom
Alles fing harmlos an: Alle 10 Sekunden wurde meine Internetleitung für etwa 3 Sekunden spürbar beansprucht. Kein Drama auf den ersten Blick – aber es passierte zu regelmäßig, um Zufall zu sein. Downloads, Browser-Tabs, Cloud-Sync – das alles verursacht unregelmäßigen Traffic. Ein Takt wie eine Uhr dagegen ist fast immer ein Zeichen für einen Prozess, der in einer Schleife hängt.
Später kam ein zweites Symptom dazu: Die CPU sprang alle 5–7 Sekunden auf 20 % Last. Und am Ende musste ich sogar feststellen, dass ein hartes kill -9 auf einen völlig unbeteiligten Prozess meinen Rechner zu einem Totalabsturz brachte (dazu mehr in der Randnotiz am Ende).
Mein System: CachyOS (Arch-basiert), Docker mit gut zwei Dutzend Containern, mehrere selbst gehostete KI-Tools (Ollama, Open WebUI, LM Studio), diverse Node/Python-Projekte – ein typisches Homelab-Setup, in dem sich ein Übeltäter hervorragend verstecken kann.
Warum das schwer zu finden ist
Ein Prozess, der alle paar Sekunden kurz aktiv wird, hinterlässt keine dauerhafte Spur. top oder der Systemmonitor zeigen einen Schnappschuss – trifft man nicht genau den Moment der Aktivität, sieht alles normal aus. Genau das macht solche Fehler tückisch: Sie sind nicht selten, sondern kurz. Man braucht Werkzeuge, die über die Zeit mitschreiben, nicht nur den aktuellen Moment zeigen.
Die Werkzeugkiste
Hier die Tools, die sich in der Reihenfolge ihrer Aussagekraft als nützlich erwiesen haben – vom Groben zum Feinen:
1. ss – Momentaufnahme der Netzwerkverbindungen
bash
sudo ss -tunp | sort
Zeigt alle offenen Verbindungen mit Prozessname und PID. Gut für den ersten Überblick, aber eine Einzelmessung sagt nichts über Wiederholung aus.
2. Wiederholte Messung in eine Datei loggen
Da man den richtigen Moment nur schwer live erwischt, sollte man über Zeit mitschreiben statt live zuzuschauen:
bash
for i in (seq 1 20); date +%T; ss -tp | grep -E "verdaechtig"; sleep 2; end > netlog.txt
(Hinweis für fish-Shell-Nutzer: Die Syntax unterscheidet sich von bash. Es gibt keine \|-Escapes und keine do...done-Blöcke, sondern for ... ; end.)
3. /proc/net/dev – Bandbreite ganz ohne Zusatzsoftware
bash
for i in (seq 1 30); date +%T; grep enp10s0 /proc/net/dev; sleep 1; end
Das zeigt die kumulierten Byte-Zähler des Netzwerk-Interfaces. Steigt der Zähler alle paar Sekunden um mehrere Megabyte, ist das ein objektiver Beweis für periodischen Traffic – unabhängig davon, welcher Prozess dahintersteckt.
4. nethogs – Bandbreite pro Prozess, live
bash
sudo nethogs -t enp10s0 > nethogs.txt
Der -t-Schalter (Trace-Modus) ist der Schlüssel: Er schreibt fortlaufend in die Konsole statt das Terminal live neu zu zeichnen – dadurch lässt sich die Ausgabe in eine Datei umleiten und in Ruhe analysieren, statt sie live wegklicken zu müssen.
5. tcpdump – der Blick auf die Pakete selbst
bash
sudo tcpdump -i enp10s0 -nn -q > tcpdump.txt
Zeigt jedes einzelne Paket mit Quelle, Ziel und Größe. Gut, um zu bestätigen, dass und wohin Daten fließen – aber ohne Prozessnamen.
6. execsnoop (aus bcc-tools) – der eigentliche Durchbruch
bash
sudo pacman -S bcc bcc-tools
sudo execsnoop > execsnoop.txt
Das ist ein eBPF-basiertes Tool, das jeden einzelnen neu gestarteten Prozess in Echtzeit protokolliert, inklusive PID, Elternprozess und vollständigem Kommandozeilenaufruf. Bei einem Prozess, der sich ständig neu startet (statt dauerhaft zu laufen), ist das die mit Abstand direkteste Methode: Man sieht sofort, wer sich alle paar Sekunden wiederholt.
Weitere nützliche Tools aus demselben Paket, falls execsnoop nicht reicht: opensnoop (welche Datei öffnet wer), tcpconnect (wer baut TCP-Verbindungen auf, mit Prozessname), filetop/biotop (Festplatten-I/O pro Prozess).
7. journalctl – die Systemlogs
bash
journalctl -u <servicename> -f
Sobald ein Verdächtiger als systemd-Service identifiziert ist, zeigt das Live-Log oft sofort die Fehlermeldung, die den Neustart-Loop auslöst.
Praxistipp: Journal-Logs überleben nicht automatisch einen Neustart, wenn die Persistenz nicht aktiviert ist. Das lohnt sich präventiv einzurichten:
bash
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
Der Weg zur Lösung – Schritt für Schritt
Runde 1: Der erste falsche Verdächtige
ss -tunp zeigte diverse Dauerverbindungen (LM Studio Link-Feature, Browser-Tabs). Diese blieben aber über mehrere Messungen mit demselben lokalen Port bestehen – ein klares Zeichen, dass es sich um normale Keep-Alive-Verbindungen handelte, nicht um den gesuchten Wiederholungstäter.
Runde 2: Bandbreite bestätigt das Muster objektiv
Die /proc/net/dev-Messung zeigte eindeutige Sprünge von 8–10 MB innerhalb einzelner Sekunden, in einem wiederkehrenden Rhythmus. Damit war klar: Es handelt sich nicht um Einbildung, sondern um echten, spürbaren Traffic.
Runde 3: nethogs zeigt wechselnde PIDs desselben Musters
Der Trace-Modus von nethogs zeigte eine Kette aus npm start → npm run build → npx vite build → esbuild, die sich alle paar Sekunden mit neuer PID wiederholte. Neue PID bei gleichem Befehl bedeutet: Der Prozess stirbt und wird neu gestartet – ein Restart-Loop.
Runde 4: Den Elternprozess finden
bash
ps -eo pid,ppid,cmd | grep -E "verdaechtiger_prozessname"
Damit ließ sich zurückverfolgen, wer diese Kette startet. Zunächst wurde ein Konfigurationsproblem bei uvx open-webui als Mitverursacher identifiziert (ein systemd --user-Service, der wegen eines fehlgeschlagenen Python-Paket-Builds ständig neu startete) und behoben. Die Netzwerklast sank spürbar, verschwand aber nicht vollständig.
Runde 5: Der zweite, hartnäckigere Verdächtige
Nach dem ersten Fix blieb ein Rest-Muster: CPU-Last alle 5–7 Sekunden, die sogar bei gekapptem Internet kurzzeitig bestehen blieb. Das war ein wichtiger diagnostischer Hinweis: Ein Prozess, der bei fehlendem Netz trotzdem CPU verbraucht, versucht wahrscheinlich, eine Verbindung aufzubauen und scheitert dabei wiederholt (DNS-Auflösung, Verbindungsversuch, Timeout) – reines Netzwerk-Monitoring reicht dann nicht mehr aus, weil das eigentliche Problem tiefer sitzt.
Runde 6: execsnoop bringt die endgültige Klarheit
Der Einsatz von execsnoop zeigte unmissverständlich denselben Kreislauf, diesmal einem konkreten Verzeichnis zugeordnet:
npm start → npm run build → npx vite build → vite build → esbuild
mit Pfad /opt/openhamclock. Ein systemd-System-Service (openhamclock.service) war der Übeltäter.
Die Auflösung
bash
journalctl -u openhamclock.service -f
zeigte die Ursache glasklar:
[FATAL] Uncaught exception: Cannot find module 'rss-parser'
...
systemd[1]: openhamclock.service: Scheduled restart job, restart counter is at 771.
Ein Node.js-Projekt (eine Amateurfunk-„Weltuhr“-Anwendung) hatte ein fehlendes npm-Paket. Die package.json enthielt ein prestart-Skript, das bei jedem Start automatisch neu baute (npm run build). Der Server crashte danach sofort wegen des fehlenden Moduls. Die systemd-Unit war mit Restart=on-failure konfiguriert – ohne Begrenzung, wie oft das passieren darf. Ergebnis: Ein kompletter Build-Zyklus (inklusive Vite-Bundling und esbuild-Kompilierung) alle 4–5 Sekunden, seit vermutlich Tagen, unbemerkt, mit Restart-Zähler 771 und weiter steigend.
Die eigentliche Lösung
Kurzfristig:
bash
sudo systemctl disable --now openhamclock.service
Langfristig (den Service reparieren):
bash
cd /opt/openhamclock
sudo -u <service-user> npm install rss-parser
sudo systemctl enable --now openhamclock.service
Und ganz wichtig für die Zukunft – jede eigene systemd-Unit mit Restart-Logik sollte ein Limit bekommen, damit ein kaputter Service nicht endlos in eine Schleife läuft:
ini
[Unit]
StartLimitIntervalSec=60
StartLimitBurst=5
Damit gibt systemd nach 5 Fehlschlägen innerhalb einer Minute auf, statt das über Tage unbemerkt zu wiederholen.
Checkliste: So gehst du bei periodischer CPU-/Netzwerklast vor
- Bestätige das Muster objektiv, bevor du tief gräbst.
/proc/net/devin einer Schleife mitschreiben reicht schon, um Einbildung von echtem Problem zu unterscheiden. - Logge über Zeit, schau nicht live zu. Einzelmessungen verpassen kurze Ereignisse fast immer. Jedes Tool, das kann, sollte in eine Datei umgeleitet werden (
nethogs -t,tcpdump > file,for-Schleifen mitdateundsleep). - Trenne Netzwerk- von Prozessanalyse.
ss/tcpdumpzeigen Verbindungen, aber keine Prozessnamen zuverlässig über Zeit.nethogs/execsnoopverbinden beides. - Achte auf wechselnde PIDs bei gleichem Befehl. Das ist das verlässlichste Signal für einen Restart-Loop, nicht für einen normal laufenden Dauerprozess.
- CPU-Last, die auch ohne Netzwerk bestehen bleibt, deutet auf einen fehlschlagenden Verbindungsversuch hin (DNS, TLS-Handshake, Timeout-Handling), nicht auf reinen Datentransfer.
- execsnoop (eBPF) ist der schnellste Weg zur Wahrheit, wenn der Verdacht auf einen sich wiederholt neu startenden Prozess fällt. Ein einziger 60-Sekunden-Mitschnitt reicht meist aus.
- Sobald ein systemd-Service identifiziert ist, sofort ins Journal schauen. Die Fehlermeldung, die den Crash-Loop auslöst, steht dort fast immer im Klartext.
- Aktiviere Journal-Persistenz präventiv. Ohne sie sind Logs nach einem Neustart oder Absturz unwiederbringlich weg – genau dann, wenn man sie am dringendsten bräuchte.
- Gib jedem selbst geschriebenen systemd-Service ein Restart-Limit.
Restart=on-failureohneStartLimitBurst/StartLimitIntervalSeckann jahrelang unbemerkt in einer Schleife laufen und dabei CPU, Strom und im schlimmsten Fall Bandbreite verschwenden.
Randnotiz: Der Systemabsturz, der keiner war
Während der Fehlersuche führte ein einfaches kill -9 auf einen npm-Prozess zu einem sofortigen Totalabsturz des gesamten Systems. Ein normaler kill -9 auf einen einzelnen User-Prozess sollte niemals einen kompletten Absturz auslösen – das deutet eher auf ein unabhängiges, zufällig gleichzeitig auftretendes Problem hin (etwa an der Schnittstelle von GPU-Treiber und Wayland-Compositor). Da zu diesem Zeitpunkt keine persistente Journal-Speicherung aktiv war, ließ sich die Ursache im Nachhinein nicht mehr rekonstruieren – ein weiteres Argument dafür, Journal-Persistenz von Anfang an einzurichten.
Fazit
Zwei Tage Fehlersuche für ein einziges vergessenes npm install – das klingt nach viel Aufwand für eine kleine Ursache. Aber der Weg dorthin zeigt ein universelles Muster: Periodische, kurze Lastspitzen lassen sich nicht durch Zuschauen finden, sondern nur durch systematisches Mitschreiben über die Zeit. Wer sich von grob (Netzwerk-Byte-Zähler) zu fein (eBPF-Prozessverfolgung) vorarbeitet, findet praktisch jeden Übeltäter dieser Art – ob es nun ein kaputtes npm-Paket, ein hängender Modell-Ladevorgang oder ein fehlkonfigurierter systemd-Service ist.
Verwendete Tools: ss, nethogs, tcpdump, bcc-tools (execsnoop), journalctl, systemctl. Getestet auf CachyOS (Arch Linux) mit der fish-Shell – die gezeigten Befehle sind teils fish-spezifisch (for i in (seq ...); ...; end statt for i in $(seq ...); do ...; done).














