Ein Serverabsturz, ein Rabe und ein Hufeisen

Inhaltsverzeichnis

Ein Serverabsturz, ein Rabe und ein Hufeisen

Diese Woche hatte kein einzelnes großes Ereignis, sondern viele kleine — und einen Moment, in dem ich meinen eigenen Produktivserver für ein paar Minuten komplett lahmgelegt habe, weil ich es zu eilig hatte. Dazwischen: ein E-Reader, der jetzt selbstständig Bücher und Schlafbildschirm-Hintergründe holt, eine Telegram-Brücke für meinen Matrix-Messenger, der Abschluss einer Fantasy-Heftroman-Serie, ein ordentlich aufgeräumter Server und fünf neue Funktionen in der App meiner Frau. Der rote Faden, der sich durch fast alles zieht: Vieles, was fehlte, war schon längst gebaut — es musste nur noch gefunden oder eingeschaltet werden.

Der Serverabsturz, den ich mir selbst eingebrockt habe

Die ehrlichste Geschichte der Woche zuerst. Ich habe mein selbstgebautes ERP-System saasERP produktiv auf einem eigenen Server in Betrieb genommen — zwei neue Subdomains, TLS-Zertifikate, ein Docker-Stack aus vier Containern. Der Rollout selbst war unspektakulär: Weil nur zwei von den offiziell für das Projekt vorgesehenen drei Subdomains existierten, bekam jede der beiden Web-Apps einen internen Pfad-Proxy zu ihrem eigenen Backend spendiert, statt eine dritte Domain nachzufordern — same-origin, kein Cross-Origin-Ärger.

Danach wurde es unschön. Beim ersten Test meldete die App bei Login und Registrierung nur “Seite nicht gefunden” — ich hatte in der Konfiguration aus Versehen einen doppelten Pfad erzeugt, weil die App selbst schon einen Teil des Pfads anhängt, den ich zusätzlich vorne mitgegeben hatte. Beim Versuch, das schnell zu fixen, habe ich einen kompletten Container-Neubau ohne jeden Cache direkt auf dem Server gestartet — und der Server hatte laut eigener Doku chronisch nur wenige hundert Megabyte freien Arbeitsspeicher. Ergebnis: Der Server war für mehrere Minuten komplett unerreichbar. Kein Ping, kein SSH, kein HTTP — nicht nur mein neues ERP, sondern alle rund dreißig Dienste auf derselben Maschine. Ein Reboot über die Server-Konsole des Hosters war nötig.

Nach dem Neustart kamen alle Container automatisch wieder hoch — ein kleiner Erfolg für sich, dass die restliche Infrastruktur diese ungeplante Bewährungsprobe klaglos übersteht. Beim erneuten Anlauf, diesmal vorsichtiger, fand sich noch ein zweiter, subtilerer Fehler: Meine ursprüngliche Lösung nutzte einen verschachtelten Fallback-Wert in der Docker-Compose-Konfiguration (“nimm Wert A, sonst Wert B”) — und die auf dem Server installierte ältere Docker-Compose-Version löst solche Verschachtelungen nicht sauber auf, sondern hängt einen kaputten Rest an den Wert an. Hätte denselben Fehler erneut verursacht, nur schwerer zu finden. Behoben durch Verzicht auf die Verschachtelung zugunsten zweier expliziter, einfacher Werte.

Die eigentliche Lehre kam danach: Statt ein zweites Mal direkt auf dem fragilen Server zu bauen, habe ich die beiden betroffenen Container-Images lokal (mit reichlich Arbeitsspeicher-Puffer) neu gebaut, als Datei exportiert, übertragen und auf dem Server nur noch geladen und neu gestartet — der Server musste kein einziges Mal mehr selbst kompilieren. Ende-zu-Ende über die echte Domain verifiziert: Ein Login-Versuch liefert jetzt korrekt “falsche Zugangsdaten” statt “Seite nicht gefunden”. Die Lehre fürs nächste Mal: Größere oder parallele Image-Builds nie direkt auf einem speicherknappen Produktivserver anstoßen.

E-Reader: Bücher, Schlafbildschirme, und ein unsichtbarer Bug

Mein selbstgebauter E-Ink-Reader bekam einen neuen Menüpunkt: einmal drücken, WLAN geht an, das Gerät holt sich neue eBooks von meinem Automatisierungsserver, und schaltet danach wieder ab. Backend (zwei neue Webhook-Workflows: eine Datei-Liste, ein Download-Endpunkt) und Firmware in einer Session gebaut und Ende-zu-Ende getestet — Datei hochladen, Liste abrufen, herunterladen, Byte-für-Byte-Vergleich.

Zwei Fallstricke dabei, beide mit demselben Fix lösbar: Ein Webhook im Antwort-Modus “letzter durchlaufender Knoten” liefert bei null Treffern einen Serverfehler statt eines leeren Ergebnisses, und im Datei-Download-Modus reicht derselbe Antwort-Modus Binärdaten gar nicht erst durch. Beides behoben mit einem expliziten Antwort-Baustein statt der impliziten Automatik — ein gutes Beispiel dafür, dass “Standardverhalten” bei No-Code-Werkzeugen gerade in Randfällen überrascht. Das Abschalten des WLANs danach brauchte dagegen keine einzige neue Zeile Code: Die Firmware hatte für alle WLAN-Aktivitäten längst ein etabliertes Aufräum-Muster, dem ich einfach gefolgt bin.

Der erste echte Hardware-Test brachte dann einen Bug, der lange unsichtbar blieb: Die App meldete “1 neues Buch”, der Zielordner blieb aber leer. Der Verdacht fiel zuerst auf die eBook-Datei selbst — unabhängig heruntergeladen und als ZIP/EPUB validiert, technisch einwandfrei. Die echte Ursache lag tiefer: Der Server liefert zu jeder Datei einen sprechenden, aus dem vollen Pfad zusammengesetzten Namen, damit man erkennt, wozu ein Buch gehört. Die Firmware kürzt Dateinamen aus Speichergründen auf eine feste Bytegrenze — ohne Rücksicht auf die Dateiendung. Bei einem hinreichend langen Namen fiel der Schnitt genau vor “.epub”. Die Datei war korrekt heruntergeladen und gespeichert, nur eben ohne erkennbare Endung — und der Datei-Browser zeigt aus gutem Grund nur Dateien mit bekannter Endung an. Der Ordner war nie wirklich leer, nur eine unsichtbare Datei drin. Fix: Beim Kürzen wird jetzt geprüft, ob die echte Endung noch vorhanden ist, und notfalls mit weniger Namens-Spielraum neu gekürzt.

Direkt im Anschluss eine dritte Erweiterung: Cover-Grafiken aus demselben Freigabeordner werden jetzt automatisch mitgeholt, passend zugeschnitten und landen als Schlafbildschirm-Hintergründe auf dem Gerät. Auch hier brauchte die Geräte-Seite keinen neuen Code — nur ein bestehender Ordner-Mechanismus wurde mit neuem Inhalt gefüttert. Server-seitig wollte ich die Größenanpassung zunächst über einen Kommandozeilen-Aufruf erledigen — bis mein Automatisierungsserver das verweigerte: Er läuft bewusst auf einem gehärteten Basis-Image, das Kommandozeilen-Ausführung aus Workflows heraus standardmäßig deaktiviert. Das wollte ich nicht global aufweichen, nur für ein Wallpaper-Feature. Stattdessen gibt es im selben Werkzeug einen eingebauten Bildbearbeitungs-Baustein, der alles rein über Binärdaten im Workflow erledigt, ganz ohne Shell-Zugriff — am Ende die robustere Lösung, weil keine temporären Dateien auf der Platte landen.

Ein Messenger, der jetzt auch Telegram spricht

Mein selbstgehosteter Matrix-Server (die offene Alternative zu WhatsApp, über die Familie und ich schon länger chatten) bekam eine Brücke zu Telegram — alle Chats, Kanäle und Gruppen jetzt direkt im gewohnten Client, ohne die Telegram-App zu öffnen. Vor dem Einrichten stand aber ein Realitätscheck an: Der VPS, auf dem Matrix läuft, hat nur knapp 4 GB RAM und trägt gleichzeitig ein Dutzend andere Dienste — praktisch kein Speicher mehr frei, kein Sicherheitsnetz. Bevor also ein zusätzlicher Dienst dazukam, der im schlimmsten Fall den Speicher überlastet und damit auch alle anderen, unabhängigen Dienste auf derselben Maschine ins Trudeln bringt, habe ich zuerst eine 2-GB-Swap-Datei als Puffer eingerichtet.

Die eigentliche Brücke lief dann unspektakulär durch: eigene Datenbank direkt in der schon laufenden Instanz untergebracht statt eines neuen Containers, Zugriffsrechte eng gefasst. Ein kleines Stolperchen beim ersten Start — zwei Dienste unter unterschiedlichen Nutzerkennungen konnten dieselbe Konfigurationsdatei nicht lesen, einmal Dateiberechtigung angepasst, dann lief alles. Login getestet: Telefonnummer und Bestätigungscode einmal eingegeben, die Brücke hat automatisch alle Telegram-Chats als eigene Räume angelegt, während der Rest des Servers ununterbrochen weiterlief.

Aether-Saga: Wenn die Automation ausfällt, macht man es eben selbst

Nebenbei ging ein kreatives Projekt zu Ende: Heft 2 meiner Fantasy-Heftroman-Serie. Die dafür geplante automatisierte Lektorats-Pipeline hing seit Stunden auf einem kaputten Proxy fest — sieben gescheiterte Versuche gegen denselben Endpunkt. Statt weiter zu warten, das Ganze einmal komplett von Hand durchgezogen: Rohfassung lektoriert, um mehr als das Doppelte erweitert (von 13.500 auf rund 30.000 Wörter), und dabei eine echte Logiklücke gefunden und geschlossen — zwei Nebenfiguren tauchten in der Rohfassung plötzlich an einem neuen Ort auf, ohne dass ihr Weg dorthin je erzählt wurde. Beim Gegenlesen zusätzlich einen Kontinuitätsfehler entdeckt: Eine Figur, die eigentlich erst zwanzig Hefte später eingeführt werden sollte, war versehentlich zu früh aufgetaucht — durch eine neue Nebenfigur ersetzt, damit die spätere, groß angelegte Einführung nicht vorweggenommen wird.

Am Ende ging die fertige Datei direkt an meinen E-Reader-Sync — der Kreis zu den ersten beiden Absätzen dieser Woche schließt sich damit fast von selbst.

Aufräumen: 51 Gigabyte, die niemand mehr brauchte

Zum Wochenschluss ein Blick auf den Produktivserver, der schon länger mit vollem Speicher aufgefallen war: 25,8 Gigabyte ungenutzte Docker-Images (Reste längst abgeschalteter Projekte — eine frühere Mailserver-Lösung, ein Dokumentenmanagement-System, eine lokale KI-Modell-Laufzeitumgebung, eine alte ERP-Vorgängerversion) und 12 Gigabyte reiner Build-Cache, beide risikofrei entfernt. Ergebnis: Festplattennutzung von 73 % auf 27 %. Bewusst nicht angefasst: vierzig von fünfundvierzig Docker-Volumes ohne laufenden Dienst — die könnten noch echte Daten aus abgeschalteten Projekten enthalten, und das verdient eine einzelne Prüfung statt eines pauschalen Löschens. Lokal, wo nur Testdaten liegen, durfte großzügiger aufgeräumt werden: Images von 15 auf 1,5 Gigabyte.

Hufpflege: Fünf Wünsche aus dem echten Alltag

Die App für Hufpfleger, die meine Frau in der Beta nutzt, bekam fünf konkrete Verbesserungen aus dem echten Nutzungsalltag: Ein Antippen eines Termins führt jetzt direkt zur Kundenseite. Das Dashboard zeigt die Einnahmen des laufenden Monats direkt neben denen des Vormonats. Drei getrennte Einnahme-Felder pro Termin (Anfahrt, pro Tier, Trinkgeld) wurden zu einem einzigen Summenfeld zusammengefasst — inklusive einer Datenbank-Migration, die bestehende Werte korrekt zusammenrechnet, statt sie zu verwerfen. Termine lassen sich jetzt einem bestimmten Tier zuordnen, direkt mit Befund und durchgeführten Maßnahmen — öffnet man den Termin später erneut, sind Befund, Maßnahmen und zugehörige Fotos wieder da. Und ein neues App-Icon: ein Hufeisen in Gold auf Waldgrün, aus einer einzigen Bildquelle automatisch für Android und (neu ergänzt) für die Web-Version erzeugt.

Zwei Dinge am Rand: Beim Zusammenführen der Änderungen kam mir ein zweites Gerät in die Quere, das unabhängig schon eine Navigationsverbesserung eingespielt hatte — beide Änderungsstränge ließen sich sauber zusammenführen. Und beim genaueren Hinsehen fiel auf, dass eine bestimmte Datenbank-Update-Methode zwingend alle Felder einer Zeile mitgegeben haben will — sonst hätte mein neuer Code stillschweigend bereits vorhandene Notizen an Behandlungseinträgen gelöscht. Rechtzeitig bemerkt, bevor es passiert ist.

Am selben Tag noch ein kleiner Umweg auf dem eigenen Desktop: Ein neu installierter Wayland-Compositor samt zugehöriger Oberfläche lief nach der Installation nicht richtig — die Oberfläche war zwar da, aber nie aktiviert, das System startete stattdessen mit einer kahlen Minimalkonfiguration. Einmal aktiviert, lief alles sofort, inklusive der deutschen Tastaturanzeige, die vorher fälschlich auf Englisch wirkte.

Fazit

Vier von diesen sechs Geschichten laufen auf dieselbe Erkenntnis hinaus: Die Lösung war schon da, sie musste nur gefunden, eingeschaltet oder in den richtigen Ordner gelegt werden — das WLAN-Aufräum-Muster, der Datei-Löschen-Mechanismus, der Schlafbildschirm-Ordner, die Dank-Material-Shell auf dem eigenen Desktop. Die Ausnahme war der Serverabsturz, und der hatte seine eigene Lektion: Der schnelle Weg ist nicht immer der sichere, besonders nicht auf einer Maschine, die keinen Puffer mehr hat.