Case Study: VPS-Betrieb ohne Plattformballast
Wie ein gewachsener VPS von schwerer App-Orchestrierung auf CI-Artefakte, PM2, Doppler und Caddy umgestellt wurde, ohne produktive Dienste in einem Big-Bang-Cutover zu riskieren.
Ein einzelner VPS kann lange gut funktionieren und trotzdem schleichend schwer werden. Erst läuft eine Website darauf, dann ein Analyse-Dienst, später eine kleine API, ein Passwort-Tresor, ein Admin-Panel und ein paar Datenbanken. Die einzelnen Entscheidungen sind jeweils nachvollziehbar. In Summe entsteht aber ein Betrieb, der mehr Speicher, mehr Container und mehr implizite Logik braucht als nötig.
Diese Case Study beschreibt eine kontrollierte Vereinfachung: weg von plattformverwalteten App-Runtimes, hin zu klaren CI-Artefakten, PM2-Prozessen, Doppler-Secrets und Caddy als öffentlicher Edge.
Ausgangslage
Der Server war produktiv. Deshalb war das Ziel nicht, alles neu zu bauen. Das Ziel war, die bestehende Umgebung schrittweise zu entflechten.
Die typischen Symptome:
- Builds liefen teilweise noch direkt auf dem Server.
- Mehrere kleine Apps liefen als eigene Runtime-Container.
- Routing war über mehrere Stellen verteilt.
- Alte Container konnten nach Deploy-Ereignissen wieder anlaufen.
- Speicherverbrauch und Betriebsoberfläche waren unnötig hoch.
Das Risiko lag nicht in einer einzelnen App. Das Risiko lag in der Summe kleiner, schwer nachvollziehbarer Abhängigkeiten.
Zielbild
Die neue Struktur sollte langweilig und wiederholbar sein:
| Ebene | Entscheidung |
|---|---|
| Build | GitHub Actions baut das produktive Artefakt |
| Secrets | Doppler liefert Runtime-Konfiguration |
| Runtime | PM2 startet Node- und App-Prozesse |
| Daten | Gemeinsame Datenbank- und Cache-Dienste werden wiederverwendet |
| Edge | Caddy übernimmt HTTPS und Reverse Proxy |
| Rollback | Alte Volumes und Releases bleiben zunächst erhalten |
Der wichtigste Architekturwechsel: Der VPS baut nicht mehr. Er entpackt ein Artefakt, setzt einen Release-Zeiger um und lädt PM2 neu.
Vorgehen
Zuerst wurde der Live-Zustand inventarisiert: laufende Container, Domains, Ports, Volumes, Secrets und aktive PM2-Prozesse. Das verhindert, dass ein scheinbar alter Container abgeschaltet wird, obwohl er noch produktiven Traffic bedient.
Danach wurde jede App einzeln migriert:
- Produktions-Secrets in Doppler abbilden.
- App lokal oder in CI als Standalone-Artefakt bauen.
- App auf einem internen Port als PM2-Canary starten.
- Daten final synchronisieren, wenn die App lokalen State nutzt.
- Öffentliche Route umstellen.
- Statuscodes, Header und Logs prüfen.
- Alte Container stoppen und Restart-Policies deaktivieren.
Für eine App mit eingebettetem Datenverzeichnis wurde das alte Docker-Volume erst nach einem kurzen Schreibstopp übernommen. Dadurch gab es keinen parallelen Schreibbetrieb zwischen alter und neuer Runtime.
Edge-Wechsel
Zunächst blieb die vorhandene Proxy-Schicht als Brücke aktiv. Das war absichtlich konservativ: Erst müssen die Apps stabil laufen, dann wird die öffentliche Edge vereinfacht.
Im zweiten Schritt wurde die Edge durch Caddy ersetzt. Caddy passt gut zu kleinen VPS-Setups, weil die Konfiguration lesbar bleibt und Zertifikate automatisch verwaltet werden. Für PM2-Apps reicht eine klare Regel: Hostname rein, interner Upstream raus. Für einzelne Legacy-Container kann Caddy gezielt in das passende Docker-Netzwerk gehängt werden.
Ergebnis
Nach dem Rückbau sank der Speicherverbrauch deutlich. Noch wichtiger war aber die neue Betriebsform:
- Deployments sind reproduzierbarer.
- Der Server wird beim Bauen nicht belastet.
- PM2 zeigt die laufenden Apps direkt.
- Die öffentliche Routing-Schicht ist explizit lesbar.
- Alte Runtime-Container bleiben gestoppt.
- Rollback bleibt über vorherige Releases und gesicherte Volumes möglich.
Das Ergebnis ist kein maximal automatisiertes Plattformprodukt, sondern ein kleiner, gut kontrollierbarer Produktionsbetrieb.
Was Teams daraus mitnehmen können
Eine Plattform ist am Anfang oft schneller. Später kann sie für kleine Setups mehr Oberfläche schaffen, als sie spart. Dann lohnt sich ein Rückbau auf einfache Bausteine:
- CI für Builds
- Secret-Management außerhalb des Repos
- PM2 für einfache Node-Runtimes
- gemeinsame Infrastruktur statt doppelter Datenbank-Container
- Caddy als klare HTTPS-Edge
Der Schlüssel ist die Reihenfolge: nicht erst abschalten und dann hoffen, sondern zuerst parallel canaryen, messen, routen und erst danach alte Runtimes entfernen.
Checkliste
- Live-Routen und aktive Container erfassen.
- Secrets und Datenvolumes identifizieren.
- CI-Artefakt bauen.
- PM2-Canary intern starten.
- Daten synchronisieren.
- Öffentliche Route umstellen.
- Öffentliche Smokes durchführen.
- Alte Container stoppen.
- Restart-Policies deaktivieren.
- Rollback dokumentieren.
So wird aus einem gewachsenen VPS wieder ein System, das man in wenigen Minuten erklären kann.