CallMeTechie
DE Login
Home Products Blog About Contact

Backup & Restore

🔒 Security · Updated 1 month ago

Audience: Ops. This chapter describes what is in a backup, how to trigger manual and automatic backups, how restore works and what the backup does not catch. For initial setup see deployment.md, for version upgrades upgrade.md.


1. What does a backup contain

A GateControl backup is a single JSON file (format version 3 since v1.35). Contents:

  • peer_groups — names, colours, descriptions
  • peers — name, public key, encrypted private/preshared keys, allowed_ips, DNS, keepalive, enabled flag, tags, expires_at, group reference by name
  • routes — domain, target IP/port, peer reference by name, all feature flags (HTTPS, Basic-Auth, ACL, rate limit, retry, backends, sticky sessions, circuit breaker, mirror, debug, bot blocker, custom header), ACL peer list by name
  • settings — complete key-value store (SMTP, security, alerts, branding, monitoring, DNS override, split tunnel, auto backup config, …)
  • webhooks — URL, events, description, enable flag
  • route_auth — email/TOTP auth configs per route (incl. encrypted TOTP secret)

What is not included:

  • Caddy TLS storage (/data/caddy/) — certificates are fetched anew after restore. With Let's Encrypt that's OK, but stays within the rate limits (50 certs/week per domain). For a short split-apart no problem; for the collapse of a whole domain list in short succession, a rate limit threatens.
  • Access logs (/data/caddy/access.log), audit log table activity_log, traffic snapshots, login attempts, API token plaintext (are only stored hashed after issue), session cookies.
  • SQLite WAL files, the auto-backup archive under /data/backups/ itself.
  • WireGuard server keypair (/data/wireguard/wg0.conf). That must be backed up separately (see section 6). Without the server key all existing peer configs fail because the public key no longer matches.
  • License token (/data/.license-token). Is fetched anew on re-validation, provided GC_LICENSE_KEY stays set.

2. Manual backup

Via UI

SettingsBackupCreate backup now. The browser downloads a file gatecontrol-backup-YYYY-MM-DD.json.

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> uses an API token with scope settings:read. Alternatively with session cookie see API.md. The response is the JSON backup directly.

The endpoint itself (GET /api/v1/settings/backup) delivers the backup object regardless of the query parameter; ?format=download additionally sets the Content-Disposition header, but is optional for curl.


3. Auto-Backup-Jobs

Setup in the UI

SettingsBackup → section Automatic backups:

  • Enabled (toggle)
  • Interval6h, 12h, daily, 3d, weekly
  • Retention1 to 100 versions. Older ones are deleted automatically after every run.

The backups land in the container under /data/backups/ as gatecontrol-YYYYMMDD-HHmmss.json. Via the UI you can view the list, download or delete individual files.

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 is strict: gatecontrol-YYYYMMDD-HHmmss.json. Anything else is rejected by the API (protects against path traversal).

Failure-Handling

If autobackup_run fails:

  1. Entry in the activity_log with severity=error.
  2. If in Settings → Alerts an alert email is configured AND SMTP is configured, a mail with subject [GateControl] Automatic backup failed is sent.
  3. The scheduler continues — at the next slot a new attempt is made.

Feature flag: Auto-backup only runs with scheduled_backups — Community build has that on, for license restrictions see concepts/licensing.md.


4. Restore

Via UI

SettingsBackupRestore:

  1. Choose JSON file.
  2. Preview — shows the summary (number of peers/routes/settings/webhooks/…). The preview route validates, but runs read-only.
  3. Confirm restore. The mode is always replace — existing peers, routes, settings, webhooks and route auth entries are completely replaced. A "merge" does not currently exist.
  4. After successful restore, the CSRF token rotates and you are logged in again in the UI.

Via API

curl -u admin@token:<token> \
  -F "backup=@gatecontrol-backup-2026-04-20.json" \
  https://gate.example.com/api/v1/settings/restore

Optional preview endpoint (without side effects):

curl -u admin@token:<token> \
  -F "backup=@..." \
  https://gate.example.com/api/v1/settings/restore/preview

Response { ok: true, summary: { version, created_at, peers, routes, … } }.

What happens server-side

  1. Validation — structure, version compatibility (2 or 3), peer names, domains, ports, webhook URLs.

  2. Decrypt check — each encrypted private/preshared key is decrypted as a trial with the current GC_ENCRYPTION_KEY. If that fails for any peer, the restore is aborted with a clear error message, before the DB is touched:

    Peer "nas-home": cannot decrypt private key — the backup was created with a different GC_ENCRYPTION_KEY

  3. Transaction — a single SQLite transaction block deletes route_peer_acl, route_auth_otp, route_auth_sessions, route_auth, routes, peers, peer_groups, settings, webhooks and writes them anew. On error: automatic rollback, the old state is intact.

  4. WireGuard — after successful commit wg0.conf is regenerated from the peer set and the interface reloaded via wg syncconf.

  5. Caddy — new config JSON is built and pushed via the admin API.


5. Special case: gateway peers after restore

Home gateways register against the server using a per-gateway issued JWT. The signing key of these tokens is a derivative of GC_SECRET.

  • Same server, same volumesGC_SECRET stays, gateway tokens remain valid. No action needed.

  • Restore on a freshly set-up instance → new GC_SECRET, gateway tokens from old .env files on the NAS are rejected. Then:

    1. Open peer in the UI → Regenerate gateway pairing.
    2. Have a new .env generated for the gateway companion and deploy it to the NAS/RPi.
    3. There run docker compose up -d --force-recreate gateway.

Alternatively: carry GC_SECRET over from the old volume — then no re-pairing is needed. This is the recommended path if you simply move to a new VPS: take the old .env + /data/.session_secret + /data/.encryption_key with you, then restore on the new instance.


6. Offsite strategy & full volume strategy

The JSON backup is a logical backup. For full disaster recovery you must additionally back up the physical volumes.

Recommended setup

Daily (automatic):

  • Auto-backup job runs, writes into /data/backups/.
  • An external system (uptime server, monitoring host) fetches every night via API token GET /api/v1/settings/backup and archives the JSON in Git or object storage.

Weekly (manual or via cron on the host):

  • Snapshot the Docker volume gatecontrol-data via tar:

    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 weekly + 12 monthly snapshots.

S3 / B2 / rsync target

The UI currently offers no direct S3 upload target for auto backups — that's a roadmap feature in the 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/

Or fetch from outside via API token and push to S3:

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. Testing a backup

A backup that you haven't regularly tested is not a backup.

Minimal test recipe:

  1. Spin up a second empty VPS with an identical Docker stack (see deployment.md).
  2. Enter the old GC_ENCRYPTION_KEY and GC_SECRET from the prod volume into the test .env (otherwise the decrypt check fails).
  3. Start the container.
  4. Upload the backup JSON via Settings → Backup → Restore.
  5. Check peer list, routes, settings.
  6. Connect an existing peer with the config and force a handshake — if that works, the key material is okay.
  7. Throw away the VPS again.

Recommendation: do this every 3–6 months or if you changed the backup format (schema migration).


8. What backup does not catch

Scenario JSON backup Volume snapshot Fix
Admin accidentally deletes all peers yes yes Restore from last auto backup
Disk failure / volume corruption partially yes Volume snapshot restore, then refetch Caddy certs
Compromised admin account no no Revoke API tokens, password + 2FA, check audit log
Lost GC_ENCRYPTION_KEY NO no Private keys lost → roll out all peers anew
Docker image corruption after bad push no no Pin image tag to last known version
Destroyed host OS no no Rebuild VPS, replay volume tarball
Compromised VPS (host root) no no Completely rebuild, rotate secrets, audit

Rule of thumb: JSON backup secures the configuration. Volume snapshot secures secrets + TLS storage. Both together make disaster recovery realistic.

For incident debugging during restore also see troubleshooting.md, section "Database".

Cookie Settings

We use cookies to improve your experience. Essential cookies are always active.

Privacy Policy
ESC
↑↓ navigate open esc close