No description
  • Go Template 66.5%
  • Shell 19.1%
  • HCL 12%
  • Go 1.1%
  • Jinja 0.6%
  • Other 0.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
falk 4df577433d
Some checks failed
zitadel-terraform / apply (push) Has been cancelled
Ändere das origin der Apps auf sunah customer forgejo app
2026-09-27 12:28:15 +02:00
.forgejo/workflows Ändere die Src zu kunden repo. 2026-09-27 11:37:59 +02:00
apps Ändere das origin der Apps auf sunah customer forgejo app 2026-09-27 12:28:15 +02:00
customers customers: sunah/forgejo on git.dev.gme24.de (cutover from r02s44) 2026-09-26 21:36:33 +02:00
docs Setup produktion Bot um cluster deployments vom sunah repo aus zu starten 2026-09-27 12:04:22 +02:00
inventory remove vikunja from system app deployments 2026-09-13 19:19:55 +02:00
playbooks k3s - agent - add r03s44 on prem 2026-08-30 20:51:13 +02:00
roles fix traefik configuration 2026-09-13 19:12:17 +02:00
scripts/backup add further secrets of zitadel and terraform 2026-09-05 20:54:47 +02:00
.gitignore fix backup restore outcomes 2026-09-05 13:42:43 +02:00
ansible.cfg fix uninstall, reinstall 2026-08-21 16:04:51 +02:00
CLAUDE.md Ändere das origin der Apps auf sunah customer forgejo app 2026-09-27 12:28:15 +02:00
loki-export.sh fix: Zitadel-Login-Client-Rauschen aus dem Fehler-Dashboard filtern 2026-08-24 21:35:44 +02:00
production_run.sh Ändere die Src zu kunden repo. 2026-09-27 11:37:59 +02:00
README.md fix terraform state setup and documentation 2026-09-16 21:44:14 +02:00
testing_run.sh Ändere die Src zu kunden repo. 2026-09-27 11:37:59 +02:00
TODO.md Update Todo 2026-09-27 10:37:32 +02:00

k3s-Cluster (Testing + Production)

Ziel: Ein k3s-Cluster, das beim Booten automatisch startet, komplett als Code definiert ist (kein manuelles Klicken/Ausführen von Shell-Scripts) und schrittweise um Services erweiterbar ist – alles aus diesem Ordner heraus. Derselbe Code deployt sowohl eine lokale Testing- als auch eine Remote-Production-Umgebung, siehe Umgebungen: Testing vs. Production.

Umgebungen: Testing vs. Production

Testing Production
Inventory inventory/testing.yml inventory/production.yml
Zielrechner localhost srv202603.sunah.de
kubectl-Context k3s-testing k3s-production
Forgejo-Domain git.local git.gme24.de
ArgoCD-Domain argocd.local argocd.gme24.de
Zitadel-Domain id.local id.gme24.de
TLS lokale Self-Signed-CA Let's Encrypt (HTTP-01)
Kustomize-Overlay apps/{forgejo,argocd,root}/overlays/testing apps/{forgejo,argocd,root}/overlays/production
/etc/hosts-Eintrag automatisch (Ansible, für beide Domains) nicht automatisiert (öffentliches DNS)
kube-apiserver direkt lokal (127.0.0.1:6443) nicht öffentlich erreichbar, nur per SSH-Tunnel

Der SSH-/kubectl-User wird nicht hardcoded, sondern in beiden Inventories über ansible_user: "{{ lookup('env', 'USER') }}" automatisch aus dem lokalen Shell-User abgeleitet, der ansible-playbook aufruft – kein manuelles Eintragen nötig.

Deploy/Update für Production: ./production_run.sh (Pendant zu testing_run.sh, siehe dortige Kommentare für Details).

Kubeconfig liegt repo-lokal unter .kube/config (gitignored, Default aus k3s_local_kubeconfig_path in roles/k3s_server/tasks/main.yml: playbook_dir + '/../.kube/config') – nicht ~/.kube/config. Gilt für beide Umgebungen gleichermaßen; die Ansible-Rolle legt/aktualisiert dort bei jedem Lauf beide Kontexte (k3s-testing, k3s-production), unabhängig davon, welches Inventory gerade läuft. testing_run.sh/production_run.sh nutzen denselben Pfad (KUBECONFIG_PATH="${KUBECONFIG_PATH:-.kube/config}"). Ein separates ~/.kube/config (z.B. aus einem älteren manuellen Setup) hat davon nichts und enthält k3s-production insbesondere nie, weil der Kontext ohnehin nur über den Tunnel unten erreichbar ist.

kube-apiserver ist in production bewusst nicht öffentlich erreichbar (höchster Angriffswert im Cluster, volle Cluster-Admin-Rechte) – anders als Forgejo/ArgoCD/Zitadel, die über Traefik+OIDC laufen. production_run.sh baut dafür selbst einen SSH-Tunnel auf (lokaler Port 16443, da 6443 durch das lokale testing-k3s dauerhaft belegt ist) und räumt ihn danach wieder auf. Für kubectl-Zugriff außerhalb des Skripts denselben Tunnel manuell öffnen:

ssh -N -L 16443:127.0.0.1:6443 "$USER@srv202603.sunah.de" &
kubectl --kubeconfig=.kube/config --context=k3s-production get nodes

Testing braucht keinen Tunnel (kube-apiserver läuft lokal auf 127.0.0.1:6443), aber denselben expliziten Kubeconfig-Pfad:

kubectl --kubeconfig=.kube/config --context=k3s-testing get nodes

Offene Punkte für Production:

  • Es wird angenommen, dass der aufrufende lokale User (z.B. falk) auf srv202603.sunah.de bereits als SSH-User mit Sudo-Rechten existiert.
  • Ob der Hoster zusätzlich eine vorgelagerte Cloud-Firewall/Security-Group einsetzt, die Port 80/443 (Let's-Encrypt-HTTP-01-Challenge, siehe apps/cert-manager/overlays/production/) blockieren könnte, ist von hier aus nicht prüfbar.

Anmelden bei Forgejo

  1. Admin-Zugangsdaten abrufen (Secret wird vom Bootstrap-Job selbst generiert, liegt nie im Repo):
    kubectl --context=k3s-testing -n forgejo get secret forgejo-admin -o jsonpath='{.data.username}' | base64 -d; echo
    kubectl --context=k3s-testing -n forgejo get secret forgejo-admin -o jsonpath='{.data.password}' | base64 -d; echo
    
    (Für Production: --context=k3s-production.)
  2. https://git.local im Browser aufrufen und mit Username + Passwort einloggen (Ingress leitet Port 80 automatisch auf 443 um). Für Production entsprechend https://git.prod.local.
  3. Falls der Browser das Zertifikat noch als nicht vertrauenswürdig anzeigt: Root-CA manuell importieren (siehe Abschnitt "Root-CA im Browser vertrauen" weiter unten) – betrifft v.a. Edge/Chrome unter Linux, die eine eigene NSS-Zertifikatsdatenbank statt (nur) den System-Trust-Store nutzen.
  4. Die Organisation local und das Repo local/k3s (privat) wurden vom Bootstrap-Job bereits automatisch angelegt – dieses Repo hier ist bereits dorthin gepusht (Remote forgejo, siehe Abschnitt "Dieses Repo pushen").

Anmelden bei ArgoCD

  1. Initial-Admin-Passwort abrufen (Secret wird von ArgoCD selbst beim ersten Start erzeugt, liegt nie im Repo):
    kubectl --context=k3s-testing -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d; echo
    
    (Für Production: --context=k3s-production.) Username ist admin.
  2. https://argocd.local im Browser aufrufen und einloggen (Ingress leitet Port 80 automatisch auf 443 um, TLS wie bei Forgejo über die lokale Root-CA). Für Production entsprechend https://argocd.prod.local.
  3. ArgoCD läuft mit server.insecure: "true" (TLS-Termination übernimmt Traefik am Ingress, nicht argocd-server selbst) – dadurch ist kein separates Vertrauen der lokalen CA im argocd-repo-server nötig, da dieser Forgejo ohnehin nur über die interne, unverschlüsselte Cluster-URL anspricht (http://forgejo.forgejo.svc.cluster.local:3000).
  4. Die beiden Applications forgejo und argocd (App-of-Apps unter apps/root/) sollten im Zustand Synced/Healthy erscheinen – ArgoCD verwaltet damit sowohl Forgejo als auch sich selbst aus Git heraus.
  5. Nur UI/Ingress-Zugriff ist eingerichtet; CLI-Login (argocd login) bräuchte client-seitig --grpc-web.

Root-CA im Browser vertrauen

update-ca-certificates (Teil der local_ca-Ansible-Rolle) aktualisiert nur den System-Trust-Store. Chromium-basierte Browser (Edge, Chrome) nutzen unter Linux zusätzlich eine eigene NSS-Zertifikatsdatenbank (~/.pki/nssdb), die davon unabhängig ist. Import per certutil (Paket libnss3-tools, unabhängig davon ob die Edge-UI unter "Zertifikate verwalten" überhaupt etwas anzeigt – das kann durch eine Unternehmensrichtlinie deaktiviert sein):

certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "Development Root CA" -i /etc/ssl/local-ca/ca.crt

Danach Browser komplett neu starten (alle Fenster schließen).

Dieses Repo pushen

Remote ist bereits eingerichtet (forgejo, SSH: ssh://git@git.local:30022/local/k3s.git):

git push forgejo main

SSH statt HTTPS, obwohl ~/.ssh/id_rsa nur 2048 Bit hat (Forgejos Default wäre mindestens 3071 Bit) — apps/forgejo/base/deployment.yaml senkt das bewusst ab. testing_run.sh richtet SSH-Zugriff bei jedem Lauf automatisch ein, bevor gepusht wird (neuer Host-Key nach Fresh-Rebuild, Public Key wird idempotent per Forgejo-API registriert). Anderer Key: SSH_PUBKEY_PATH vor dem Skriptaufruf setzen.

Für production (production_run.sh) fehlt der Remote noch und muss einmalig angelegt werden:

git remote add forgejo-production ssh://git@git.gme24.de:30022/local/k3s.git

Nach dem Push: ArgoCD liest den Repo-Inhalt ausschließlich vom forgejo-Remote, nicht vom lokalen Arbeitsverzeichnis — ungepushte Änderungen zeigen bei der betroffenen Application Unknown/ ComparisonError: ... app path does not exist, kein Bug. Nach dem Push reconciled ArgoCD automatisch, kein manueller Sync nötig.

Terraform manuell anstoßen (wenn CI nicht geht)

Alle Terraform-Roots im Repo (apps/zitadel/terraform/, sowie pro Kunde customers/<environment>/<kunde>/terraform/ und customers/<environment>/<kunde>/<produkt>/terraform/) laufen normalerweise automatisiert per CI-Job (.forgejo/workflows/{zitadel,customer-org,customer-product}-terraform.yml) mit der In-Cluster-ServiceAccount zitadel-terraform-ci. Für einen manuellen Lauf vom eigenen Rechner aus (z. B. Debugging, State-Reparatur) müssen zwei unabhängige Stellen explizit umkonfiguriert werden — beide haben sonst unterschiedliche, stillschweigende Fallbacks, die live schon zu einem kaputten State geführt haben (siehe docs/troubleshooting.md):

  • Backend (State-Speicherort) — kein eigener Terraform-Variable dafür, muss per -backend-config bei terraform init gesetzt werden.
  • Provider (tatsächliche kubectl-Verbindung für kubernetes_*- Ressourcen) — config_path/config_context kommen aus den Variablen kubeconfig_path/kubeconfig_context (Default null = In-Cluster- ServiceAccount bzw. deren client-go-Fallback). Beide immer explizit auf die repo-lokale .kube/config setzen, nie auf eine eventuell vorhandene ~/.kube/config verlassen (siehe oben, "Kubeconfig liegt repo-lokal").

Beispiel für apps/zitadel/terraform/ in testing (Pfade für die Kunden-Roots entsprechend anpassen, PAT bleibt gleich):

cd apps/zitadel/terraform
PAT=$(kubectl --kubeconfig=../../../.kube/config --context=k3s-testing \
  -n zitadel get secret iam-admin-pat -o jsonpath='{.data.pat}' | base64 -d | tr -d '\n\r')

terraform init -input=false \
  -backend-config="in_cluster_config=false" \
  -backend-config="config_path=../../../.kube/config" \
  -backend-config="config_context=k3s-testing"

terraform plan -input=false \
  -var-file="testing.tfvars" \
  -var "zitadel_access_token=$PAT" \
  -var "zitadel_domain=id.local" -var "zitadel_port=443" -var "zitadel_insecure=false" \
  -var "kubeconfig_path=../../../.kube/config" -var "kubeconfig_context=k3s-testing"

zitadel_domain=id.local/zitadel_port=443/zitadel_insecure=false biegen den Zitadel-Provider auf den öffentlichen Ingress um (die Defaults in provider.tf sind die In-Cluster-Werte, die von außerhalb des Clusters nicht auflösen). Erst nach einem sauberen plan apply statt plan verwenden — nie direkt -auto-approve bei einem manuellen Lauf.

Architektur-Entscheidungen

Ebene Werkzeug Warum
k3s-Installation (System) Ansible (eigene Rolle roles/k3s_server) Idempotent, deklarativ, kein Blackbox-Shell-Script (get.k3s.io)
k3s-Konfiguration /etc/rancher/k3s/config.yaml, per Ansible-Template gerendert Versioniert in Git statt manuell auf der Maschine gepflegt
k3s-Autostart systemd-Unit, per Ansible-Template selbst erzeugt Kein Verlass auf das Install-Script, volle Kontrolle über Unit-Datei
Services/Workloads GitOps (ArgoCD) Deklarativ, Cluster reconciled sich selbst aus Git
Git-Hosting Forgejo, im Cluster selbst gehostet Bleibt lokal, kein externer Anbieter
CI (optional, später) Forgejo Actions Falls eigene Container-Images gebaut werden sollen
TLS für Forgejo/ArgoCD Eigene lokale Root-CA (Ansible-Rolle roles/local_ca, community.crypto) Echtes vertrauenswürdiges Zertifikat für https://git.local/https://argocd.local, ohne externe CA/Domain
ArgoCD-Repo-Zugriff Interne Cluster-URL (http://forgejo.forgejo.svc.cluster.local:3000) statt https://git.local Vermeidet, dass argocd-repo-server der lokalen selbstsignierten CA vertrauen muss

Aktueller Stand

  • k3s system-weit per Ansible installiert (roles/k3s_server, Binary + Template-Config + systemd-Unit, kein get.k3s.io-Script), Kubeconfig automatisch nach .kube/config gemergt (repo-lokal, nicht ~/.kube/config — bestehende globale Kontexte bleiben unangetastet)
  • Lokale Root-CA + Sub-CA (roles/local_ca), Sub-CA als Secret für cert-managers ClusterIssuer local-ca-issuer — Ansible bootstrapped nur noch die CA, App-Zertifikate erzeugt cert-manager selbst
  • Forgejo (apps/forgejo/) mit Bootstrap-Job (Admin-User, Org local, Repo local/k3s), Postgres statt sqlite3, eigener Actions-Infra-Runner
  • ArgoCD (apps/argocd/, vendorter Install-Manifest) + App-of-Apps (apps/root/) — verwaltet sich selbst und alle Apps aus Git
  • cert-manager, Sealed Secrets, CloudNativePG + service-db + reflector (geteilte Postgres-Instanz für Root-/Plattform-Services)
  • Zitadel + OIDC-Login für Forgejo und ArgoCD (inkl. RBAC)
  • HowTo für manuelles Terraform-Anstoßen dokumentiert (siehe unten)
  • Erste eigene Services unter apps/ definieren (über Forgejo/ArgoCD hinaus), Production-Deploy, kube-apiserver-OIDC, Backup/Recovery

Details, Begründungen und gelöste Stolpersteine zu jedem Punkt: CLAUDE.md (Konventionen + aktueller Stand) und docs/troubleshooting.md (volle Debugging-Chroniken).

Wichtige Pfade auf dem Cluster-Node

  • /var/lib/rancher/k3s/ – containerd-Daten, Kubelet-State, eingebettete DB (SQLite unter server/db/), Zertifikate (server/tls/)
  • /var/lib/rancher/k3s/server/manifests/ – Manifeste hier werden von k3s automatisch beim Start angewendet (nutzt k3s intern z.B. für Traefik)
  • /etc/rancher/k3s/k3s.yaml – Kubeconfig (Admin-Zugriff); Ansible merged daraus automatisch in die repo-lokale .kube/config, kein manuelles Kopieren nötig (siehe "Reproduzieren")
  • /etc/ssl/local-ca/ – lokale Root-CA + Leaf-Zertifikate für git.local und argocd.local (Key-Dateien 0600, root-only; nicht im Git-Repo)
  • Logs: journalctl -u k3s (kein eigenes Logfile, systemd-Unit ist Type=notify ohne File-Logging)

Reproduzieren / erneutes Ausrollen

Ansible verbindet sich per SSH auf localhost (nicht connection: local) – Grund: Bei connection: local läuft sudo/become über eine gepufferte Pipe ohne echtes TTY, was gelegentlich zu "timeout waiting for privilege escalation password prompt" mitten im Lauf führte (Timing-Race beim Puffer-Flush, unabhängig vom Sudo-Auth-Cache). Über SSH bekommt become ein echtes Pseudo-TTY, das Problem tritt nicht mehr auf. Dafür einmalig pro Sitzung den privaten Key im Agenten entsperren:

ssh-add ~/.ssh/id_rsa
# k3s installieren/konfigurieren (Ansible), Testing-Umgebung (Default-Inventory)
cd ${path_to_folder}/k3s
ansible-playbook playbooks/site.yml -K       # -K fragt sudo-Passwort ab

# nur prüfen, ohne etwas zu verändern
ansible-playbook playbooks/site.yml --check --diff

# Production-Umgebung (Remote-Server)
ansible-playbook playbooks/site.yml -K -i inventory/production.yml

Ein manuelles Kopieren der Kubeconfig ist nicht nötig – das Playbook merged den passenden Kontext (k3s-testing/k3s-production) automatisch in die repo-lokale .kube/config auf diesem Rechner (siehe "Aktueller Stand"). Danach: export KUBECONFIG=$(pwd)/.kube/config oder --kubeconfig=.kube/config an jedem kubectl-Aufruf.

Bootstrap-Ablauf für Forgejo + GitOps

Grund für die Reihenfolge: Forgejo hängt an Postgres, aber Postgres/ cert-manager/reflector können nicht über GitOps kommen — ArgoCD selbst braucht ja erst ein erreichbares Forgejo-Repo. Deshalb Schritte 2–5 wie Forgejo/ArgoCD per direktem kubectl apply gebootstrapped; sobald ArgoCD (Schritt 10) hochkommt, übernimmt es dieselben Ressourcen nahtlos in die GitOps-Verwaltung. --server-side bei 2/3/9: einzelne CRDs überschreiten die 262144-Byte-Grenze für client-side Apply. Details/Stolpersteine: docs/troubleshooting.md.

  1. ✅ ansible-playbook playbooks/site.yml -K (testing) – k3s installieren, Kubeconfig lokal mergen, Root-CA + Sub-CA erzeugen.
  2. ✅ kubectl apply -k apps/cert-manager/overlays/testing --server-side – cert-manager + ClusterIssuer local-ca-issuer. Muss vor Forgejo/ArgoCD (Schritt 6/9) und vor dem cnpg-operator-Schritt direkt danach laufen – dessen testing-Overlay installiert auch das Barman-Cloud-Plugin, dessen Certificate/Issuer-Ressourcen die cert-manager-CRDs brauchen (live beim ersten kompletten Restore-Test so aufgetreten, siehe docs/troubleshooting.md).
  3. ✅ kubectl apply -k apps/cnpg-operator/overlays/testing --server-side – CloudNativePG-Operator.
  4. ✅ kubectl apply -k apps/reflector/overlays/testing – reflector.
  5. ✅ kubectl apply -k apps/service-db/overlays/testing – Cluster service-db, bootstrap.initdb erzeugt Datenbank+Rolle forgejo.
  6. ✅ kubectl apply -k apps/forgejo/overlays/testing – Deployment/Service/ Ingress/Certificate, Bootstrap-Job legt Admin-User falk, Org local, Repo local/k3s an.
  7. ✅ https://git.local im Browser erreichbar (Root-CA importiert, forgejo-tls von cert-manager ausgestellt).
  8. ✅ Dieses Repo zu Forgejo gepusht (git push forgejo main).
  9. ✅ kubectl apply -k apps/argocd/overlays/testing --server-side – installiert ArgoCD selbst, der repo-creds-job mintet ein Forgejo-Zugriffstoken und legt das ArgoCD-Repository-Secret an. https://argocd.local erreichbar, Login mit admin + Passwort aus argocd-initial-admin-secret.
  10. ✅ kubectl apply -k apps/root/overlays/testing – registriert die App-of-Apps (forgejo, argocd, root selbst, cnpg-operator, cert-manager, reflector, service-db, sealed-secrets), ab hier verwaltet ArgoCD alle acht selbst aus Git (inkl. sich selbst über die root-Application).
  11. Ab jetzt: git push zu Forgejo → ArgoCD reconciled automatisch sowohl Änderungen an bestehenden Apps als auch neue Application-Manifeste unter apps/root/base/ – auch das manuelle kubectl apply -k apps/root/overlays/testing aus Schritt 10 entfällt für künftige Änderungen.
  12. ⏳ Production: ansible-playbook playbooks/site.yml -K -i inventory/production.yml gegen srv202603.sunah.de, danach dieselben Schritte 2–6/9/10 mit overlays/production --context=k3s-production – noch nicht durchgeführt, da SSH-Zugang zum Server noch nicht eingerichtet ist.

Alle Schritte 1–10 (testing) sind auch gebündelt in ./testing_run.sh enthalten – das Skript erkennt pro Schritt, ob er schon erledigt ist, und überspringt ihn (postet aber den Befehl zum manuellen Erzwingen mit --force).

Kompletter Restore-Test (testing)

⏳ Ablauf noch nicht live durchgeführt (Stand siehe docs/projekt/plan.md Roadmap-Punkt 6) – diese Anleitung ist der geplante Weg, keine bereits verifizierte Prozedur. Bei Abweichungen im echten Lauf hier korrigieren.

Ziel: kompletter Cluster-Verlust simulieren (Teardown wie beim normalen Fresh-Rebuild-Test oben) und danach Forgejo/Postgres/Secrets aus dem Backup statt aus Git/Ansible wiederherstellen. Die einzelnen Restore-Befehle je Komponente (Postgres/Forgejo/Secrets) sind bereits dokumentiert und live verifiziert – siehe docs/backup-recovery.md Abschnitte "Backup-Verfahren"/"Restore-Verfahren" – und werden hier nicht noch einmal wiederholt.

Sonderfall Garage + restic-Passwörter: Garage speichert die Backups selbst als Pod im Testing-Cluster (local-path-PVC unter /var/lib/rancher/k3s/storage/). playbooks/uninstall.yml löscht genau dieses Verzeichnis – ein normaler Teardown würde also die Backups vor ihrer eigentlichen Verwendung mit wegwerfen. Dieselbe Zirkularität gilt für die beiden restic-Repository-Passwörter (Forgejo/Secrets, siehe docs/backup-recovery.md Abschnitt "Zugangsdaten") – ohne sie sind die verschlüsselten Snapshots in Garage nach einem kompletten Cluster-Verlust zwar noch da, aber nicht mehr entschlüsselbar (restic: wrong password or no key found; live so aufgetreten, siehe docs/troubleshooting.md). Deshalb sichert Schritt 1 unten zusätzlich Garages Rohdaten + die vier Zugangs-Secrets + die beiden restic-Passwörter, die alle außerhalb des in docs/backup-recovery.md dokumentierten Backup-Scope liegen, nach backup/testing/ (lokal, per .gitignore ausgeschlossen).

  1. Frische Backups anstoßen, Garage-Rohdaten + Zugangs-/restic-Secrets lokal nach backup/testing/ sichern und danach den Teardown ausführen – alles in einem Skript, mit Bestätigungsabfrage vor dem eigentlichen Uninstall:
    ./scripts/backup/restore-drill-teardown.sh
    
  2. Leeres Fresh-Rebuild (wie im normalen Reproduzierbarkeits-Test):
    ./testing_run.sh
    
  3. Alten Garage-Zustand zurückspielen, bevor die Restore-Skripte aus Schritt 4 laufen – sonst zeigen die auf die neue, leere Garage-Instanz statt auf die alten Backups. Der garage-bootstrap-PostSync-Hook aus Schritt 2 hat zwischenzeitlich schon ein frisches, leeres Layout + neue Keys angelegt; die zugehörigen k8s-Secrets werden hier bewusst überschrieben. Pausiert dafür automatisch ArgoCD komplett (sonst wird die Deployment-Skalierung sofort wieder zurückgedreht, siehe docs/troubleshooting.md) und reaktiviert es danach garantiert wieder, auch bei einem Fehler mittendrin:
    ./scripts/backup/restore-drill-garage-restore.sh
    
  4. Postgres/Forgejo/Secrets aus dem (jetzt wieder erreichbaren) Backup restaurieren – Live-Restore, destruktiv, nur in testing:
    ./scripts/backup/backup-restore.sh testing postgres --live
    ./scripts/backup/backup-restore.sh testing forgejo --live
    ./scripts/backup/backup-restore.sh testing secrets --live
    
  5. Verifizieren: Forgejo-Repos/Issues aus dem alten Stand vorhanden (Login siehe "Anmelden bei Forgejo" oben), ArgoCD-Applications wieder Synced/Healthy (die kommen aus Git, nicht aus dem Backup – prüft nur, dass GitOps nach dem Restore weiterläuft), Zitadel-OIDC-Login für Forgejo/ArgoCD funktioniert weiterhin.

Verworfene Alternativen (und warum)

  • Rootless k3s: Netzwerk-Einschränkungen (privilegierte Ports, CNI-Features), weniger ausgereift getestet als der normale Modus.
  • k3d: würde eine Docker/Podman-Rootless-Engine voraussetzen; verworfen, da System-weite k3s-Installation bevorzugt wurde.
  • xanmanning.k3s-Ansible-Galaxy-Rolle: zu umfangreich für den tatsächlichen Bedarf (nur Binary + Symlinks + Unit nötig).
  • Rohes install.sh von get.k3s.io: kein "echtes" IaC, keine Ansible-native Idempotenz-Prüfung.
  • GitOps gegen GitHub: explizit nicht gewünscht – alles soll lokal bleiben, daher Forgejo im Cluster statt externem Hosting.
  • Git-Hook (post-commit → kubectl apply) statt GitOps-Controller: ursprünglich als einfachere Alternative vorgeschlagen, dann zugunsten von Forgejo + Flux/ArgoCD verworfen (bessere Reconciliation/Drift-Erkennung).