# Memory-Limits Baseline - Vorbereitung F-19 Status: **Vorbereitung**. Echte `mem_limit`-Werte werden erst gesetzt, wenn mindestens 7 Tage realer Peak-Werte vorliegen. Bezug: `docs/archive/2026-05/AUDIT_2026-05-25.md` F-19 "Keine Container-Memory-Limits". ## Warum nicht heute Audit-TODO 2026-05-30: F-19 ist nicht akut. Im `docs/MIGRATION_LOG.md` ist **kein einziger** OOM-/Memory-Vorfall dokumentiert. `services/posture-check/docker-critical-events.sh` ueberwacht `die`/`oom`/`kill`-Events und alarmiert via ntfy — der Detektions-Pfad ist da, der Daten-Befund fehlt. Limits ohne Peak-Daten zu setzen bedeutet entweder zu eng (Flapping) oder so weit weg vom Realwert, dass die Schutzwirkung gegen Null geht. Familien-Einladung verschiebt die Risiko-Bilanz nach oben: Ein OOM in Authelia/Postgres bei Familien-Nutzung kostet Vertrauen, nicht nur Operator-Zeit. Sobald die Einladung raus ist, wird F-19 ein "should" statt "nice". ## Plan ### Phase 1 - Peak-Beobachtung (7 Tage) Auf dem Host stuendlich `docker stats --no-stream` snappen und in eine Textfile pro Container schreiben. Beispiel-Snippet fuer das Cron-Skript: ```bash #!/usr/bin/env bash # /boot/config/plugins/user.scripts/scripts/docker-stats-snapshot/script set -euo pipefail OUT="/mnt/user/services/policy-checks/docker-stats-$(date +%Y%m%d).log" mkdir -p "$(dirname "$OUT")" { echo "=== $(date -Iseconds) ===" docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.CPUPerc}}' } >> "$OUT" ``` Cron: stuendlich (`0 * * * *`), 7 Tage laufen lassen. ### Phase 2 - Peak-Auswertung Pro Tier-1-Container das Maximum `MemUsage` aus dem 7-Tage-Log ableiten: ```bash grep -E '^postgresql17|^authelia|^Redis|^vaultwarden|^gitea|^traefik|^komodo-mongo' \ /mnt/user/services/policy-checks/docker-stats-*.log \ | awk -F'\t' '{print $1, $2}' \ | sort -u ``` Erwartete Groessenordnungen (zur Plausibilitaetspruefung, nicht zur Festlegung): | Container | Erwartung | |---|---| | postgresql17 | 200-600 MB | | Redis | 30-80 MB | | authelia | 50-150 MB | | vaultwarden | 100-300 MB | | gitea | 200-500 MB | | traefik | 80-200 MB | | komodo-mongo | 300-800 MB | ### Phase 3 - Limit-Setting Pro Tier-1-Container: ```yaml deploy: resources: limits: memory: ``` Floor-Werte: - postgresql17: 1G (Cache-Verhalten leidet bei weniger) - komodo-mongo: 1G (WiredTiger braucht Working-Set) - Redis: 256M (Paperless-Cache) - vaultwarden: 256M - gitea: 512M - traefik: 256M - authelia: 256M `mem_reservation` bewusst nicht setzen — auf einem Single-Host-Setup ist Reservation Theater. ### Phase 4 - Rollout-Reihenfolge 1. Redis und authelia zuerst (kleinste Risiko-Container, klares Memory-Profil). 2. Wenn nach 48 h kein Flapping: traefik, vaultwarden, gitea. 3. Zuletzt postgresql17 und komodo-mongo, weil DB-Limits bei zu engem Setting Performance kippen. Jede Stufe einzeln committen und 24 h beobachten. ### Phase 5 - Tier-2 (optional) Tier-2 (Immich, Nextcloud, Paperless, Mealie, Mail-Archiver) bewusst spaeter, nur wenn ein konkreter Vorfall das rechtfertigt. Immich-ML ist der wahrscheinlichste Kandidat fuer den ersten echten OOM-Vorfall, deshalb dort zuerst beobachten, dann limitieren. ## Stop-Regel Falls in Phase 3 ein Container nach Limit-Setzung haeufiger restartet als vor dem Limit: Limit raus, kein zweiter Versuch ohne dazwischenliegende Peak-Reanalyse. Doku-Eintrag in `docs/MIGRATION_LOG.md`, F-19 weiter offen. ## Was nicht ins Skript gehoert - Mem-Limits sind kein Tuning, kein Performance-Hebel. Wer sich Performance erhofft, hat das falsche Werkzeug. - Postgres-`shared_buffers` und `effective_cache_size` muessen zur Limit-Groesse passen. Setzen ohne Postgres-internes Tuning macht die DB langsamer, nicht stabiler. - Komodo-Mongo waechst mit Stack-/Update-Historie. Limit fuer naechste 12 Monate planen, nicht fuer den heutigen Stand. ## Naechster Trigger - Familien-Einladung raus, 4 Wochen stabile Nutzung, **oder** - erster echter OOM-Vorfall im `docker-critical-events.sh`-Log, **oder** - ein Immich/Nextcloud-Last-Sprung (z.B. grosses Foto-Backup), bei dem Host-Swap sichtbar wird. Bei einem dieser Trigger: Phase 1 starten.