--- type: wiki-page area: deployment sources: - "docs/issues/0030-der-restore-ist-nie-geprobt-sicherungen-sind.md" related: - "docs/wiki/deployment/deploy-uebergabe.md" - "docs/adr/0001-gitlab-kanonisch-push-mirror.md" --- # Wiederherstellung: Matrix-Plattform (Hetzner/K3s) Ablauf für den Ernstfall — Totalverlust des Hetzner-Hosts. Kein Fließtext, sondern Reihenfolge: **was zuerst, woher kommt was, wie prüft man es.** > **Warum dieses Dokument in Git liegt und nicht im Wiki:** Wiki.js läuft *auf* dem Cluster. > Ist der Cluster weg, ist das Wiki weg. Dieses Verfahren muss aus einem Git-Clone (oder von > git.lab/Gitea) lesbar sein, ohne dass irgendetwas von der Plattform läuft. **Stand:** 2026-08-14, geschrieben aus dem laufenden System heraus (Cluster-Inspektion). ⚠️ **Noch nicht durchgespielt** — siehe „Offene Punkte" am Ende. ## 0. Was du in der Hand haben musst, bevor du anfängst | Was | Woher | Ohne das geht | |---|---|---| | **age-Schlüssel** (`age.agekey`) | Passwort-Vault (sorb). Zweitkopien: `~/.age/keys.txt` auf sorbs Mac, `sops-age`-Secret im alten Cluster | **gar nichts** — alle Secrets sind damit verschlüsselt | | **gitops-Repo** | `git.lab/axion1337.chat/axion1337.chat-gitops` (kanonisch), Spiegel auf Gitea/rohana | kein Cluster-Zustand | | **Zugang Hetzner** | Hetzner Cloud Console (Neuinstallation) | kein Host | | **Gitea-Zugangsdaten** für Flux | im Repo SOPS-verschlüsselt bzw. Gitea-Token neu erzeugen | Flux kann nicht syncen | | **Storage-Box-Zugang** | SSH-Key + Borg-Passphrase, beide in `apps/production/synapse-backup-secret.yaml` (SOPS) | keine Daten | **Wichtig:** Borg-Passphrase und Storage-Box-SSH-Key holst du **ohne Cluster** aus einem lokalen Clone — genau dafür ist der age-Schlüssel da: ```bash export SOPS_AGE_KEY_FILE=~/.age/keys.txt # aus dem Vault wiederhergestellt sops -d apps/production/synapse-backup-secret.yaml # Borg-Passphrase + SSH-Key sops -d apps/authentik/authentik-backup-secret.yaml ``` ## 1. Wo die Daten liegen Hetzner **Storage Box** `u641795@u641795.your-storagebox.de`, **Port 23**, drei getrennte Borg-Repos. Verschlüsselung `repokey-blake2` → **die Passphrase allein genügt**, es gibt keine separate Schlüsseldatei zu retten. | Repo | Inhalt | Quelle im Betrieb | |---|---|---| | `/./synapse-backup` | DBs `synapse` + `matrixauthenticationservice`, **plus** Medien (`media_store`) | `matrix-stack-postgres` | | `/./authentik-backup` | DB `authentik` | `authentik-postgresql` | | `/./wikijs-backup` | DB `wiki` | `wikijs-postgres` | Aufbewahrung: 7 täglich / 4 wöchentlich / 6 monatlich (`borg prune`, nachweislich aktiv). Archivname: `-`. Layout im Archiv: `scratch/dumps/.dump` (Format `pg_dump -Fc` → `pg_restore`) und `media/media_store/…`. Zugriff von einer beliebigen Maschine mit Borg: ```bash export BORG_REPO='ssh://u641795@u641795.your-storagebox.de:23/./synapse-backup' export BORG_PASSPHRASE='…' # aus sops -d, s.o. export BORG_RSH="ssh -i -p 23" borg list "$BORG_REPO" # Archive auflisten borg extract "$BORG_REPO::" # ins aktuelle Verzeichnis ``` ## 2. Reihenfolge der Wiederherstellung ### Phase A — Fundament 1. **Host + K3s** neu aufsetzen (siehe `gitops:docs/install.md`, Schritt 1). Kubeconfig sichern. 2. **Zwei Secrets von Hand anlegen** — ohne die kann Flux nichts tun (Henne-Ei, bewusst manuell): ```bash kubectl create namespace flux-system # a) age-Schlüssel (aus dem Vault) — entschlüsselt alle SOPS-Secrets kubectl create secret generic sops-age -n flux-system --from-file=age.agekey= # b) Git-Zugang für Flux (Keys heißen: username / password) kubectl create secret generic flux-system -n flux-system \ --from-literal=username= --from-literal=password= ``` 3. **Flux installieren** und auf das Repo zeigen lassen (`clusters/matrix`). > ⚠️ **Flux zieht aus Gitea (`rohana.axion1337.de`), nicht aus git.lab** — bewusst so, damit der > Cluster ohne das Homelab baubar ist (siehe `gitops:CLAUDE.md`). **Ist rohana ebenfalls weg**, > dann zuerst eine erreichbare Git-Quelle herstellen (Repo aus git.lab oder lokalem Clone auf ein > neues Remote pushen) und `clusters/matrix/flux-system/gotk-sync.yaml` (`url:`) darauf zeigen > lassen. Sonst hängt der Wiederanlauf an einem Host, der gar nicht Teil des Backups ist. ### Phase B — Dienste hochziehen (noch leer) Flux arbeitet die Kustomizations in dieser Abhängigkeit ab: ``` flux-system └── infra-apps (Namespaces, Cert-Manager, HelmRepositories) — keine Secrets nötig ├── production-apps (ESS/Synapse/MAS/Wiki.js, entschlüsselt via sops-age) ├── authentik-apps (entschlüsselt via sops-age) └── monitoring-apps ``` Warten, bis die **Postgres-Instanzen** laufen (`matrix-stack-postgres`, `authentik-postgresql`, `wikijs-postgres`). Die Anwendungen kommen dabei mit **leeren** Datenbanken hoch — das ist erwartet. ### Phase C — Daten zurückspielen > ⚠️ **Konsumenten vorher herunterfahren.** Synapse/MAS/Authentik/Wiki.js legen beim Start ein > leeres Schema an; ein `pg_restore` dagegen kollidiert. Also erst herunterskalieren, dann > zurückspielen, dann wieder hochfahren. ```bash kubectl scale -n matrix deploy --replicas=0 -l app.kubernetes.io/name=synapse kubectl scale -n matrix deploy --replicas=0 matrix-stack-matrix-authentication-service wikijs kubectl scale -n authentik deploy --replicas=0 authentik-server authentik-worker ``` Pro Datenbank: Archiv extrahieren, Dump einspielen (`pg_restore` in die jeweils **leere** DB; bei Bedarf `--clean --if-exists`), danach Konsumenten wieder hochskalieren. Reihenfolge sinnvoll: **Authentik zuerst** (Anmeldung/OIDC hängt daran), dann Synapse+MAS, dann Wiki.js. **Synapse-Medien** nicht vergessen: `media/media_store/…` aus dem Archiv zurück in das PVC `matrix-stack-synapse-media`. Ohne die Medien ist die Datenbank zwar konsistent, aber alle Bilder/Dateien in Räumen sind tote Links. ### Phase D — Verifikation - `flux get kustomizations -A` → alles `Ready=True`. - Anmeldung über Authentik funktioniert (OIDC-Kette steht). - Ein bestehender Raum zeigt **Verlauf und Medien**. - Wiki erreichbar; Inhalte kommen ohnehin zusätzlich aus git-storage (Gitea/git.lab). - Zertifikate: Traefik/Cert-Manager stellen neu aus (DNS-01 ist umgestellt, kein offener Port 443 nötig — siehe #0007). - Die drei Backup-CronJobs laufen wieder (`kubectl get cronjob -A`). ## 3. Was am Backup **nicht** hängt - **Wiki-Inhalte**: liegen zusätzlich als Git-Repo (Wiki.js → Gitea → git.lab kanonisiert). - **Gesamte Konfiguration**: im gitops-Repo, nicht im Backup (deshalb ist der Cluster reproduzierbar und nicht nur wiederherstellbar). - **Gitea/rohana selbst**: Push-Mirror, wird bewusst nicht gesichert (#0010) — Repos aus git.lab neu befüllen. **Aber:** die Container-Registry dort ist kein Spiegel; die vier eigenen Images (u.a. `threadnet-web`, `axion-backup`) müssen ggf. neu gebaut werden. ## 4. Offene Punkte - **Nie durchgespielt.** Dieses Verfahren ist aus dem laufenden System abgeleitet, nicht erprobt (#0030, Schritt 3). Ein Durchlauf gegen eine Wegwerf-Umgebung steht aus — erst danach ist es belastbar. - **Homelab-Seite ungeprüft**: GitLab auf Overmind → MinIO/DSM ist nicht Teil dieser Prüfung. - Nach dem ersten echten Durchlauf: Zeitbedarf und Stolpersteine hier ergänzen und einen Wiederholungsrhythmus festlegen.