knuspermagier.de
Hallo. Ich bins! Philipp!

Homelab-Autodeployment

In den letzten Wochen habe ich – ja, gemeinsam mit Claude und Codex – an meinem Deployment im Homelab (und darüber hinaus) gearbeitet. Ursprünglich hatte ich mich ja mit Komodo für eine etablierte Software entschieden, aber nach ein bisschen Rumspielen damit erschien mir das alles zu fett und unübersichtlich. Ich warf es also wieder weg und baute meine eigenen übersichtlichen Skripte.

Jetzt, nachdem ich alle Zahnräder beisammen habe, erinnere ich mich wieder an das völlig überladene und komplizierte UI von Komodo und kann immerhin die ein oder andere Funktion gut nachvollziehen. Alles daran war vielleicht doch nicht übertrieben, ich musste es nur erst einmal nachbauen, um es zu verstehen.


Meine Zahnräder sehen also zusammengefasst so aus:

1) Infra-Repo

In meinem selbst gehosteten Forgejo habe ich für jeden Server ein Repository, in dem beschrieben ist, was dort alles als Docker-Container läuft. Für zu Hause ist z. B. infra-home. Das besteht aus zwei Teilen:

  • server/ für die Konfiguration der globalen Sachen – Caddy, der Backup-Service etc.
  • stacks/ für die einzelnen Stacks. Ein Stack ist dabei jeweils eine compose.yaml, in der die Container beschrieben sind.
file://lefpchzfehec6vsu

Secrets manage ich aktuell noch nicht über git, die werden manuell auf dem Server in .env-Dateien abgelegt, die von der compose.yaml referenziert werden.

2) Woodpecker CI

Ich nutze weiterhin Woodpecker. Wenn ein Infra-Repo gepusht wird, wird hier geschaut, ob alles passt. Es werden ein paar Regeln gecheckt, zum Beispiel, dass ich in den compose.yaml-Dateien keine Secrets direkt reingeschrieben habe und dass ich bei nicht selbstgebauten Containern keine :latest-Tags benutze. (Nervig, aber wer schon mal nach einem random docker compose pull erst mal eine halbe Stunde ein postgres-Update fixen musste, wird es verstehen.)

Diese Überprüfung ist etwas, das ich niemals selbst gebaut hätte, wenn ich alles selbst gemacht hätte – denn wer hat Zeit für so etwas, wenn man sich privat etwas baut? Da die KI das aber einfach für sinnvoll fand, nahm ich das natürlich gerne mit.

3) nana

Als ich mich dagegen entschied, Komodo zu verwenden, ließ ich nana bauen. Das ist ein kleiner Daemon, der einfach nur auf einen Webhook reagiert, das Git-Repository neu auscheckt und guckt, in welchen Stacks sich was geändert hat, um diese neu zu starten.

file://tqacztpye6sawyas

Am Anfang hatte ich also in der .woodpecker.yaml der infra-Repos immer noch einen curl auf den entsprechenden nana-Server.

4) Eigene Services

Natürlich hoste ich ja nicht nur externen Kram, sondern zu einem großen Teil auch selbst entwickelte Sachen. Die sollen natürlich auch entsprechend möglichst leicht ihren Weg auf den Server finden.

Um das zu vereinfachen, habe ich darauf geachtet, dass sich jedes Projekt in der Woodpecker CI zu einem Docker-Container bauen lässt. Egal, ob Node.js-, Go- oder Laravel-Projekt, das Endergebnis ist das gleiche.

Nun stand ich vor einem kleinen Problem: Wie sage ich dem Server, dass er bitte neu deployen soll, sobald das Paket fertig ist, ohne dass ich jedem Projekt mitteilen muss, auf welchem Server es gehostet ist? Also, es braucht ja nur einen curl an Nana, aber an welches?

Um der Pipeline das mitzuteilen, hätte ich es hart in die .woodpecker.yml schreiben müssen oder es über eine Variable in Woodpecker pflegen. Das ist ja aber wirklich Käse.

Ich brauchte also doch noch ein übergreifendes Tool.

5) Waltraut

Glücklicherweise hatte ich ja bereits vor Monaten mal eine Laravel-App gebaut, in der ich alle meine laufenden Dienste (manuell) eingetragen habe, die sich zum Beispiel um Health-Checks kümmert und auch Error-Logs über die eingebaute Sentry-Schnittstelle einsammelt.

file://ltwfag4swzeakwdr

Statt die Services manuell anzulegen, kann man hier nun also eine URL eines infra-Repos angeben und alles wird automatisch importiert. Zusätzlich kennt Waltraut die URL der entsprechenden nana-Instanz und kann dort klingeln, wenn etwas los ist.

Aber wie bekommt Waltraut nun mit, dass sich etwas verändert? Zum einen bestand die KI darauf, dass es sinnvoll ist, Forgejo einfach alle fünf Minuten zu pollen, falls ein Webhook mal fehlschlägt. Ja, okay.

Da ich auf so etwas allerdings nicht warten kann, ehrlich gesagt, und mir selbst eine Minute Wartezeit bis zum nächsten Poll zu viel wäre, musste natürlich auch der Webhook her.

Ich baute also erst mal in jede .woodpecker.yml einen entsprechenden curl ein, der mitteilte: „Hier, das Image von Projekt foobar ist neu!“ Waltraut schaute dann, auf welchem Server das Image benutzt wird, und sagte der nana-Instanz Bescheid.

Das funktionierte gut, war aber nervig. Ich hasse jede Zeile zu viel in der .woodpecker.yml, vor allem Zeilen, die ich in jedes Projekt reinkopieren muss. Das muss besser gehen.

Irgendwann kam mir die Idee, die globalen Hooks von Forgejo zu benutzen.

Man kann einfach immer dann einen Webhook triggern, wenn ein neues Docker-Image gepusht, also – um in Forgejo-Speak zu bleiben – ein neues Package erstellt wurde. Ich konnte also den ganzen Kram aus der CI-Konfiguration wieder entfernen.

Flow

Wenn ich mir nun also überlege, dass ich jetzt Tool XYZ in meinem Homelab installieren will, könnte ich so vorgehen:

  • Ich sage der KI, dass sie im infra-home-Repo einen Stack für foobar anlegen und mir eine beispielhafte .env-Datei erstellen soll. (Oder mache diesen Schritt, ganz verrückt, manuell)
  • Ich fülle die .env-Datei aus und speichere sie auf dem Server
  • Ich merge den Pull Request für das infra-Repo
  • Woodpecker checkt es und sagt Waltraut Bescheid, dass sich was geändert hat
  • Waltraut sagt der entsprechenden nana-Instanz Bescheid, die checkt das Repository aus und startet den foobar-Stack
  • Zwei Minuten später hat Caddy es auch geschafft, ein neues SSL-Zertifikat zu besorgen, und ich kann foobar.example.com aufrufen.

Genauso funktioniert das im Grunde auch für selbst entwickelte Sachen:

  • Ich sage der KI, dass sie mir bitte Tool XYZ entwickeln und in ein vorgegebenes Git-Repo pushen soll. Oder ich mache es halt selber.
  • Woodpecker baut das Image und packt es in die Forgejo-Registry
  • Der Rest ist exakt so wie oben.
file://1t6k8emtw98hqbgj
Grün: Dinge, die ich noch selber machen muss.

Hätte ich mir jetzt lieber Komodo genauer angucken sollen, statt alles selber zu bauen? Vielleicht wäre das schneller gegangen. So hat es aber mehr Spaß gemacht und mindestens 50 % der Sachen hätte ich eh machen müssen, da man ja auch erst mal alle Projekte dazu kriegen muss, sich korrekt zu verhalten.

Ist meine Lösung production-ready, komplett abgesichert und vor Ausfällen geschützt? Sicherlich nicht, aber da ich 90 % davon sowieso nur zu Hause betreibe und die nana-Instanzen der öffentlichen Server auch nur per Tailscale erreichbar sind, ist das verschmerzbar.

Abgesehen davon kann man über letztere eh nur sagen: „Hier, trigger mal einen Deploy.“ Außer einem DDoS wäre da jetzt auch nicht so viel Angriffspotenzial.

Das Wichtigste an so einem Setup ist ja, dass es übersichtlich und gut dokumentiert ist. Ist ja nicht so, als hätte ich in meinem Webhosting-Leben nicht schon tausende Varianten von automatischen Pipelines in diversen Ausbaustufen gehabt. Meistens ist es so, dass man es entweder nicht fertig baut oder sich nicht merken kann, wie es funktioniert, und vergisst, es zu dokumentieren.

Hier habe ich nun immerhin einen guten Überblick; die Sache ist überschaubar und dank des unermüdlichen Willens der KI sogar dokumentiert. Vor allem das Letzte ist eine Sache, die ich bei privaten Sachen früher natürlich niemals gemacht hätte. Dokumentieren, hahaha.

8.10.2026

Ich habe mich heute mal durchs Wiki gekämpft und dort versucht, alles zu reparieren, was kaputt war, zum Beispiel die Band-Seiten wie die zu Subway to Sally. Manchmal freut man sich ja dann doch über ganz netten Content, den man mal geschrieben hat. Umso mehr freut man sich, wenn er wieder funktioniert!


Beim Anschauen von ein paar Götz-Widmann-Klassikern auf YouTube kam ich auf GoetzWidmann.live. Das ist eine Plattform, auf der man ein paar Euro einwerfen kann, um Zugriff auf einen ganzen Haufen von Live-Mitschnitten zu bekommen; so was finde ich ja immer ganz toll als alter Live-Fan.

7.10.2026

Ich hab heute doch den anderen Mac im Haushalt etwas beschlagnahmt, um ein paar Dinge zu erledigen, die ich auf die Schnelle am Linux-Rechner nicht eingerichtet bekommen habe. Hab ja immerhin fast einen Tag ausgehalten.

Immerhin brachte es mich auch direkt zu der beruhigenden Erkenntnis, dass ich außer der Bitwarden-App oder zur Not dem Vaultwarden-Webinterface gar nichts brauche, um wieder frisch durchzustarten.


Ansonsten ist heut nicht viel passiert. Ich habe nochmal den ganzen Code-CI-Deployment-Prozess unter die Lupe genommen und ein paar Bugs gefixt, hier und da klemmte es nämlich noch etwas. Jetzt läuft es aber wirklich sehr flüssig durch, und auf allen bisher eingebundenen Servern kann ich mit einem Klick etwas deployen.


Kurz nach dem Mittag kam auch die Nachricht von der Werkstatt, dass die Diagnose abgeschlossen ist: Die Tastatur ist kaputt, wer hätte es gedacht. Der Kostenvoranschlag umfasst jetzt 1.240 €, worauf ich ja mehr oder weniger vorbereitet war. Dass das Topcase ungefähr 800 € netto kostet, wusste ich, und naja, ein bisschen Arbeitszeit kommt ja auch dazu.

Nun ist das natürlich eine Menge Geld für eine Tastatur und einen Akku, das kann man nicht anders sagen, aber was soll man machen? Ich sehe es so, dass ich in den vergangenen fünf Jahren keine Probleme mit dem Mac hatte und auf die Zeit gesehen ist es halbwegs okay.

Was wäre auch die Alternative? Ein neues 16" MacBook Pro in 2TB/64GB-Konfiguration kostet aktuell ungefähr 6.000 €, und das ist einfach ziemlich viel Geld und würde sich für mich gerade absolut nicht lohnen. Ich denke, dass ich, wenn er repariert ist, sicher noch ein oder zwei Jahre hinkomme. Bisher hatte ich nie den Eindruck, dass der M1 für irgendetwas nicht mehr reicht.

6.10.2026

Das MacBook ist bei der Diagnose. Im Bestfall dauert es eine Woche, bis es fertig ist. Ich bin gespannt. Es hat sich angefühlt, als hätte ich einen Teil meines Körpers abgegeben.


Ich musste heute also tatsächlich unter Linux arbeiten, und es klappte alles nicht ganz ohne Probleme.

Erst einmal hatte ich kurz vor der Abgabe noch ein paar Daten auf einen USB-Stick gezogen und … natürlich hatte ich ihn aus Versehen mit APFS formatiert. So viel zu meinen Daten. Es gibt zwar einen APFS-FUSE-Treiber, aber der funktioniert einfach nicht. Schade.

(Ich kam später noch über das andere MacBook an die Daten, aber bei dem sind die USB-C-Ports kaputt, d. h. ich musste den Stick ca. 30 Mal neu einstecken und hoffen, dass er dabei nicht kaputtgeht.)


Des Weiteren musste ich noch ein paar Programme installieren, und teilweise schlug da was fehl oder ich musste irgendwelche Sonderlocken machen. Am Ende hab ich die gleiche Sache dann per Snap, Flathub, DNF und über den „Copy-paste einfach diesen Bash-Befehl“-Weg installiert, und alles war durcheinander. Normales Linux-Gefühl.

Als ich Ghostty installierte, das aber ohne extra Konfiguration keinen Window Frame hatte, war meine Wut schon wieder auf einem kaum aushaltbaren Level. Zu diesem Zeitpunkt hatte ich meinen Mac immerhin auch schon 90 Minuten abgegeben.

(Als ich den Window Frame hergestellt hatte, merkte ich, dass Ghostty beim Druck auf Backspace nur Leertasten produzierte, und gab damit auf.)


Zur Beruhigung hab ich mir erst mal ein Konzert von den Wise Guys angemacht, ein Klassiker, der immer funktioniert.

Zu viel Noise

Es gibt ja so ein paar RSS-Feeds, die einfach ne Menge Noise für sehr wenig Signal erzeugen. Bei mir ist es hauptsächlich der von DWDL mit seinen geschätzt 10–20 Artikeln pro Tag.

Ich habe ihn seit Jahrzehnten abonniert und mich interessieren vielleicht 0,5 % der Headlines, und trotzdem ließ ich es jahrelang einfach geschehen: die komplette Vermüllung meines RSS-Readers.

Als ich mir letztens ein Tool baute, das mir morgens eine Zusammenfassung meiner RSS-Feeds schickt, fiel es mir wieder besonders auf: 90 % der Artikel waren von DWDL, und nicht mal das schlauste LLM hätte herausfinden können, was daran jetzt für mich spannend wäre. Ich konnte ja selbst nicht mal ein paar Stichwörter fürs Filtern liefern.

Somit heißt es nun: bye-bye DWDL. Ich hoffe, ich bekomme die zwei bis drei spannenden News zur deutschen Medienlandschaft, die mich interessieren, in Zukunft irgendwie mit.

5.10.2026

Äh ja, ich habe direkt noch das dritte Achtsam Morden-Buch hinterhergehört. Eine Serie muss beendet werden. Allerdings sind jetzt auch meine Audible-Credits erstmal alle.


Beim Blog gab es auch ein paar kleine Umbauten:

  • Die Seite mit der Übersicht der Albenbewertungen ist endlich nach Erscheinungsdatum sortiert.
  • Ich habe angefangen, eine Liste von Musicals zu pflegen, die ich mag. Da muss noch ein bisschen mehr Content rein, aber mühsam ernährt sich das Eichhörnchen.

Abgesehen von diesen Content-Änderungen habe ich nochmal was umgebaut: Bisher habe ich den Kirby-site-Ordner als Mount aus dem Dateisystem eingebunden, damit ich da immer noch per SFTP drankam, um schnell Template-Änderungen zu deployen.

Leider hatte das natürlich den Nachteil, dass sich das nur schwer sauber halten lässt, wenn man den Ordner nicht nach jedem Deploy neu erstellen lassen will. Ich ließ den Mount also weg und fügte dafür einen OpenSSH-Container hinzu, der mir SFTP direkt in den Container gibt, haha. Lauscht natürlich nur auf dem Tailscale-Interface.

Jetzt kann ich wild rumschmieren und ein normaler Docker-Deploy macht am Ende alles sauber.

Achtsam morden am Rande der Welt

Karsten Dusse

Pff. Weiterhin unterhaltsam, aber der schwächste Band der Reihe.

4.10.2026

Ich werde meinen Rechner am Dienstag zur Diagnose bringen und danach die Tastatur reparieren lassen. Ich habe wenig Hoffnung, dass das innerhalb eines Nachmittags geht, also muss ich mir für die Übergangszeit einen Ersatzrechner besorgen.

Ursprünglich war der Plan, das andere MacBook im Haushalt zu benutzen, aber mir fiel ein, dass ich ja noch diesen Ayaneo Mini-PC in der Schublade habe. So langsam kann der ja nicht sein, und für ein bisschen PHP-Programmieren wird es wohl ausreichen. Ich machte es mir heute also zur Aufgabe, ihn einzurichten, was natürlich nicht so einfach war.

Erst mal musste ich einen Monitor, eine Tastatur und eine Maus besorgen. Zusätzlich einen USB-Stick für die Linux-ISO, die ich ganz ohne Torrent heruntergeladen habe. Ich entschied mich nach kurzer Beratung für Fedora mit KDE. Mein erstes Mal ohne apt als Paketmanager!

Nachdem es installiert war, spielte es erst mal seine Systemsounds auf der Sonosbox ab, die es irgendwie gefunden (das finde ich ja ganz nice, dass Linux jetzt so Plug-and-Play ist) und als Standard auserkoren hatte (das finde ich etwas komisch; stellt euch vor, das wäre mitten in der Nacht passiert).

Ich habe mal ein paar Dinge installiert und habe jetzt noch ein bisschen Zeit, mich da einzurichten, um zu sehen, ob ich ein paar Tage auch auf einem Linux-Rechner klarkommen kann.


In dem Zuge habe ich natürlich auch mit externer Tastatur und Maus gearbeitet, und es war das erste Mal seit bestimmt 15 Jahren, dass es sich irgendwie gut angefühlt hat, eine Maus zu benutzen? Normalerweise bin ich ja Trackpad-Ultra und würde es nie hergeben wollen, aber heute fühlte sich die Maus plötzlich irgendwie gut an. Hilfe, was passiert?


Des Weiteren habe ich mir ein paar Gedanken gemacht, was genau ich am MacBook eigentlich backupen muss, was sowieso gebackupt ist und was man wie leicht wiederherstellen kann. Das muss ich mir mal aufschreiben und zu Ende bringen, damit es mir beim nächsten Mal weniger schwerfällt, abzuwägen.


Ich habe das zweite Buch aus der Achtsam-Morden-Reihe gehört, und es war auch ganz unterhaltsam!


Diese Woche habe ich 132 $ in Tokens verbraten, zu 99,6 % bei Anthropic.

Noch einer

Ein weiterer Server ist auf die neue Infrastruktur umgestellt!

knuspermagier.de läuft nun auch mit Git-gestützter Konfiguration und wird automatisch neu deployt, wenn die CI-Pipeline den Container neu gebaut hat!

An sich war dieser Server auch gar nicht so aufwändig, denn außer Kirby und dem neu installierten öffentlichen Forgejo läuft hier ja nichts, aber der Teufel liegt ja bekanntlich im Detail: Ich hatte auch noch einen nginx-Container, der einfach statische Seiten ausgeliefert hat, die ich per SFTP in einem Verzeichnis ablegte. Wie damals. Cool!

Bisher lief das so: Upload, im Nginx Proxy Manager die Domain anlegen, auf das SSL-Zertifikat warten und fertig. Dank Caddy kann ich mir den einen Umweg jetzt noch sparen. Kommt ein Request auf foo.example.com, schaut er einfach, ob es ein entsprechendes Verzeichnis gibt, und kümmert sich automatisch um das SSL.

(Ja, dass Caddy automatisch SSL macht, ist schon immer der USP des Tools und das macht es seit 10 Jahren, aber manchmal bin ich late to the party.)