# H:/ Nearline-Backup — Struktur und Betrieb Stand: 2026-06-21 ## Rolle der H:/ Die externe HDD (asmedia ASM235, 7.4 TB, Laufwerk `H:`) dient ausschließlich als **Nearline-Backup-Spiegel** für kritische Dumps und Git-Bundles. Sie ist kein Primär-Backup (das ist Hetzner/Borg) und kein dauerhaftes Archiv. ## Sollzustand ``` H:\ └── kallilab-nearline-backups\ ├── borg-dumps\latest\ ← aktuelle DB-Dumps (per Script) ├── git-bundles\gitea\ ← Gitea-Repo-Bundles (per Script) ├── _dr-kit\ ← SSH-Keys, Offline-Secrets (manuell) ├── _logs\ ← Robocopy-Logs je Lauf └── _reports\ ← Markdown-Reports je Lauf ``` Nichts weiteres gehört dauerhaft auf die H:/. Temporäre Recovery- oder Backup-Ordner aus Notfallsituationen sind nach Abschluss zu löschen. ## Restore aus H:/ (DR-Fall) Wenn Unraid/Hetzner nicht erreichbar sind, ist die H:/ die schnellste **lokale** Quelle für die aktuellsten DB-Dumps und Gitea-Bundles. Sie ersetzt **nicht** die Hetzner-Borg-Kette (Einordnung: `docs/CAPACITY_AND_LIFECYCLE.md`), verkürzt aber den Wiederanlauf, solange die Artefakte frisch sind. Inhalt und Restore-Weg: - **DB-Dumps:** `H:\kallilab-nearline-backups\borg-dumps\latest\*` — dieselben Dateien wie `/mnt/user/backups/borg/dumps/latest`. Im DR-Fall auf den neu aufgesetzten Host nach `/mnt/user/backups/borg/dumps/latest` zurückspielen (SMB/Robocopy), dann pro Dienst nach `docs/RESTORE_MATRIX.md` einspielen. - **Gitea-Bundles:** `H:\kallilab-nearline-backups\git-bundles\gitea\` — bare Repo-Bundles für den Gitea-Bootstrap (Reihenfolge: `docs/SERVICES_RECOVERY.md`). - **DR-Kit (Keys/Secrets):** `H:\kallilab-nearline-backups\_dr-kit\` — SSH-Keys und Offline-Secrets, siehe Abschnitt `_dr-kit` unten. > **Vor dem Verlassen auf H:/ immer die Frische prüfen:** Die LastWriteTime der > Dumps muss vom selben Tag sein > (`Get-ChildItem H:\kallilab-nearline-backups\borg-dumps\latest`). Ein still > veralteter Spiegel (siehe S4U-Vorfall unten) ist als Restore-Quelle wertlos — > dann stattdessen aus Hetzner-Borg restaurieren. ## Automatischer Pull `pull-critical-backups.ps1` zieht per Robocopy vom Unraid-SMB-Share: - `\\192.168.178.58\backups\borg\dumps\latest` → `borg-dumps\latest\` - `\\192.168.178.58\backups\git-bundles\gitea` → `git-bundles\gitea\` Der Windows Scheduled Task `KalliLab H Drive Nearline Pull` laeuft seit 2026-05-28 taeglich 05:30. Das Script kopiert bewusst **nicht** mit `/MIR` und loescht nichts auf H:/; alte Artefakte werden nur nach manueller Sichtpruefung entfernt. > **Wichtig — Task-LogonType (kein S4U!):** Der Task darf **nicht** mit `S4U` > laufen ("Unabhaengig von der Benutzeranmeldung ausfuehren" *mit* angehaktem > "Kennwort nicht speichern"). S4U-Laeufe haben keine Netzwerk-Anmelde- > informationen und erreichen den authentifizierten SMB-Share > `\\192.168.178.58\backups` nicht -> jeder geplante Lauf bricht still mit > Exitcode 1 ab, ohne Report. Genau das passierte 2026-06-19 bis 2026-06-21 > (Spiegel still veraltet). > > **Behoben am 2026-06-21:** Task auf **"Nur ausfuehren, wenn der Benutzer > angemeldet ist"** (LogonType `Interactive`) umgestellt. Das braucht **kein** > gespeichertes Passwort und funktioniert, weil `michi` der dauerhaft > angemeldete Konsolen-User ist (gesperrter Bildschirm zaehlt als angemeldet). > Per Planer ausgeloest und mit Ergebnis `0x0` verifiziert. Alternative waere > LogonType `Password` (gespeichertes Passwort), erfordert aber das > Windows-Passwort. Aufruf zum Testen: ```powershell powershell.exe -NoProfile -ExecutionPolicy Bypass -File G:\Gitea_Clone\homelab-infra\ops\h-drive-nearline\pull-critical-backups.ps1 -WhatIf ``` Das Script schließt bewusst aus: - `unraid-flash-config.tar.gz` (0600 root:root, nicht per SMB zugänglich → Restore aus Hetzner-Borg) - Migration-/Cutover-Verzeichnisse (`immich-vectorchord-*`, `pg18-major-*`, `redis8-*` etc.) ## Externer Dead-Man's-Switch Der Pull lief ~2026-06-04 bis 2026-06-18 still gestoppt, ohne dass etwas Alarm schlug (Scheduled Task fehlte; Prometheus auf Unraid sieht den baerchen-Pull nicht). Gegenmittel: ein externer Heartbeat. `pull-critical-backups.ps1` pingt am Lauf-Anfang `/start`, am Ende den Erfolg und im Fehlerfall `/fail`. Bleibt der Erfolgs-Ping aus, alarmiert der externe Dienst von aussen. Die Integration ist **endpoint-agnostisch**: jede Healthchecks-kompatible URL funktioniert (Healthchecks.io-Cloud oder self-hosted). Ist keine URL gesetzt, ist der Switch ein No-Op und der Pull laeuft unveraendert weiter. URL-Quelle (in dieser Reihenfolge): 1. Parameter `-HealthchecksUrl` 2. ENV `HEALTHCHECKS_NEARLINE_URL` 3. Datei `%USERPROFILE%\.kallilab\healthchecks-nearline-url.txt` Die Ping-URL ist eine Capability-URL -> wie ein Secret behandeln, nie ins Repo (siehe `docs/SECRETS_MAP.md`). ### Operator-Setup (einmalig) 1. Check anlegen (Healthchecks.io oder self-hosted): Period = 1 Tag, Grace z. B. 2-3 h passend zum taeglichen 05:30-Task. Ping-URL kopieren. 2. Auf baerchen die URL hinterlegen, z. B. als Datei: ```powershell New-Item -ItemType Directory -Force -Path "$HOME\.kallilab" | Out-Null Set-Content -LiteralPath "$HOME\.kallilab\healthchecks-nearline-url.txt" -Value "https://hc-ping.com/" -NoNewline ``` (alternativ als persistente User-Umgebungsvariable `HEALTHCHECKS_NEARLINE_URL`.) 3. Testlauf: `pull-critical-backups.ps1` ausfuehren; im Healthchecks-Dashboard muss ein "success"-Ping ankommen. ### Borg-Pre-Hook-Pendant Den gleichen Switch gibt es host-seitig in `ops/borg-ui/scripts/pre-borg.sh`. URL dort via ENV `HEALTHCHECKS_BORG_URL` oder Datei `/mnt/user/appdata/secrets/healthchecks_borg_url` (chmod 600), bewusst als **eigener** Check (getrennter Heartbeat fuer die Unraid-Backup-Vorpruefung). ## _dr-kit Enthält offline hinterlegte Schlüssel und Secrets für den DR-Fall: - `dr-hetzner` / `dr-hetzner.pub` — SSH-Key für Hetzner Storage Box - `dr-readonly` / `dr-readonly.pub` — Read-only Deploy-Key - `KOmodo Secrets.txt` — Komodo Stack ENV-Offline-Dokumentation Diese Dateien sind **manuell** zu pflegen und **nicht** vom Pull-Script verwaltet. ## Archiv-Ordner Temporäre Notfall-Artefakte verbleiben als `_archiv-*`-Ordner bis zur bewussten Löschentscheidung: | Ordner | Inhalt | Anlassdatum | |---|---|---| | `kallilab-recovery\_archiv-nvme-crash-image-2026-05-14\` | nvme0n1 Disk-Image (1863 GB) + Crash-Runbooks aus dem Mai-2026-Ausfall | 2026-05-14 | ## Aufräum-Historie | Datum | Aktion | |---|---| | 2026-06-10 | `OneDrive - Stroetmann Group\` gelöscht (leer) | | 2026-06-10 | SSH-Keys + Secrets aus nearline-Root in `_dr-kit\` verschoben | | 2026-06-10 | Migration-Artefakt-Verzeichnisse in `borg-dumps\latest\` gelöscht (immich-vectorchord-*, pg18-major-*, redis8-*, nextcloud-redis-pre-redis8-*, shared-redis-pre-redis8-*) | | 2026-06-10 | Pre-major-prod-Dumps gelöscht (PG17→PG18-Migration abgeschlossen) | | 2026-06-10 | `kallilab-recovery\2026-05-15\` gelöscht (DNS-Restore-Reste) | | 2026-06-10 | `kallilab-recovery\2026-05-14\` → `_archiv-nvme-crash-image-2026-05-14\` umbenannt | | 2026-06-10 | `kallilab-recovery\disk1-phase2-2026-05-23\` gelöscht (1677 GB Media-Share-Kopie; Unraid-Share verifiziert vollständig) | ## Offene Punkte - `Windows-Neuaufsetzen-Backup\` (48 GB): nach vollständiger Rückspielung auf D:\ löschen - `_archiv-nvme-crash-image-2026-05-14\` (1863 GB): löschen sobald sicher, dass nichts mehr aus dem alten System benötigt wird - Log-Rotation für `_logs\` und `_reports\`: manuell oder per Script, Empfehlung 30 Tage