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:
- ntfy über den nativen
ntfy:-Hook, Topichomelab-alerts,states: [fail]. Erfolgsmeldungen kämen sonst jede Nacht und würden das Topic entwerten. - Prometheus-Metrik über
borgmatic-textfile.shimafter-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¶
- Ubuntu Server neu aufsetzen,
borgbackup+borgmaticinstallieren - Passphrase aus dem Passwortmanager nach
/etc/borgmatic.d/passphraseschreiben (root, 0600) - SSH-Key wiederbeschaffen oder neu erzeugen und auf NUC/Storage Box hinterlegen — der Key schützt den Zugang, nicht die Daten
/etc/borgmatic.d/aiserver.yamlwiederherstellen/etcextrahieren und gezielt zurückspielen:default/grub,modprobe.d/ttm.conf, die apt-Holds aus/root/backup-info/apt-holds.txt- Repo neu klonen bzw.
/opt/aiserverextrahieren, Stacks starten, Modelle neu ziehen - 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.