Backup & Restore
Zielgruppe: Ops. Dieses Kapitel beschreibt was in einem Backup steckt, wie du manuelle und automatische Backups anstößt, wie Restore läuft und was das Backup nicht abfängt. Für erste Einrichtung siehe deployment.md, für Version-Upgrades upgrade.md.
1. Was enthält ein Backup
Ein GateControl-Backup ist eine einzelne JSON-Datei (Format-Version 3 seit v1.35). Inhalt:
- peer_groups — Namen, Farben, Beschreibungen
- peers — Name, Public-Key, verschlüsselte Private/Preshared-Keys,
allowed_ips, DNS, Keepalive,enabled-Flag, Tags,expires_at, Gruppen-Referenz per Name - routes — Domain, Target-IP/Port, Peer-Referenz per Name, sämtliche Feature-Flags (HTTPS, Basic-Auth, ACL, Rate-Limit, Retry, Backends, Sticky-Sessions, Circuit-Breaker, Mirror, Debug, Bot-Blocker, Custom-Header), ACL-Peer-Liste per Name
- settings — kompletter Key-Value-Store (SMTP, Security, Alerts, Branding, Monitoring, DNS-Override, Split-Tunnel, Auto-Backup-Config, …)
- webhooks — URL, Events, Beschreibung, Enable-Flag
- route_auth — Email-/TOTP-Auth-Konfigs pro Route (inkl. verschlüsseltem TOTP-Secret)
Was nicht drin steht:
- Caddy-TLS-Storage (
/data/caddy/) — Zertifikate werden nach Restore neu geholt. Bei Let's-Encrypt ist das OK, bleibt aber innerhalb der Rate-Limits (50 Cert/Woche pro Domain). Bei kurzem Auseinander-Nehmen kein Problem; bei Zerfall einer ganzen Domain-Liste in kurzer Folge droht Rate-Limit. - Access-Logs (
/data/caddy/access.log), Audit-Log-Tabelleactivity_log, Traffic-Snapshots, Login-Versuche, API-Token-Plaintext (werden nach Issue nur gehasht gespeichert), Session-Cookies. - SQLite-WAL-Dateien, Auto-Backup-Archiv unter
/data/backups/selbst. - WireGuard-Server-Keypair (
/data/wireguard/wg0.conf). Das muss separat gesichert werden (siehe Abschnitt 6). Ohne Server-Key schlagen alle bestehenden Peer-Configs fehl, weil der Public-Key nicht mehr matcht. - Lizenz-Token (
/data/.license-token). Wird bei Re-Validation neu geholt, sofernGC_LICENSE_KEYgesetzt bleibt.
2. Manuelles Backup
Via UI
Einstellungen → Backup → Backup jetzt erstellen. Der Browser lädt
eine Datei gatecontrol-backup-YYYY-MM-DD.json herunter.
Via API
curl -u admin@token:<token> \
https://gate.example.com/api/v1/settings/backup \
-o backup-$(date -u +%F).json
-u admin@token:<token> benutzt einen API-Token mit Scope settings:read.
Alternativ mit Session-Cookie siehe API.md.
Die Antwort ist direkt das JSON-Backup.
Der Endpoint selbst (GET /api/v1/settings/backup) liefert das Backup-Objekt
unabhängig vom Query-Parameter; ?format=download setzt zusätzlich den
Content-Disposition-Header, ist für curl aber optional.
3. Auto-Backup-Jobs
Einrichtung in der UI
Einstellungen → Backup → Sektion Automatische Backups:
- Aktiviert (Toggle)
- Intervall —
6h,12h,daily,3d,weekly - Aufbewahrung —
1bis100Versionen. Ältere werden nach jedem Run automatisch gelöscht.
Die Backups landen im Container unter /data/backups/ als
gatecontrol-YYYYMMDD-HHmmss.json. Über die UI kannst du dir die Liste
anschauen, einzelne Dateien runterladen oder löschen.
API-Endpoints
# Aktuelle Job-Konfig abrufen
GET /api/v1/settings/autobackup
# Konfig updaten (Body: { enabled, schedule, retention })
PUT /api/v1/settings/autobackup
# Sofort manuell auslösen (nutzt die konfigurierte Retention)
POST /api/v1/settings/autobackup/run
# Verzeichnis-Listing
GET /api/v1/settings/autobackup/list
# → [{ filename, size, created }]
# Einzeldatei runterladen / löschen
GET /api/v1/settings/autobackup/download/:filename
DELETE /api/v1/settings/autobackup/:filename
Filename-Format ist strikt: gatecontrol-YYYYMMDD-HHmmss.json. Alles andere
wird von der API abgelehnt (schützt gegen Path-Traversal).
Failure-Handling
Wenn autobackup_run fehlschlägt:
- Eintrag im
activity_logmitseverity=error. - Wenn in Settings → Alerts eine Alert-Mail konfiguriert UND SMTP
konfiguriert ist, geht eine Mail mit Betreff
[GateControl] Automatic backup failedraus. - Der Scheduler läuft weiter — beim nächsten Slot wird ein neuer Versuch unternommen.
Feature-Flag: Auto-Backup läuft nur mit scheduled_backups — Community-Build
hat das an, Lizenz-Restriktionen siehe concepts/licensing.md.
4. Restore
Via UI
Einstellungen → Backup → Restore:
- JSON-Datei wählen.
- Preview — zeigt die Summary (Anzahl Peers/Routes/Settings/Webhooks/…). Die Preview-Route validiert, läuft aber read-only.
- Restore bestätigen. Der Modus ist immer replace — bestehende Peers, Routes, Settings, Webhooks und Route-Auth-Einträge werden komplett ersetzt. Ein "merge" existiert aktuell nicht.
- Nach erfolgreichem Restore rotiert der CSRF-Token und du wirst in der UI neu eingeloggt.
Via API
curl -u admin@token:<token> \
-F "backup=@gatecontrol-backup-2026-04-20.json" \
https://gate.example.com/api/v1/settings/restore
Optionaler Preview-Endpoint (ohne Side-Effects):
curl -u admin@token:<token> \
-F "backup=@..." \
https://gate.example.com/api/v1/settings/restore/preview
Antwort { ok: true, summary: { version, created_at, peers, routes, … } }.
Was serverseitig passiert
-
Validierung — Struktur, Versions-Kompatibilität (2 oder 3), Peer-Namen, Domains, Ports, Webhook-URLs.
-
Decrypt-Check — jeder verschlüsselte Private-/Preshared-Key wird mit dem aktuellen
GC_ENCRYPTION_KEYprobewise entschlüsselt. Schlägt das bei irgendeinem Peer fehl, wird der Restore mit klarer Fehlermeldung abgebrochen, bevor die DB angefasst wird:Peer "nas-home": cannot decrypt private key — the backup was created with a different GC_ENCRYPTION_KEY -
Transaction — ein einzelner SQLite-Transaktionsblock löscht
route_peer_acl,route_auth_otp,route_auth_sessions,route_auth,routes,peers,peer_groups,settings,webhooksund schreibt sie neu. Bei Fehler: automatischer Rollback, der alte Zustand ist intakt. -
WireGuard — nach erfolgreichem Commit wird
wg0.confneu aus dem Peer-Set generiert und das Interface perwg syncconfreloaded. -
Caddy — neues Config-JSON wird gebaut und per Admin-API gepusht.
5. Special-Case: Gateway-Peers nach Restore
Home-Gateways registrieren sich gegen den Server über einen pro-Gateway
ausgestellten JWT. Der Signing-Key dieser Tokens ist ein Derivat aus
GC_SECRET.
-
Gleicher Server, gleiche Volumes →
GC_SECRETbleibt, Gateway-Tokens bleiben gültig. Kein Handeln nötig. -
Restore auf frisch aufgesetzter Instanz → neuer
GC_SECRET, Gateway-Tokens aus alten.env-Dateien auf den NAS werden abgelehnt. Dann:- Peer in der UI öffnen → Gateway-Pairing neu generieren.
- Neue
.envfür den Gateway-Companion generieren lassen und auf die NAS/RPi deployen. - Dort
docker compose up -d --force-recreate gateway.
Alternativ: GC_SECRET aus dem alten Volume übernehmen — dann ist kein
Re-Pairing nötig. Das ist der empfohlene Weg, wenn du einfach auf eine neue
VPS umziehst: alte .env + /data/.session_secret + /data/.encryption_key
mitnehmen, dann Restore auf neue Instanz.
6. Offsite- & Voll-Volume-Strategie
Das JSON-Backup ist eine logische Sicherung. Für vollständige Desaster-Recovery musst du zusätzlich die physischen Volumes sichern.
Empfohlenes Setup
Täglich (automatisch):
- Auto-Backup-Job läuft, schreibt in
/data/backups/. - Ein externes System (Uptime-Server, Monitoring-Host) fetcht jede Nacht per
API-Token
GET /api/v1/settings/backupund archiviert das JSON in Git oder Objekt-Storage.
Wöchentlich (manuell oder per Cron auf dem Host):
-
Snapshot des Docker-Volumes
gatecontrol-datapertar:docker run --rm \ -v gatecontrol-data:/data:ro \ -v /var/backups/gatecontrol:/out \ alpine \ tar czf /out/gatecontrol-volume-$(date -u +%F).tar.gz /data -
Rotation: 4 wöchentliche + 12 monatliche Snapshots.
S3 / B2 / Rsync-Ziel
Die UI bietet aktuell keinen direkten S3-Upload-Target für Auto-Backups — das ist als Roadmap-Feature im Backlog. Workaround:
# Cron auf dem Host, läuft nach dem Auto-Backup-Fenster
0 4 * * * aws s3 sync /var/lib/docker/volumes/gatecontrol-data/_data/backups/ \
s3://company-backups/gatecontrol/
Oder per API-Token von außen fetchen und in S3 schieben:
curl -sS -H "Authorization: Bearer $GC_API_TOKEN" \
https://gate.example.com/api/v1/settings/backup \
| aws s3 cp - s3://company-backups/gatecontrol/backup-$(date -u +%F).json
7. Backup testen
Ein Backup, das du nicht regelmäßig getestet hast, ist kein Backup.
Minimal-Test-Rezept:
- Zweite leere VPS mit identischem Docker-Stack hochziehen (siehe deployment.md).
- Alte
GC_ENCRYPTION_KEYundGC_SECRETaus dem Prod-Volume in das Test-.enveintragen (sonst scheitert der Decrypt-Check). - Container starten.
- Backup-JSON hochladen über Settings → Backup → Restore.
- Peer-Liste, Routes, Settings prüfen.
- Einen existierenden Peer mit der Config verbinden und einen Handshake erzwingen — wenn das klappt, ist das Keymaterial okay.
- VPS wieder wegwerfen.
Empfehlung: alle 3–6 Monate machen oder wenn du am Backup-Format (Schema-Migration) was geändert hast.
8. Was Backup nicht abfängt
| Szenario | JSON-Backup | Volume-Snapshot | Fix |
|---|---|---|---|
| Admin löscht versehentlich alle Peers | ja | ja | Restore aus letztem Auto-Backup |
| Disk-Failure / Volume-Corruption | teilweise | ja | Volume-Snapshot restore, dann Caddy-Certs nachholen |
| Kompromittierter Admin-Account | nein | nein | API-Tokens revoken, Passwort + 2FA, Audit-Log prüfen |
Verlorener GC_ENCRYPTION_KEY |
NEIN | nein | Private-Keys verloren → alle Peers neu ausrollen |
| Docker-Image-Corruption nach Bad Push | nein | nein | Image-Tag auf letzte bekannte Version pinnen |
| Zerstörtes Host-OS | nein | nein | VPS neu bauen, Volume-Tarball einspielen |
| Compromised VPS (Host-Root) | nein | nein | Komplett neu aufsetzen, Secrets rotieren, Audit |
Merkregel: JSON-Backup sichert die Konfiguration. Volume-Snapshot sichert Secrets + TLS-Storage. Beides zusammen macht Desaster-Recovery realistisch.
Für Incident-Debugging beim Wiederherstellen siehe auch troubleshooting.md, Abschnitt "Datenbank".