--- created: 2026-08-11 title: Compose-Datei auf dem Testserver ist eine handgepflegte Kopie und driftet vom Repository ab area: infra severity: major trigger: bevor eine zweite Installation (Kunde oder Produktivsystem) aufgesetzt wird files: - docker-compose.yml - docker-compose.prod.yml - /opt/tessera/docker-compose.yml (auf 192.168.13.12, nicht im Repository) --- ## Problem `/opt/tessera` auf dem Testserver (192.168.13.12) ist **keine** Git-Arbeitskopie — dort liegt eine von Hand gepflegte `docker-compose.yml`. Der Deploy-Weg holt nur die Images aus der Registry; Aenderungen an den Compose-Dateien im Repository kommen dort nie an. Am 2026-08-11 konkret aufgefallen: Der umbenannte Verschluesselungsschluessel (`TESSERA_ENCRYPTION_KEY`, Commit f574884) wurde in die `.env` auf dem Server eingetragen, kam aber nicht im Container an, weil die dortige Compose-Datei die Variable gar nicht durchreicht. Die Anwendung fiel deshalb auf den alten Namen zurueck und protokollierte die Veraltet-Warnung. Behoben durch eine Hand-Aenderung an der Server-Datei — was das Problem genau wiederholt. Zweiter, wichtigerer Effekt: die im selben Zug eingebaute Absicherung "Stack startet nicht ohne Schluessel" (Commit 7bda56d) greift auf alpha **nicht**, weil sie nur in der Repository-Datei steht. Beide Dateien laufen seit dem 2026-08-11 inhaltlich auseinander, und niemand merkt es, bis etwas nicht tut. Fuer eine Kundeninstallation heisst das: aufgesetzt wird nach dem Repository, alpha laeuft nach seiner eigenen Kopie. Was auf alpha funktioniert, ist damit kein Beleg dafuer, dass die ausgelieferte Konfiguration funktioniert — und genau dafuer ist ein Testsystem da. ## Solution Noch offen, drei denkbare Wege — Entscheidung gehoert vor die Umsetzung: 1. **`/opt/tessera` zur Arbeitskopie machen.** `git clone`, danach zieht ein `git pull` Compose-Datei und Deploy-Anleitung mit. Einfachster Weg, aber der Server braucht dann Lesezugriff auf das Repository, und lokale Anpassungen (die es geben mag) muessen vorher sichtbar gemacht werden. 2. **Compose-Datei als Artefakt ausliefern.** Die CI legt die passende Datei zu jedem Image-Build ab, der Server holt sie zusammen mit den Images. Sauberste Trennung, meiste Arbeit. 3. **Unterschiede bewusst machen.** Die Server-Datei bleibt handgepflegt, aber ein Abgleich-Skript meldet Abweichungen zur Repository-Version. Loest das Problem nicht, macht es aber sichtbar. Vor der Entscheidung zu klaeren: **warum** die Datei dort ueberhaupt von Hand gepflegt wird. Moeglicherweise enthaelt sie Anpassungen, die im Repository fehlen (Ports, Netzwerke, Volumes fuer den konkreten Host) — die muessten dann erst ins Repository, sonst bricht Weg 1 oder 2 den laufenden Betrieb. Eine Sicherung der aktuellen Server-Datei liegt als `/opt/tessera/docker-compose.yml.bak.20260811` daneben. ## Zwischenstand 2026-08-11: Drift abgeglichen, Mechanik weiter offen Die beiden Dateien sind jetzt identisch. Verglichen wurden sie erst an diesem Tag zum ersten Mal — es gab genau drei Unterschiede: | Einstellung | Server | Repository | Uebernommen | |---|---|---|---| | `NEXT_PUBLIC_API_URL` | `${APP_URL}` | `http://api:3001` | Server-Fassung ins Repository | | `TESSERA_FORCE_CHANGE` | Standard `true` | Standard `false` | Server-Fassung ins Repository | | `TESSERA_ENCRYPTION_KEY` | kein Abbruch | `:?`-Abbruch | Repository-Fassung auf den Server | Der erste Punkt war der ernste: `http://api:3001` existiert nur im Compose-Netz. Der Browser eines Kunden sitzt ausserhalb und haette die API nie erreicht — die Repository-Fassung war fuer eine echte Installation unbrauchbar, und niemand haette es vor dem ersten Kundentermin gemerkt. Die Korrektur lebte seit unbekannter Zeit nur auf alpha. Ablauf: Sicherung als `/opt/tessera/docker-compose.yml.bak.20260811-1530`, danach die Repository-Fassung auf den Server kopiert und mit `docker compose config` geprueft (gueltig, alle Variablen aufloesbar). Das Neuerzeugen der Container bleibt Sache des Users. **Was den Zettel offen hielt:** abgeglichen ist ein Zustand, keine Loesung. Die naechste Aenderung an `docker-compose.prod.yml` driftet genauso, weil `/opt/tessera` weiterhin keine Arbeitskopie ist und der Deploy weiterhin nur Images holt. Die Frage nach host-eigenen Anpassungen ist inzwischen beantwortet: es gab keine, nur zwei Korrekturen, die ins Repository gehoerten. ## Resolution 2026-08-11: Weg 1 umgesetzt `/opt/tessera` ist jetzt eine Arbeitskopie des Repositorys (`git init` + `fetch --depth 1` + `checkout` im bestehenden Verzeichnis, nicht neu geklont). Ein `git pull` bringt die Compose-Datei kuenftig mit. Das Verzeichnis wurde bewusst behalten statt woanders hin geklont: Compose leitet den Projektnamen aus dem Verzeichnisnamen ab, und ein anderer Name haette `tessera_pgdata` verwaist und die Datenbank leer wirken lassen. Vorher und nachher geprueft — Name `tessera`, Volume `tessera_pgdata`, beides unveraendert. `COMPOSE_FILE=docker-compose.prod.yml` steht jetzt in `/opt/tessera/.env`. Ohne diesen Eintrag haette der Checkout den Server auf die Entwicklungs-Fassung (`docker-compose.yml`, `localhost:3001`) umgestellt — die Falle bei diesem Weg, weil das Repository beide Dateien fuehrt. `.env` selbst steht in `.gitignore` und hat den Checkout unveraendert ueberlebt. Geprueft: `docker compose config` gueltig, `NEXT_PUBLIC_API_URL` loest auf `https://alpha.tessera.ctl.de` auf. Container neu erzeugen bleibt Sache des Users. **Offen, bewusst nicht entschieden:** Das Repository-Token steht jetzt in `/opt/tessera/.git/config` (nur fuer root lesbar). Der Server braucht es fuer `git pull`. Wenn das nicht bleiben soll, waere ein eigener Lese-Zugang fuer den Server die Alternative.