Files
homelab-infra/docs/STORAGE_LAYOUT.md
T

33 KiB

Storage Layout — KalliLab CORE

Feld Wert
Version 1.4
Status Active — bindend als docs/STORAGE_LAYOUT.md
Datum 2026-05-27
Geltungsbereich Unraid-Host Kallilabcore, alle Pools, Array-Disks, User-Shares, Appdata-Strukturen
Verbindlichkeit Bindend ab Inkraftsetzung. Abweichungen nur dokumentiert als Ausnahme (siehe Abschnitt 12)
Vorgänger keiner; entstanden aus Incident 2026-05-11 (NTFS-Cache-Korruption)
Verwandte Docs HOMELAB_ARCHITECTURE_MASTER_V2.md, docs/REPO_MAP.md, docs/SERVICE_CATALOG.md, docs/RESTORE_MATRIX.md, docs/SECRETS_MAP.md, docs/DISASTER_RECOVERY.md

1. Zweck

Dieses Dokument definiert die verbindliche Storage-Architektur für den KalliLab-CORE-Homelab-Stack. Es beantwortet:

  • Welche physikalischen Disks gibt es, mit welchem Filesystem.
  • Welche User-Shares existieren, mit welchen Cache- und Allokations-Settings.
  • Wo liegen Container-Daten und in welcher Sub-Struktur.
  • Welche Pfade dürfen in Compose-Dateien als Bind-Mount auftauchen, welche nie.
  • Welche Backup- und Posture-Check-Pflichten daraus folgen.

Es ist vor jeder Storage- oder Compose-Änderung zu lesen. Wenn ein neuer Stack, ein neuer Pfad, eine neue Disk, ein neuer Share gebraucht wird und hier nicht spezifiziert ist, wird dieses Dokument erst ergänzt, dann implementiert.

2. Leitprinzipien

  1. Filesystem-Semantik ist nicht verhandelbar. Datenbanken, Container-Persistenz und Array-Daten brauchen Linux-natives Filesystem (XFS, BTRFS, ZFS). NTFS, exFAT, FAT32 sind ausschließlich für Boot-USB und externe Austauschmedien zulässig, niemals für Cache, Array oder Pool.
  2. Recovery > Convenience. Eine Konvention, die Restore vereinfacht, schlägt jede Konvention, die nur die Erstinstallation vereinfacht.
  3. Posture-Check als First-Class-Concern. Jede Annahme über den Host-Zustand (FS-Typ, Mount, Permissions) muss automatisiert prüfbar sein. „Wir wissen schon, dass das stimmt" ist keine zulässige Begründung.
  4. GitOps-Konformität. Alles, was Compose-, Share-, oder Storage-Verhalten beeinflusst, ist im Repo dokumentiert oder versioniert. Was nur im Host lebt, existiert für Recovery nicht.
  5. Pfad-Stabilität. Pfade in Compose-Bind-Mounts bleiben stabil über Filesystem-Migrationen, Disk-Wechsel und Pool-Umzüge hinweg. Konkret: Container kennen /mnt/user/...-Pfade, nie /mnt/cache/... oder /mnt/disk1/....
  6. Keine Sammelordner. Eine Datei hat genau einen logischen Eigentümer (Stack, Share, Nutzkategorie). Mischungen erzeugen Drift und brechen Backup-Excludes.
  7. Bewusste Heterogenität nur mit Begründung. Wenn ein Stack vom Standard abweicht (z. B. Host-Network, Privileged, Direct-Cache-Mount), ist die Abweichung in Abschnitt 12 dokumentiert mit Begründung und Risiko-Akzeptanz.

3. Physikalisches Layout (Soll-Zustand)

Slot Device Filesystem Größe Zweck Status nach Recovery
Cache (Pool) Samsung SSD 970 EVO Plus 2TB, NVMe (S4J4NM0W609649H) XFS 1.8T nutzbar Appdata, system, domains Reformat von NTFS auf XFS, Phase 1
Disk1 (Array) WDC WD60EFAX-68JH4N1 (WD-WX32D90PC0V0) XFS auf md1p1 5.5T nutzbar Nutzdaten, Backups, Services NTFS-zu-XFS-Migration Phase 2 abgeschlossen am 2026-05-25
Parity TOSHIBA HDWG480 (2460A03VFA3H) — (keine FS) 7.3T Redundanz für Array Unverändert
Boot Samsung Flash Drive (0375125090000587) FAT32 59.8G Unraid-OS, Konfiguration Regelmäßig per Flash-Backup gesichert
Externe Backup-Platte H:/ Externe HDD am Windows-PC NTFS 8.0T Nearline-Pull-Ziel für kritische Restore-Artefakte Kein Off-site-/Airgap-Ersatz; Pull-Workflow in docs/H_DRIVE_NEARLINE_PULL.md

Physikalische Basisdaten sind aus docs/HARDWARE_INVENTORY.md und dem Host-Readout vom 2026-05-27 übernommen. Detailwerte zu SMART/Health bleiben dort die autoritative Quelle; dieses Dokument hält die Storage-Policy.

Begründung Filesystem-Wahl:

  • XFS für Cache und Disk1: Unraid-Standard, robuste fsck, gute Performance für Datenbank-Workloads, niedrige Komplexität, einheitlicher Stack vereinfacht Operations.
  • BTRFS bewusst nicht für Single-Disk-Cache: Mehrwerte (RAID1, Snapshots) werden ohne zweite NVMe nicht realisiert; zusätzlicher COW-Overhead und Komplexität ohne Gegenwert.
  • ZFS bewusst nicht für aktuellen Bedarf: höherer RAM-Verbrauch, in der bestehenden Topologie kein Mehrwert; spätere Migration eines dedizierten Pools auf ZFS bleibt offen (siehe Abschnitt 13).

Eskalationspfad bei zweiter Cache-NVMe: Migration zu BTRFS-Pool (RAID1) als bewusste Architektur-Entscheidung mit eigenem Migrations-Plan. Bis dahin: Single-XFS.

4. Logisches Layout — User-Shares

Verbindliche Share-Definition. Jeder Share hat genau einen primären Datenträger.

Share Cache-Setting Primary Storage Allocation Split Level Zweck
appdata only Cache High Water 1 Container-Persistenz, niemals auf Array
system only Cache High Water Docker-Image bzw. Folder-Mode-Verzeichnis, libvirt — bewusst only, weil Mover-Migration der Docker-Image-Datei oder von libvirt-State zwischen FS-Typen riskant ist
domains only Cache High Water 1 VM-Disk-Images — Performance und Stabilität für produktive VMs (HAOS, hermes-runner). Wenn ältere/seltene VM-Images aufs Array dürfen, bewusst auf prefer herabsetzen, dokumentiert pro VM
isos yes Cache → Array High Water 2 VM-Installations-ISOs, optional
services no Disk1 High Water 2 Gitea, Komodo-State, secrets-Pfad — recovery-kritisch, siehe Hinweis unter Tabelle
documents no Disk1 High Water 2 Persönliche Dokumente
photos no Disk1 High Water 2 Persönliche Fotos
backups no Disk1 High Water 2 Lokale Borg-Repos, Appdata-Backup-Archive
media no Disk1 High Water 2 Optional, Media-Files
finance no Disk1 High Water 2 Persönlich
projekte no Disk1 High Water 2 Persönlich

Erklärung der Cache-Settings:

  • only: Daten landen ausschließlich auf Cache, Mover ignoriert sie. Schutz vor Mover-induzierter Daten-Migration zwischen Filesystemen.
  • prefer: Neue Daten landen auf Cache, Mover schiebt sie bei Cache-Druck auf Array.
  • yes: Neue Daten landen auf Cache, Mover schiebt sie regelmäßig auf Array (klassisches Cache-Schreibverhalten).
  • no: Daten umgehen Cache, gehen direkt auf Array.

Wahl only für appdata, system, domains ist die wichtigste Setting in diesem Dokument. Sie verhindert genau die Klasse von Vorfällen, die im Mai 2026 passiert ist: dass Container-Daten, Docker-Image-State oder VM-Disks unbemerkt zwischen Filesystemen wandern. Wenn der Cache voll wird, ist das ein Kapazitätsproblem, kein Filesystem-Problem — und wird durch zweite NVMe oder Cache-Tuning gelöst, nicht durch Mover-Migration.

services ist recovery-kritisch. Gitea (operative Source of Truth des GitOps-Stacks) und Komodo-State (Deployment-Runtime) liegen dort. Ohne services ist Self-Recovery nicht mehr möglich. Daraus folgt:

  • services wird vor jedem strukturellen Eingriff am Array (Disk1-Migration Phase 2, Disk-Tausch, Pool-Umzug) vollständig auf zweites Medium gesichert
  • services darf nicht auf einem als instabil bekannten Datenträger leben — Warnung in posture-check, Eskalation bei Disk1-FS-Anomalien
  • Änderungen an Gitea-Repos, Komodo-Stacks oder secrets-Pfad nur nach erfolgreichem letzten Backup-Check (siehe §11)
  • Mirror-Backup für Gitea-Repo-Inhalte (Operator-Entscheidung 2026-05-15, verbindlich): zusätzlich zum Standard-Borg-Lauf wird services/gitea/git/repositories/ per separatem Mirror-Mechanismus auf ein zweites physisches Medium außerhalb Disk1 gesichert, mit Frequenz ≤ 6 h. Konkrete Implementierung (Optionen: git bundle-Bundles in separates Repo auf Cache, rsync auf externe Platte, separates Borg-Repo mit kürzerem Schedule) wird in docs/SERVICES_RECOVERY.md (zu erstellen) detailliert. Begründung: Gitea ist operative Source of Truth des GitOps; Verlust seit letztem Standard-Backup (bis zu 24 h) wäre für Recovery-Pfad unzumutbar.

Erklärung Allocation-Method: „High Water" verteilt neue Daten auf der am wenigsten gefüllten Disk bis zu einer Wasserlinie, dann auf die nächste. Praktisch unauffällig bei Single-Array-Disk; wird relevant bei Erweiterung.

Split Level bestimmt, wie tief Verzeichnisse über Disks aufgeteilt werden dürfen. Für appdata zwingend 1 — ein Stack-Verzeichnis bleibt auf einer Disk, wird nie aufgespalten.

5. Appdata-Sub-Struktur

Verbindliches Layout pro Stack:

/mnt/user/appdata/<stackname>/
├── config/         # Statische Konfiguration (yaml, conf, env-Dateien)
├── data/           # Live-Datenverzeichnis (DB-Datendir, Container-Persistenz)
├── dumps/          # Pre-Backup-Hooks schreiben hier (pg_dump, mongodump, sqlite .backup)
├── logs/           # Container-Logs, falls nicht via journald gelöst
└── README.md       # Pflicht: Kurzbeschreibung, Restore-Zeiger, Sondereigenheiten

Regeln:

  • Ein Verzeichnis pro Stack, kein Mischen mehrerer Stacks
  • Stack-Namen sind lowercase, Bindestriche statt Unterstriche (z. B. mail-archiver, nicht Mail_Archiver)
  • data/ enthält Live-State. Behandlung pro Stack klassifiziert in RESTORE_MATRIX.md:
    • Reines DB-Datenverzeichnis (z. B. raw Postgres-data/, raw Mongo-data/): wird nicht direkt gesichert, ist durch dumps/ abgedeckt
    • Echte Nutzdaten (z. B. Immich-Uploads, Paperless-Dokumente, Gitea-Repo-Inhalte, Vaultwarden-Attachments, Mealie-Bilder, Grafana-Dashboards/Provisioning): werden direkt gesichert oder über expliziten Export-Mechanismus erfasst
    • Mischformen (z. B. ein Stack mit DB-Files und Nutzdaten im selben data/): pro Stack im Detail spezifiziert, mit getrennten Sub-Pfaden wo möglich
  • dumps/ enthält von Pre-Hooks erzeugte konsistente Dumps; wird immer von Borg gesichert
  • config/ ist die einzige Quelle der Container-Konfiguration; idealerweise versioniert oder aus Git deploybar
  • README.md pro Stack: drei Zeilen genügen (was, warum, Restore-Zeiger)

Ausnahmen, wenn Container das Layout nicht zulassen: dokumentiert in docs/SERVICE_CATALOG.md pro Stack, mit Verweis hier.

6. Naming-Konventionen

Element Konvention Beispiel
Stack-Name kebab-case, lowercase, keine Sonderzeichen vaultwarden, mail-archiver, immich-server
Container-Name identisch zu Stack-Name (oder <stack>-<role>), kebab-case postgresql-17, immich-postgres, komodo-mongo
Volume-Pfad in Compose /mnt/user/appdata/<stack>/<sub> /mnt/user/appdata/vaultwarden/data:/data
Netz-Name frontend_net, backend_net, oder <stack>_internal frontend_net, immich_internal
Env-Datei <stack>.env, im Komodo-Stack-Workspace, nicht im Repo

Begründung Bindestriche: vermeiden Verwechslung mit Shell-Variablen (Unterstriche werden in env vars verwendet), passen besser zu Docker-Compose-Konventionen, bleiben URL-safe.

Migration Bestand → kebab-case (Operator-Entscheidung 2026-05-15): existierende Stacks mit Unterstrichen oder gemischten Konventionen (postgresql17, immich_postgres, komodo-mongo etc.) werden im Rahmen der Recovery-Phase (Cache-Restore Phase 1, Disk1-Migration Phase 2) auf kebab-case migriert. Pro Stack: alter Name → neuer Name in docs/RESTORE_MATRIX.md dokumentiert. Container-Renames erfolgen sauber per Compose-Down → Rename → Compose-Up mit neuer Identität, nicht im laufenden Betrieb. Netzwerk-Referenzen, Healthchecks und Inter-Stack-Abhängigkeiten werden im selben Schritt mit angepasst. Nicht-migrierte Stacks am Ende der Recovery-Phase werden in §20 als bewusste Ausnahme dokumentiert oder auf einen Folge-Termin verschoben.

7. Permissions-Modell

Anwendungsfall UID:GID Begründung
Standard-Files in appdata/ 99:100 (nobody:users) Unraid-Standard, von shfs/Mover sauber behandelt
Container-spezifischer User individuell, dokumentiert manche Images erwarten zwingend 1000:1000 o. ä.
secrets/-Verzeichnis 0:0, mode 0700 oder 0750 Schutz vor versehentlichem Read
logs/ 99:100, mode 0755 Lesbar für Debugging, nicht weltschreibbar
dumps/ 99:100, mode 0750 Backup-Hook schreibt, Borg liest

Regel: Permission-Setzung beim Stack-Restore ist explizit per Skript oder Init-Container, nicht „erstmal chown -R root:root und gucken was kaputtgeht". Dokumentiert pro Stack.

8. Backup-Architektur

8.0 Aktueller Ist-Zustand vs. Ziel-Zustand

Operator-Entscheidung 2026-05-15: Backrest wird abgeschaltet. Borg ist die alleinige Backup-Technologie. Restic-Repos werden nicht weiterentwickelt; vorhandene Restic-Daten verbleiben als historisches Material bis zur expliziten Entsorgung.

Ist-Zustand zum Zeitpunkt der Inkraftsetzung:

  • Borg via Hetzner Storagebox: produktiv, im Vorfall 2026-05-11 als Restore-Quelle verifiziert
  • Backrest/restic: wird abgeschaltet — Container nicht mehr starten, Repo-Daten archivieren oder löschen, Stack-Eintrag aus Compose entfernen
  • Appdata-Backup-Plugin (CIFS auf WD MyBookLive): abgeschaltet, Ziel komplett aus Setup entfernt
  • Lokales Borg-Repo auf /mnt/user/backups/borg/: einzurichten falls noch nicht vorhanden, als zweites Borg-Ziel neben Hetzner

Ziel-Zustand (was dieses Dokument definiert):

  • Borg ist die einzige produktive Backup-Technologie (lokal + remote)
  • Backup-Pfade, Pre-Hooks und Verifikation einheitlich über Borg
  • Migrationspfad Backrest-Abschaltung pro Stack in docs/RESTORE_MATRIX.md

Dieses Dokument definiert die Zielarchitektur. Abweichungen zwischen Ist und Soll sind dokumentiert in docs/RESTORE_MATRIX.md.

8.1 Backup-Ziele

Ziel Typ Zweck Retention
/mnt/user/backups/borg/ (lokal) Borg-Repo auf Disk1 Schneller lokaler Restore 30 Tage täglich, 12 Monate monatlich
Hetzner Storagebox Borg-Repo via SSH Off-Site, Disaster Recovery 90 Tage täglich, 24 Monate monatlich
Externe Wechselplatte Cold Storage, manuell Air-Gap, Recovery-Material rotiert manuell, kein festes Schema

WD MyBookLive ist explizit kein Backup-Ziel mehr. Eintrag in docs/SECRETS_MAP.md und allen Backup-Konfigs entfernt.

8.2 Backup-Inhalt

Pflicht-Backup-Scope (Borg, beide Repos):

  • /mnt/user/appdata/*/config/
  • /mnt/user/appdata/*/dumps/
  • /mnt/user/appdata/*/README.md
  • /mnt/user/appdata/*/data/ für Stacks mit Nutzdaten in data/ (siehe §5 und Klassifikation in RESTORE_MATRIX.md)
  • /mnt/user/services/
  • /boot/config/ (Unraid-Konfiguration)
  • persönliche Daten vollständig im Scope (Operator-Entscheidung 2026-05-15):
    • /mnt/user/documents/
    • /mnt/user/photos/
    • /mnt/user/finance/
    • /mnt/user/projekte/
    • /mnt/user/media/ (falls behalten)

Borg dedupliziert über Läufe, der erste Vollbackup ist groß, Folge-Backups inkrementell. Retention nach §8.1.

Pflicht-Excludes (mit Vorsicht — siehe §5):

  • **/data/ innerhalb appdata/*/ nur dann, wenn data/ ausschließlich rohes DB-Datenverzeichnis ist (durch dumps/ abgedeckt). Stacks mit echten Nutzdaten in data/ (Immich, Paperless, Gitea, Vaultwarden, Mealie, Grafana etc.) sind aus diesem Exclude explizit ausgenommen — pro Stack klassifiziert in docs/RESTORE_MATRIX.md. Unklassifizierte Stacks gelten als „nicht ausschließbar" — lieber zuviel sichern als versehentlich zu wenig.
  • **/cache/, **/tmp/, **/lost+found/ (jeweils generisch unbedenklich)
  • Container-Image-Layer (/var/lib/docker/)
  • VM-Disk-Images (/mnt/user/domains/) außer auf Operator-Entscheidung pro VM

8.3 Pre-Backup-Hooks pro DB-Stack

Verbindlich. Jeder Stack mit eigener DB hat einen Pre-Hook, der einen konsistenten Dump nach appdata/<stack>/dumps/ schreibt:

Stack-Typ Befehl Ziel
Postgres pg_dump -Fc oder pg_dumpall --globals-only appdata/<stack>/dumps/<dbname>.dump
MariaDB/MySQL mariadb-dump --single-transaction appdata/<stack>/dumps/<dbname>.sql
MongoDB mongodump --archive=... appdata/<stack>/dumps/mongo.archive.gz
SQLite (Vaultwarden, Gitea) sqlite3 db .backup target.db appdata/<stack>/dumps/<dbname>.sqlite
Redis redis-cli BGSAVE plus Kopie der dump.rdb appdata/<stack>/dumps/dump.rdb

Dumps sind nur dann wertvoll, wenn sie regelmäßig gegen Restore-Test verifiziert werden. Siehe Abschnitt 11.

8.4 Backup-Verifikation

Frequenz Prüfung Pass-Kriterium
Pro Backup-Lauf Exit-Code, Dauer im erwarteten Korridor, Archivgröße im erwarteten Korridor alle drei grün, sonst Alarm
Wöchentlich borg check --repository-only auf beiden Repos fehlerfrei
Monatlich Probe-Restore eines zufällig ausgewählten Stacks aus Hetzner-Repo in Wegwerf-Pfad restored Files lesbar, DB-Dump per pg_restore --list lesbar
Quartalsweise Voll-Restore-Drill einer kompletten App-Klasse (z. B. Gitea allein) auf Test-Pfad App startet, Login funktioniert

Backup-Verifikations-Ergebnisse landen in /mnt/user/services/backup-verify/ als Log mit Zeitstempel.

9. Mover-Policy

  • Mover läuft nicht auf einem Schedule, der parallel zu Backup-Jobs liegt
  • Mover wird niemals für Shares mit cache: only aktiv
  • Mover wird vor planmäßigen Reboots manuell ausgelöst und abgewartet
  • Mover-Logs werden vom Posture-Check auf Anomalien geprüft

10. Network-Bezug zu Storage

Reine Verweis-Sektion. Authoritative Definition in HOMELAB_ARCHITECTURE_MASTER_V2.md.

  • Datenbanken, die Storage-intensiv sind, hängen ausschließlich an backend_net (internal: true), niemals an frontend_net
  • Backup-Container hängen entweder an backend_net (für DB-Connect) oder eigenem internen Netz, nicht an frontend_net
  • Es gibt keine Storage-Pfade, die über frontend_net-Container exponiert werden

11. Posture-Check (Pflicht-Audit)

Automatisierter Check, der mindestens täglich läuft (z. B. via User-Script oder Cron im posture-check-Stack), und bei jedem Fail einen Alarm produziert.

Zusätzlich zwingend ausgelöst:

  • Bei jedem Boot des Hosts (Unraid User-Script at start, oder systemd-äquivalent) — fängt sofort ab, wenn nach Reboot ein Mount falsch ist
  • Vor jedem Backup-Lauf als Pre-Hook — wenn Posture-Check fehlschlägt, wird das Backup abgebrochen und alarmiert, statt fragwürdige Daten zu sichern und so die Backup-Historie zu kontaminieren
  • Nach jedem Mover-Lauf als Post-Hook — verifiziert, dass Mover keine cache: only-Shares angefasst hat
  • Vor jeder strukturellen Änderung (Disk-Tausch, Pool-Umzug, Format-Aktion) manuell — Soll-Ist-Abgleich vor destruktiven Aktionen

Alarmziel (Operator-Entscheidung 2026-05-15, normalisiert 2026-05-17): ntfy. Problem-Alerts gehen an den selbst gehosteten ntfy-Server https://ntfy.kaleschke.info mit dem Topic homelab-alerts. Optionale Erfolgsmeldungen gehen an homelab-info. Push-Empfänger sind die Mobil-Geräte des Operators. Optional als Folge-Erweiterung: E-Mail-Fallback über mail-archiver-Stack als persistente Trail für historische Auswertung — nicht jetzt entscheiden, kann später dazukommen.

Pflichtprüfungen:

Check Erwartung Bei Fail
findmnt -no FSTYPE /mnt/cache xfs Sofortalarm, kritisch
findmnt -no FSTYPE /mnt/disk1 xfs (nach Phase 2) Sofortalarm, kritisch
mount enthält keinen ntfs3- oder fuseblk-Eintrag auf Cache/Array wahr Sofortalarm
nvme smart-log /dev/nvme0n1 Critical Warning 0x00 Sofortalarm
nvme smart-log Media Errors 0 oder stabil zur Vorwoche Warnung
D-State-Prozesse > 60s keine Warnung, ab 5 Min Alarm
iowait über 5-Min-Mittel < 30 % Warnung ab 50 %, Alarm ab 80 %
Letztes erfolgreiches Borg-Archiv (lokal) < 26 h alt Warnung ab 30 h, Alarm ab 48 h
Letztes erfolgreiches Borg-Archiv (Hetzner) < 26 h alt Warnung ab 30 h, Alarm ab 48 h
Erwartete Mindestgröße pro Borg-Archiv matcht Profil pro Stack Warnung
CIFS/NFS-Mount-Liveness (für jeden konfigurierten Remote-Mount) stat mit 10s-Timeout erfolgreich Sofortalarm
Anzahl laufender Container matcht Soll aus Repo Warnung

Implementierung: Skript unter services/posture-check/posture-check.sh plus Kompatibilitaets-Wrapper services/posture-check/posture_check.sh, ausgegeben als JSON nach /mnt/user/services/posture-check/last.json, konsumiert von ntfy.

12. Hard Rules — Constitution

Diese Regeln sind nicht optional. Verstoß ist Incident, kein Feature-Request.

  1. Kein NTFS, exFAT, FAT32 auf Cache, Pool, Array. Nur XFS, BTRFS, ZFS.
  2. Keine Compose-Bind-Mounts auf /mnt/cache/... oder /mnt/disk1/.... Immer /mnt/user/....
  3. Keine Container schreibt nach /mnt/disks/... (Unassigned Devices) im Dauerbetrieb.
  4. Keine latest-Image-Tags in Production-Compose. Versionspin oder bewusster :stable/:lts-Alias.
  5. Keine Secrets im Repo. Niemals.
  6. Kein Backup-Ziel über CIFS mit Hard-Mount ohne Liveness-Check.
  7. Keine produktive Stack-Änderung ohne Compose-Commit im Repo.
  8. Keine Disk- oder Pool-Änderung ohne Update dieses Dokuments im selben Commit.
  9. Kein neuer Stack ohne Eintrag in STORAGE_LAYOUT.md (Share/Pfad), SERVICE_CATALOG.md (Funktion), RESTORE_MATRIX.md (Restore-Pfad).
  10. Kein Pre-Backup-Hook, der DBs nicht konsistent dumped, für Stacks mit eigener DB.
  11. Kein produktiver Stack ohne dokumentierten Restore-Pfad in docs/RESTORE_MATRIX.md. Idealerweise mit dokumentiertem Restore-Test (≤ 90 Tage alt); bei fehlendem Test mindestens schriftliche Restore-Schritte und Backup-Quelle. Stacks ohne diesen Eintrag laufen nicht produktiv — entweder dokumentieren oder abschalten.
  12. Kein Backup-Lauf ohne vorgeschalteten Posture-Check (siehe §11). Backup auf kompromittiertem Filesystem überschreibt unter Umständen den letzten guten Stand und kontaminiert die Backup-Historie.

Dokumentierte Host-Observability-Ausnahmen (Operator-Entscheidung 2026-05-16): glances, scrutiny, monitoring-promtail, monitoring-node-exporter und monitoring-cadvisor duerfen gezielt Host-/Device-Bind-Mounts ausserhalb /mnt/user/... nutzen, weil ihre Kernfunktion sonst nicht erfuellbar ist. Erlaubt sind nur die in docs/SERVICE_CATALOG.md pro Dienst genannten Binds. Diese Ausnahmen sind keine Datenpersistenz-Pfade und duerfen nicht fuer Appdaten, Backups oder normale Service-Konfiguration erweitert werden. Neue oder geaenderte Host-Binds brauchen eine explizite Doku-Aenderung im selben Commit.

13. Soft Rules — Konventionen

Erwartet, aber begründbare Abweichungen sind dokumentiert.

  • Stack-Verzeichnisse werden mit der Konvention aus Abschnitt 5 angelegt
  • Ein Stack hat einen README.md in seinem appdata/-Verzeichnis
  • Image-Tags werden quartalsweise auf Updates geprüft, dokumentiert in einem Update-Log
  • Container ohne klare Eigentümerschaft werden quartalsweise reviewed (rauswerfen oder in Doku aufnehmen)
  • Neue Shares werden grundsätzlich mit Cache-Setting no angelegt, Promotion auf prefer/only ist bewusste Entscheidung
  • Compose-Dateien sind kommentiert, wenn die Konfiguration nicht selbsterklärend ist

14. Anti-Patterns mit Begründung

Anti-Pattern Warum Verboten
Direkter /mnt/cache/X-Bind-Mount in Compose Pfad verschwindet, wenn Share-Setting auf Array migriert wird; bricht beim ersten Mover-Lauf
chown -R 1000:1000 /mnt/user/appdata blanket Bricht alle Stacks, die andere UIDs erwarten; bei Mover-Lauf werden Permissions ggf. überschrieben
Backup direkt von Live-DB-Datendir Inkonsistent unter Last; WAL-Divergenz nach Restore möglich
latest-Tags in Production Reproduzierbarkeit weg; Rollback nur per Glück möglich
Mehrere Stacks im selben Appdata-Verzeichnis Backup-Excludes greifen falsch; Restore betrifft Nachbar-Stack
Cache-Filesystem ohne automatisierten Posture-Check Genau der Vorfall vom 2026-05-11
Hard-mounted CIFS auf wackelige Geräte D-State-Prozesse können den ganzen Host blockieren
Secret in Compose-Env (statt env_file) Secret im Repo lesbar; bei git log -p für immer drin
Stack ohne dokumentierten Restore-Pfad Bei Recovery muss improvisiert werden; das war der Auslöser dieses Dokuments

15. Migrations- und Wachstumspfade

15.1 NTFS-zu-XFS-Migration (Cache, Phase 1)

Behandelt in docs/DISASTER_RECOVERY.md, Sektion „Cache Recovery 2026-05".

15.2 NTFS-zu-XFS-Migration (Disk1, Phase 2)

Folgeprojekt nach mindestens 7 Tagen stabilem Cache-Betrieb. Eigener Plan in docs/DISASTER_RECOVERY.md. Grobschritte:

  1. Selektive Sicherung aller Disk1-Inhalte auf zweite Disk oder Hetzner
  2. Hash-Verifikation der Sicherung
  3. Disk1 entfernen aus Array (oder leer, wenn Daten temporär auf zweiter Disk)
  4. Disk1 als XFS in Unraid-GUI neu formatieren
  5. Daten zurückspielen
  6. Parity neu bauen
  7. Posture-Check grün

15.3 Erweiterung um zweite Cache-NVMe

Trigger: Cache-Auslastung > 70 %, oder Wunsch nach RAID1 für Appdata.

Pfad: BTRFS-Pool aus zwei NVMe (RAID1) als Ersatz für Single-XFS-Cache. Migration über Pool-Wechsel mit zwischengeschalteter Image-Sicherung. Eigenes Migrations-Dokument vor Beginn.

15.4 Erweiterung um zweite Array-Disk

Trigger: Disk1 > 80 % voll, oder Performance-Bedarf.

Pfad: zweite Daten-Disk dem Array hinzufügen, Allocation-Method überprüfen, Split-Level pro Share validieren. Parity-Disk muss ≥ größte Daten-Disk bleiben.

15.5 Multi-Host (Hermes-Skalierung)

Wenn Hermes-Worker auf weiteren Hosts skaliert: dieser Storage-Layout-Plan gilt zunächst nur für Kallilabcore. Weitere Hosts brauchen eigenes Storage-Layout-Dokument. Gemeinsamer Storage (NFS, iSCSI, S3-API) ist ein eigener Architektur-Schritt mit eigenem Dokument.

16. Glossar

Begriff Bedeutung
Cache (Pool) Schneller Pool (NVMe), nicht Teil des Arrays, ohne Parity-Schutz, für Performance-kritische Daten
Array Klassische Unraid-Daten-Disks plus Parity, paritätsgeschützt, langsamer
shfs Unraid-User-Share-FUSE-Filesystem; vereinigt Cache und Array unter /mnt/user/
Mover Unraid-Service, der Daten zwischen Cache und Array gemäß Cache-Setting verschiebt
Cache-Setting Pro Share: only, prefer, yes, no — steuert Mover-Verhalten
Allocation Method High Water / Most Free / Fill Up — wie Daten auf Array-Disks verteilt werden
Split Level Maximale Verzeichnistiefe, in der Unraid Inhalte über Disks aufteilen darf
Pre-Backup-Hook Skript, das vor jedem Backup einen konsistenten DB-Dump erzeugt
Posture-Check Automatisierter Audit, der Host-Realität gegen Soll-Zustand prüft
Constitution Hard Rules in Abschnitt 12, nicht verhandelbar

17. Referenzen

  • HOMELAB_ARCHITECTURE_MASTER_V2.md — Gesamtarchitektur, Netze, Traefik, GitOps
  • docs/REPO_MAP.md — Repo-Struktur, wo welcher Stack lebt
  • docs/SERVICE_CATALOG.md — Pro Stack: Funktion, Abhängigkeiten, Eigenheiten
  • docs/RESTORE_MATRIX.md — Pro Stack: Restore-Quelle und -Verfahren
  • docs/SECRETS_MAP.md — Pro Secret: Speicherort, Rotation, Recovery
  • docs/DISASTER_RECOVERY.md — Konkrete Recovery-Pläne, inkl. NTFS-Migration
  • docs/GITOPS_DRIFT_RUNBOOK.md — Drift-Erkennung und -Behebung
  • docs/AI_CONTEXT.md — Kontext für AI-Assistenten
  • docs/HARDWARE_INVENTORY.md — physische Host-, Disk- und Health-Baseline

18. Changelog

Version Datum Änderung Autor
1.0 (Draft) 2026-05-15 Initialfassung nach Incident 2026-05-11. Ursprung: NTFS-Cache-Korruption, fehlende Posture-Checks, ungeplante Backup-Strategie. Operator + AI-Assistenten
1.1 (Draft) 2026-05-15 Operator-Review-Feedback eingearbeitet: system/domains auf only, services als recovery-kritisch markiert, data/-Behandlung pro Stack klassifiziert (statt blanket Exclude), Backup-Tooling Ist/Soll explizit getrennt, Posture-Check zusätzlich bei Boot/vor Backup/nach Mover, Hard Rules 11+12 ergänzt (Restore-Pfad-Pflicht, Posture-vor-Backup), Alarmziel-Optionen benannt, Review-Items in eigene Sektion §20 verschoben. Operator + AI-Assistenten
1.2 (Draft) 2026-05-15 Operator-Entscheidungen #3, #4, #6, #9, #11 eingearbeitet: Backrest abgeschaltet (Borg alleinige Backup-Technologie), persönliche Daten vollständig im Pflicht-Backup-Scope, ntfy als Alarmziel verbindlich, kebab-case-Migration im Rahmen der Recovery-Phase, Mirror-Backup für Gitea-Repo-Inhalte als verbindliche Spec (Implementierung in SERVICES_RECOVERY.md zu detaillieren). Offen: Items #1, #2, #5, #7, #8, #10. Operator + AI-Assistenten
1.3 (Draft) 2026-05-15 Operator-Entscheidungen #1, #7, #8 eingearbeitet: Disk-Größen/-Modelle als Deferred via Posture-Check-Detection (kein Blocker), optionale Stacks (Filebrowser, code-server, Speedtest, Scrutiny, Uptime-Kuma) bleiben im Layout und sind produktiv, Network-Verweis auf MASTER_V2 bestätigt. Damit alle akuten Items entschieden. Verbleibend: Items #2, #5, #10 (Retention, Schwellen-Kalibrierung, RESTORE_MATRIX-Klassifikation) — alle als Folge-Aufgaben über Inkraftsetzung hinaus, kein Commit-Blocker. Operator + AI-Assistenten
1.4 (Active) 2026-05-27 Datei von docs/STORAGE_LAYOUT.draft.md auf docs/STORAGE_LAYOUT.md gehoben; Hardware-/Diskwerte aus docs/HARDWARE_INVENTORY.md übernommen; Gitea-Bundle-Mirror und H:/ Nearline-Pull als umgesetzte Folgepfade referenziert. Verbleibend: Retention-Kalibrierung, Monitoring-Schwellen und RESTORE_MATRIX-Detailklassifikation als normale Folgeaufgaben. Operator + AI-Assistenten

19. Inkraftsetzung

Dieses Dokument tritt in Kraft mit dem Commit der finalen Fassung in master-Branch des Repos homelab-infra. Ab Inkraftsetzung gelten alle Hard Rules; Soft Rules werden im Rahmen der laufenden Recovery-Arbeit etabliert; Posture-Check muss innerhalb von 14 Tagen nach Inkraftsetzung produktiv laufen.

Erste Audit-Review dieses Dokuments: spätestens 90 Tage nach Inkraftsetzung. Danach jährlich oder bei jeder strukturellen Änderung des Storage-Layouts.

20. Review Items und Folgeaufgaben

Diese Sektion dokumentiert erledigte Review-Punkte und verbleibende Folgeaufgaben nach Aktivierung des Dokuments.

Nr. Item Status Verantwortung
1 Disk-Größen und Modelle in §3 (Disk1, Parity, externe Backup-Platte) ERLEDIGT 2026-05-27 — Werte aus docs/HARDWARE_INVENTORY.md übernommen; Disk1-Filesystem ist seit 2026-05-25 XFS Operator + Posture-Check
2 Retention-Werte in §8.1 (Borg-Repos lokal/remote) — abhängig von tatsächlicher Storage-Kapazität Vorschlag steht, anzupassen Operator
3 Backup-Tooling: Backrest abschalten, Borg alleinige Backup-Technologie ENTSCHIEDEN 2026-05-15 erledigt (siehe §8.0)
4 Backup-Scope für persönliche Daten: documents, photos, finance, projekte (und media falls behalten) vollständig im Pflicht-Scope ENTSCHIEDEN 2026-05-15 erledigt (siehe §8.2)
5 Posture-Check-Schwellen in §11 — Vorschlag konservativ, nach erstem Monitoring kalibrieren Vorschlag steht Operator nach 30 Tagen
6 Alarmziel: ntfy als primärer Push-Kanal, Mail-Fallback als Folge-Erweiterung optional ENTSCHIEDEN 2026-05-15 erledigt (siehe §11)
7 Optionale Stacks (Filebrowser, code-server, Speedtest, Scrutiny, Uptime-Kuma) — bleiben im Layout, sind produktiv im Scope, folgen den Standard-Konventionen aus §5 und Backup-Pflichten aus §8.2 ENTSCHIEDEN 2026-05-15 erledigt
8 Verweis auf HOMELAB_ARCHITECTURE_MASTER_V2.md für Network-Architektur (§10) — Net-Architektur steht dort authoritativ, Verweis ist ausreichend ENTSCHIEDEN 2026-05-15 erledigt (siehe §10)
9 Naming-Konvention: kebab-case durchziehen, Migration im Rahmen der Recovery-Phase ENTSCHIEDEN 2026-05-15 erledigt (siehe §6); pro Stack in RESTORE_MATRIX.md zu dokumentieren
10 Pro-Stack-Klassifizierung in RESTORE_MATRIX.md (DB-Typ, Nutzdaten in data/, Dump-Verfahren, letzter Restore-Test, kebab-case-Migrationsname) — als Folge-Aufgabe aus Hard Rule §12.11 Folge-Aufgabe Operator + Recovery-Phase
11 Mirror-Backup für services/gitea/git/repositories/ auf zweites Medium, ≤ 6 h Frequenz, konkrete Implementierung in docs/SERVICES_RECOVERY.md zu erstellen ERLEDIGT 2026-05-26ops/borg-ui/scripts/gitea-bundle-mirror.sh erzeugt verifizierte Bundles; Host-Erstlauf mit 4 Bundles und git fsck erfolgreich Operator + Folge-Doc

Das Dokument ist mit Version 1.4 als Active geführt. Offene Punkte 2, 5 und 10 bleiben normale Folgeaufgaben und blockieren die Gültigkeit der Hard Rules nicht.