docs(wiki): add restore procedure for the Matrix platform (#0030 step 2)
Derived from the running system: prerequisites (age key from the vault first), the three Borg repos with their archive layout, bootstrap order (host/K3s, the two manual secrets, Flux, kustomization dependencies), data restore and verification. Lives in git rather than Wiki.js on purpose — the wiki runs on the cluster being restored. Two previously undocumented findings: Flux pulls from Gitea rather than git.lab, so a simultaneous loss of rohana requires repointing gotk-sync first; and consumers must be scaled down before pg_restore or their startup schema collides. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
87aab7948f
commit
30e1962280
@@ -112,3 +112,30 @@ Schritt 1 (Bestandsaufnahme) ist erledigt und positiv ausgefallen; Schritt 2–4
|
|||||||
Restore-Verfahren schreiben, einmal gegen eine Wegwerf-Umgebung durchspielen, Ergebnis nach
|
Restore-Verfahren schreiben, einmal gegen eine Wegwerf-Umgebung durchspielen, Ergebnis nach
|
||||||
`verfahren/` und Wiederholungsrhythmus. Die Homelab-Seite (GitLab auf Overmind → MinIO/DSM)
|
`verfahren/` und Wiederholungsrhythmus. Die Homelab-Seite (GitLab auf Overmind → MinIO/DSM)
|
||||||
ist weiterhin ungeprüft — von der Hetzner-Seite aus nicht erreichbar.
|
ist weiterhin ungeprüft — von der Hetzner-Seite aus nicht erreichbar.
|
||||||
|
|
||||||
|
## Schritt 2 erledigt 2026-08-14 — Restore-Verfahren geschrieben
|
||||||
|
|
||||||
|
[`docs/wiki/deployment/restore.md`](../wiki/deployment/restore.md) — als Ablauf, nicht als Prosa:
|
||||||
|
Voraussetzungen (age-Key aus dem Vault zuerst), Fundort und Layout der drei Borg-Repos,
|
||||||
|
Bootstrap-Reihenfolge (Host/K3s → die zwei manuellen Secrets `sops-age` + `flux-system` → Flux →
|
||||||
|
`infra-apps` → production/authentik/monitoring), Daten-Rückspielung und Verifikation.
|
||||||
|
|
||||||
|
**Abweichung vom Issue-Wortlaut:** abgelegt unter `docs/wiki/deployment/` statt `verfahren/` —
|
||||||
|
dort liegt bereits die Deploy-Übergabe, nur `docs/**` wird von `validate.py` geprüft, und die
|
||||||
|
Seite ist über den Wiki-Index auffindbar. `verfahren/` enthält bislang ausschließlich Skripte.
|
||||||
|
|
||||||
|
**Zwei Erkenntnisse beim Schreiben, die vorher nicht dokumentiert waren:**
|
||||||
|
|
||||||
|
1. **Flux zieht aus Gitea (rohana), nicht aus git.lab.** Ist rohana beim Ausfall ebenfalls weg,
|
||||||
|
hängt der Wiederanlauf an einem Host, der gar nicht Teil des Backup-Konzepts ist — dann muss
|
||||||
|
zuerst eine erreichbare Git-Quelle hergestellt und `gotk-sync.yaml` umgebogen werden. Im
|
||||||
|
Verfahren als Warnung vermerkt.
|
||||||
|
2. **Konsumenten müssen vor dem `pg_restore` heruntergefahren werden.** Synapse/MAS/Authentik/
|
||||||
|
Wiki.js legen beim Start ein leeres Schema an, gegen das ein Restore kollidiert.
|
||||||
|
|
||||||
|
Ebenfalls festgehalten: Borg nutzt `repokey-blake2`, die Passphrase allein genügt (keine separate
|
||||||
|
Schlüsseldatei), und Passphrase/SSH-Key sind **ohne Cluster** per `sops -d` aus einem lokalen
|
||||||
|
Clone lesbar — der age-Key aus dem Vault ist damit tatsächlich der einzige harte Startpunkt.
|
||||||
|
|
||||||
|
**Offen: Schritt 3** (einmal gegen eine Wegwerf-Umgebung durchspielen) und **Schritt 4**
|
||||||
|
(Wiederholungsrhythmus). Das Verfahren ist bis dahin abgeleitet, aber unerprobt.
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
---
|
||||||
|
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: `<hostname>-<zeitstempel>`. Layout im Archiv:
|
||||||
|
`scratch/dumps/<db>.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 <storagebox-key> -p 23"
|
||||||
|
borg list "$BORG_REPO" # Archive auflisten
|
||||||
|
borg extract "$BORG_REPO::<archivname>" # 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=<pfad>
|
||||||
|
|
||||||
|
# b) Git-Zugang für Flux (Keys heißen: username / password)
|
||||||
|
kubectl create secret generic flux-system -n flux-system \
|
||||||
|
--from-literal=username=<user> --from-literal=password=<token>
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
+1
-1
@@ -16,7 +16,7 @@ mit Inhalt. Projektfassung des neckbeard-Index (Original:
|
|||||||
| Area | Enthält | Stand |
|
| Area | Enthält | Stand |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `admin/` | Betrieb: [cfgmon](admin/cfgmon.md) · [game](admin/game.md) · [matrix](admin/matrix.md) · [overmind](admin/overmind.md) · [Refinement & Retro](admin/refinement.md) · [Stillstandsprüfung](admin/stillstandspruefung.md) · [Textbausteine](admin/textbloecke.md) | belegt |
|
| `admin/` | Betrieb: [cfgmon](admin/cfgmon.md) · [game](admin/game.md) · [matrix](admin/matrix.md) · [overmind](admin/overmind.md) · [Refinement & Retro](admin/refinement.md) · [Stillstandsprüfung](admin/stillstandspruefung.md) · [Textbausteine](admin/textbloecke.md) | belegt |
|
||||||
| `deployment/` | [Deploy-Übergabe](deployment/deploy-uebergabe.md) (Definition of Done, Kanonisierungs-Verfahren) | belegt |
|
| `deployment/` | [Deploy-Übergabe](deployment/deploy-uebergabe.md) (Definition of Done, Kanonisierungs-Verfahren) · [Wiederherstellung](deployment/restore.md) (Restore-Ablauf Matrix-Plattform) | belegt |
|
||||||
| `architecture/` | [Mirror-Topologie](architecture/mirror-topologie.md) · [Lab-Netz](architecture/lab-netzwerk.md) · [DNS-Zone](architecture/zone-axion1337.md) · [Branding](architecture/branding.md) | belegt |
|
| `architecture/` | [Mirror-Topologie](architecture/mirror-topologie.md) · [Lab-Netz](architecture/lab-netzwerk.md) · [DNS-Zone](architecture/zone-axion1337.md) · [Branding](architecture/branding.md) | belegt |
|
||||||
| `vision/` | Eine Datei je Linie: [axion1337.chat](vision/axion1337-chat.md) · [Homelab](vision/homelab.md) · [ThreadNet](vision/threadnet.md) | belegt |
|
| `vision/` | Eine Datei je Linie: [axion1337.chat](vision/axion1337-chat.md) · [Homelab](vision/homelab.md) · [ThreadNet](vision/threadnet.md) | belegt |
|
||||||
| `user-guide/` | Für Nicht-Owner | entfällt — Gate 0: Publikum ist Owner + Sessions |
|
| `user-guide/` | Für Nicht-Owner | entfällt — Gate 0: Publikum ist Owner + Sessions |
|
||||||
|
|||||||
Reference in New Issue
Block a user