Files
homelab-infra/ops/policy-checks/mem-limits-baseline.md
T
Micha f2923aac62 F-19 prep: document mem-limits baseline plan (no compose changes)
ops/policy-checks/mem-limits-baseline.md captures the deliberate
"not today" decision for memory limits plus the plan for when it
becomes relevant:

- Phase 1: 7 days of hourly docker stats snapshots
- Phase 2: derive Tier-1 peak per container
- Phase 3: set limits at peak * 1.5 with documented floors
  (Postgres 1G, Mongo 1G, Redis 256M, etc.)
- Phase 4: roll out smallest-risk containers first, observe 24h
  between stages
- Phase 5: Tier-2 only after a concrete trigger event

Next trigger: family invitation out + 4 weeks stable use, or
first real OOM event in docker-critical-events.sh, or a sudden
Immich/Nextcloud load spike where host swap becomes visible.

Today's policy check is clean (0 Critical, 1 documented Warning
on influxdb3-core user 0, 13 documented Info findings on host
ports / privileged exceptions / latest+digest tags).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 11:58:54 +02:00

4.1 KiB

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/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:

#!/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:

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:

deploy:
  resources:
    limits:
      memory: <peak * 1.5, mindestens floor>

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.