Zum Inhalt

Backup

Der AI-Server hängt sich in die bestehende 3-2-1-Strategie des Homelabs ein: ein lokales Ziel auf dem NUC, ein Offsite-Ziel auf der Hetzner Storage Box.

Borgmatic läuft nativ auf dem Host, nicht als Container — es braucht Root-Leserechte auf /etc und /root.

Die Systemplatte ist zusätzlich per Snapper gesnapshottet. Das ist ein Rollback-Werkzeug, kein Backup: Die Snapshots liegen auf derselben NVMe.

Versionen borgmatic 2.0.11 (Ubuntu universe), BorgBackup 1.4.4
Zeitplan täglich 20:00 (RandomizedDelaySec=15m)
Retention 7 täglich, 4 wöchentlich, 6 monatlich
Verschlüsselung repokey-blake2, eine Passphrase für beide Ziele
Archivgröße rund 50 MB

Der NUC fährt noch borgmatic 1.8.14

Die Konfiguration hier ist borgmatic-2-Syntax (flach) und lässt sich nicht 1:1 auf den NUC übertragen, der noch das alte verschachtelte Schema nutzt.

Ziele

Label Repository Transport
nuc ssh://robin@10.10.10.3/mnt/borg-repo-aiserver SSH-Key, borg serve per Forced Command
storagebox ssh://…@…your-storagebox.de:23/./backups/aiserver SSH-Key, Port 23

Auf dem NUC liegt der AI-Server in einem eigenen btrfs-Subvolume neben dem des NUC, nicht in einem Unterverzeichnis:

NUC AI-Server
Subvolume @borg @borg-aiserver
Mountpunkt /mnt/borg-repo /mnt/borg-repo-aiserver

Nicht /mnt/borg-repo/aiserver

/mnt/borg-repo ist selbst ein aktives Borg-Repository — das des NUC, mit config, data/, index und nonce. Ein Unterverzeichnis darin wäre ein fremder Ordner mitten in einer Produktionsdatenbank.

Beide Subvolumes liegen auf /dev/md0 (4× 4 TB RAID 5 im QNAP TR-004, btrfs mit compress=zstd:3). Der monatliche btrfs scrub erfasst das neue Subvolume automatisch mit.

Der eigene Mountpunkt ist nicht Kosmetik: nofail in der fstab heißt, dass der NUC auch ohne angeschlossenes TR-004 bootet. Dann existiert /mnt/borg-repo-aiserver schlicht nicht, und borg bricht hörbar ab, statt irgendwohin auf die System-SSD zu schreiben.

Was gesichert wird

Quelle Größe Warum
/etc 7,6 MB Die erarbeiteten Pflichteinstellungen: default/grub, modprobe.d/ttm.conf, Docker-Config, apt-Holds
/home/robin ~25 MB SSH-Keys, Skripte, Shell-Zustand (ohne Caches)
/opt/aiserver 1 MB Live-Checkout des Repos inklusive lokalem Git-Zustand
/opt/ai/eval 11 MB Messergebnisse und ASR-Sprachaufnahmen — nicht reproduzierbar
/opt/ai/volumes wächst Container-Daten (Open WebUI SQLite) — ohne Modell-Caches
/root/backup-info < 1 MB Zustandsabzug, siehe unten
/var/spool/cron/crontabs < 1 MB Der GPU-Textfile-Collector läuft als User-Cron

Ausgeschlossen, jeweils weil reproduzierbar und groß:

Pfad Größe Ersatzbeschaffung
/opt/ai/models 135 GB Ollama-Blobs und HF-GGUFs — erneut ziehbar
/opt/ai/npu-models FastFlowLM-Bestand, Stack stillgelegt
/opt/docker Images kommen aus den Registries, Compose-Dateien liegen im Repo
/opt/ai/volumes/{parakeet,piper} 4,9 GB Modell-Caches, laden beim Containerstart selbst nach
/home/robin/{.cache,.local,.npm} 676 MB Werkzeug-Caches

Damit liegt das Archiv bei rund 50 MB statt der ursprünglich geschätzten 1–3 GB. Das ist kein Fehler: Alles Große auf dieser Maschine ist per Definition wiederbeschaffbar — genau dafür ist /opt nicht gesnapshottet.

Zustandsabzug vor dem Archiv

Ein before-Hook schreibt nach /root/backup-info, damit im Archiv steht, wie die Maschine lief:

apt-mark showhold                        > apt-holds.txt
dpkg-query -f '${binary:Package}\n' -W   > packages.txt
docker ps -a --format '…'                > docker-container.txt
cat /proc/cmdline                        > kernel-cmdline.txt

Beide Hooks hängen an configuration, nicht an action

action liefe einmal pro Repository, also zweimal — der Zustandsabzug doppelt, und der Erfolgs-Zeitstempel schon nach dem ersten Ziel, obwohl das zweite noch aussteht.

Alarmierung

Zwei voneinander unabhängige Wege, weil jeder für sich eine Lücke hat:

  1. ntfy über den nativen ntfy:-Hook, Topic homelab-alerts, states: [fail]. Erfolgsmeldungen kämen sonst jede Nacht und würden das Topic entwerten.
  2. Prometheus-Metrik über borgmatic-textfile.sh im after-Hook.

ntfy deckt nur den Fall ab, dass borgmatic läuft und scheitert. Läuft es gar nicht mehr — Timer deaktiviert, Unit kaputt, Maschine nachts aus — kommt genau nichts. Dagegen greift der Zeitstempel: Auf dem NUC feuert AiserverBackupStale, wenn er älter als 48 Stunden ist.

Bekannter Defekt: Monitoring-Hook

Stand 18.08.2026 — die Backups laufen, der Hook nicht

Seit dem 15.08.2026 endet jeder nächtliche Lauf mit Exit-Code 1.

Die Archive selbst entstehen fehlerfrei. Im Log des Laufs vom 18.08. stehen für beide Ziele Creating archive, Pruning, Compacting und die Konsistenzprüfung — je 5 Archive vorhanden. Es scheitert ausschließlich der after-Hook:

borgmatic-textfile.sh: line 30:
/opt/ai/volumes/monitoring-textfile/.borgmatic.prom.<PID>: Permission denied

Ursache: Das Zielverzeichnis gehört robin:robin (Modus 755). Borgmatic läuft als root, aber die systemd-Unit des Pakets setzt CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_NET_RAW. Ohne CAP_DAC_OVERRIDE darf root das Verzeichnis lesen und durchsuchen, aber nicht hineinschreiben. Beim manuellen Erstlauf am 14.08. griff die Einschränkung nicht — daher stammt die vorhandene borgmatic.prom.

Folge: Der Erfolgs-Zeitstempel steht auf dem 14.08. still, damit feuert AiserverBackupStale auf dem NUC seit dem 16.08. — und der ntfy-Hook meldet jede Nacht einen Fehlschlag. Beides sind Fehlalarme: Das Backup ist aktuell. Genau das ist das Problem, denn eine Meldung, an die man sich gewöhnt, verdeckt den echten Ausfall.

Mögliche Behebung (Server-Eingriff, noch nicht ausgeführt):

  • Sauber: Verzeichnis root:root übereignen und den GPU-Collector von User-Cron nach /etc/cron.d/ als root verlagern. Dann sind beide Schreiber Eigentümer, die Härtung bleibt unangetastet.
  • Kurz: Drop-in mit CapabilityBoundingSet=… CAP_DAC_OVERRIDE. Eine Zeile, weicht aber die Härtung des Pakets auf.

Betrieb

sudo borgmatic list                       # Archive beider Repos
sudo borgmatic list --repository nuc      # nur ein Ziel
sudo borgmatic info                       # Repo-Kennzahlen
sudo borgmatic --verbosity 1 --stats      # Lauf von Hand
sudo borgmatic check                      # Konsistenzprüfung erzwingen
sudo borgmatic config validate            # Config-Syntax prüfen

systemctl list-timers borgmatic.timer     # nächster Lauf
journalctl -u borgmatic -n 50             # letzter Lauf

In borgmatic 2 heißt die Aktion repo-create, nicht mehr init

Restore

Einzelne Datei

sudo borgmatic list --repository nuc
sudo borgmatic extract --repository nuc --archive latest \
    --path opt/aiserver/CLAUDE.md --destination /tmp/restore-probe
diff /tmp/restore-probe/opt/aiserver/CLAUDE.md /opt/aiserver/CLAUDE.md && echo RESTORE-OK

Pfade in --path sind relativ — ohne führenden /.

Stöbern statt extrahieren

sudo borgmatic mount --repository nuc --archive latest --mount-point /mnt/borg
ls /mnt/borg/opt/aiserver
sudo borgmatic umount --mount-point /mnt/borg

Totalverlust der Maschine

  1. Ubuntu Server neu aufsetzen, borgbackup + borgmatic installieren
  2. Passphrase aus dem Passwortmanager nach /etc/borgmatic.d/passphrase schreiben (root, 0600)
  3. SSH-Key wiederbeschaffen oder neu erzeugen und auf NUC/Storage Box hinterlegen — der Key schützt den Zugang, nicht die Daten
  4. /etc/borgmatic.d/aiserver.yaml wiederherstellen
  5. /etc extrahieren und gezielt zurückspielen: default/grub, modprobe.d/ttm.conf, die apt-Holds aus /root/backup-info/apt-holds.txt
  6. Repo neu klonen bzw. /opt/aiserver extrahieren, Stacks starten, Modelle neu ziehen
  7. Die Hardware-Verifikation komplett durchlaufen

Ohne die Passphrase aus dem Passwortmanager ist keine Wiederherstellung möglich

Die Datei auf dem Server ist nur die Betriebskopie — sie liegt im eigenen Backup und ist damit im Katastrophenfall wertlos. Die maßgebliche Kopie liegt in Robins Passwortmanager.

Append-only — bewusst noch nicht

Beide Forced Commands laufen ohne --append-only. Das ist eine offene Entscheidung, keine Auslassung: Mit --append-only melden prune und compact Erfolg, geben aber serverseitig keinen Platz frei. Die Retention oben wäre dann Fiktion, bis jemand von Hand mit einem Admin-Schlüssel aufräumt.

Solange das Verfahren nicht für den NUC steht (dort unter „Noch umzusetzen", siehe Backup-Betrieb), macht der AI-Server es nicht anders — sonst gäbe es zwei Backup-Regime im Haus, von denen eines still vollläuft.