fix(prod): Cache darf ausfallen, ohne die Seite mitzunehmen #94

Merged
Phil merged 3 commits from fix/prod-disk-hardening into main 2026-09-07 08:34:45 +00:00
Owner

Closes #85

Worum es geht

Die Seite war komplett aus, weil ein Cache ausgefallen ist. Redis konnte bei
voller Platte seinen Snapshot nicht schreiben und schaltete daraufhin alle
Schreibbefehle ab. Craft nutzt ihn als Cache-Backend, also scheiterte jeder
Request schon beim Laden der Site-Konfiguration.

Das Aufräumen der Platte war die Erste Hilfe. Die eigentliche Schwachstelle ist,
dass ein Cache die Anwendung überhaupt mitnehmen kann.

Drei Ebenen

  1. Redis persistiert nicht mehr (--save "" --appendonly no), das
    redis_data-Volume entfällt. Er ist ein reiner Cache mit allkeys-lru; ein
    Verlust kostet einen Kaltstart, keine Daten. Damit ist der Ausfallpfad zu.
  2. scripts/deploy/prod.sh — der Ablauf aus docs/DEPLOYMENT.md ist jetzt
    ausführbar statt abgeschrieben. Er bricht unter 10 GB freiem Plattenplatz ab,
    räumt den Build-Cache nach jedem Lauf auf und prüft am Ende alle 25 Seiten
    live auf HTTP 200.
  3. docker/prod/daemon.json deckelt den BuildKit-Cache bei 10 GB.

Ein Fund nebenbei, der nicht im Issue stand

Das static_files-Volume überdeckt das ganze web/-Verzeichnis, der alte Ablauf
synchronisierte aber nur dist/ und static/ hinein. Live lief deshalb seit
Monaten eine 86-zeilige llms.txt mit veralteter Positionierung, während im
Repo längst eine 29-zeilige stand. Das ist derselbe Fehler, der 2026-06-23 schon
die Schriften erwischt hat. Das Skript synct jetzt das komplette web/.

Damit ist ein Teil von #84 (Punkt 3) miterledigt: der Text war nie falsch, er kam
nur nie an.

Aufräumen

Acht konkurrierende Deployment-Dokumente und drei Deploy-Skripte im Repo-Root
sind weg. Keines kannte Blitz, den Volume-Drift oder die compiled-templates-Falle.
docs/DEPLOYMENT.md erklärt das Warum, das Skript führt das Wie aus.

Gemessen, nicht angenommen

Voller Deploy gelaufen, Ausgabe in dieser Sitzung:

  • Alle 25 Seiten HTTP 200, langsamste 0,24 s
  • redis-cli CONFIG GET save → leer, appendonly → no
  • /llms.txt liefert jetzt 29 Zeilen statt 86
  • Platte von 89 % auf 69 %, 23 GB frei (17,89 GB Build-Cache freigegeben)
  • systemctl reload docker angewandt, 23 Container liefen durch

Nicht geprüft: ob eine künstlich vollgeschriebene Platte die Seite jetzt
wirklich stehen lässt. Das würde einen absichtlichen Ausfall auf dem Produktions-
host verlangen; die Redis-Konfiguration ist stattdessen direkt am laufenden
Container ausgelesen.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VyiQJ8BM3hcqH3E3sPvg1N

Closes #85 ## Worum es geht Die Seite war komplett aus, weil ein **Cache** ausgefallen ist. Redis konnte bei voller Platte seinen Snapshot nicht schreiben und schaltete daraufhin alle Schreibbefehle ab. Craft nutzt ihn als Cache-Backend, also scheiterte jeder Request schon beim Laden der Site-Konfiguration. Das Aufräumen der Platte war die Erste Hilfe. Die eigentliche Schwachstelle ist, dass ein Cache die Anwendung überhaupt mitnehmen kann. ## Drei Ebenen 1. **Redis persistiert nicht mehr** (`--save "" --appendonly no`), das `redis_data`-Volume entfällt. Er ist ein reiner Cache mit `allkeys-lru`; ein Verlust kostet einen Kaltstart, keine Daten. Damit ist der Ausfallpfad zu. 2. **`scripts/deploy/prod.sh`** — der Ablauf aus `docs/DEPLOYMENT.md` ist jetzt ausführbar statt abgeschrieben. Er bricht unter 10 GB freiem Plattenplatz ab, räumt den Build-Cache nach jedem Lauf auf und prüft am Ende alle 25 Seiten live auf HTTP 200. 3. **`docker/prod/daemon.json`** deckelt den BuildKit-Cache bei 10 GB. ## Ein Fund nebenbei, der nicht im Issue stand Das `static_files`-Volume überdeckt das ganze `web/`-Verzeichnis, der alte Ablauf synchronisierte aber nur `dist/` und `static/` hinein. Live lief deshalb seit Monaten eine **86-zeilige `llms.txt`** mit veralteter Positionierung, während im Repo längst eine 29-zeilige stand. Das ist derselbe Fehler, der 2026-06-23 schon die Schriften erwischt hat. Das Skript synct jetzt das komplette `web/`. Damit ist ein Teil von #84 (Punkt 3) miterledigt: der Text war nie falsch, er kam nur nie an. ## Aufräumen Acht konkurrierende Deployment-Dokumente und drei Deploy-Skripte im Repo-Root sind weg. Keines kannte Blitz, den Volume-Drift oder die compiled-templates-Falle. `docs/DEPLOYMENT.md` erklärt das Warum, das Skript führt das Wie aus. ## Gemessen, nicht angenommen Voller Deploy gelaufen, Ausgabe in dieser Sitzung: - Alle 25 Seiten HTTP 200, langsamste 0,24 s - `redis-cli CONFIG GET save` → leer, `appendonly` → `no` - `/llms.txt` liefert jetzt 29 Zeilen statt 86 - Platte von 89 % auf 69 %, 23 GB frei (17,89 GB Build-Cache freigegeben) - `systemctl reload docker` angewandt, 23 Container liefen durch **Nicht geprüft:** ob eine künstlich vollgeschriebene Platte die Seite jetzt wirklich stehen lässt. Das würde einen absichtlichen Ausfall auf dem Produktions- host verlangen; die Redis-Konfiguration ist stattdessen direkt am laufenden Container ausgelesen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VyiQJ8BM3hcqH3E3sPvg1N
Redis ist hier reiner Cache (allkeys-lru, Craft-Datencache + Blitz). Mit
aktivem AOF und RDB-Regeln schaltet er bei stop-writes-on-bgsave-error alle
Schreibbefehle ab, sobald er nicht mehr auf die Platte schreiben kann. Craft
scheitert dann schon beim Laden der Site-Konfiguration: HTTP 500 auf allen
Seiten, auch die CLI tot. Genau so ist die Seite in der Nacht auf den 07.09.
ausgefallen.

Ein Cache darf ausfallen, ohne die Anwendung mitzunehmen. Ohne Persistenz
kostet ein Neustart einen Kaltstart, keine Daten. Das redis_data-Volume
entfällt damit in allen fuenf Compose-Varianten.

Refs #85

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyiQJ8BM3hcqH3E3sPvg1N
Der Ablauf stand bisher nur als Prosa in docs/DEPLOYMENT.md, in neun Schritten,
die von Hand kopiert wurden. Jeder Deploy hat dabei mindestens einen Schritt
verloren: erst die Fonts (Gotcha 4), zuletzt web/llms.txt.

scripts/deploy/prod.sh fuehrt den Ablauf jetzt aus. Drei Aenderungen gegenueber
der Prosa-Fassung:

- Vorpruefung bricht unter 10 GB freiem Plattenplatz ab. Der alte Ablauf begann
  mit einem DB-Backup, das bei voller Platte selbst fehlschlaegt.
- Das static-Volume bekommt das komplette web/ statt einer Liste einzelner
  Unterordner. Die Liste traf nur, woran jemand gedacht hatte; alles andere
  blieb auf dem Stand des Erst-Deploys.
- Die Live-Pruefung laeuft ueber alle 22 Seiten und bricht mit Logauszug ab,
  wenn eine nicht 200 liefert. Dieselbe Liste dient dem Warmlauf.

docker/prod/daemon.json deckelt den BuildKit-Cache bei 10 GB. Auf dem Host lagen
22,87 GB davon, das war der eigentliche Platzfresser.

Refs #85

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyiQJ8BM3hcqH3E3sPvg1N
Im Repo-Root lagen acht Deployment-Dokumente und drei Deploy-Skripte neben
docs/DEPLOYMENT.md, alle mit unterschiedlichem Stand. Keines davon kannte
Blitz, den Volume-Drift oder die compiled-templates-Falle. Wer eines davon
befolgte, deployte falsch.

docs/DEPLOYMENT.md ist ab jetzt die einzige Wahrheit und erklaert das Warum;
scripts/deploy/prod.sh fuehrt das Wie aus. Gotcha 4 beschreibt den Voll-Sync
statt der Einzelpfade, Gotcha 13 den Plattenausfall.

Refs #85

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyiQJ8BM3hcqH3E3sPvg1N
Phil merged commit 15c4e93aea into main 2026-09-07 08:34:45 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Phil/philflow.io!94
No description provided.