platform/docs/adr/0006-decoupled-backup-streams.md
Bart Van Geyt 9f819df4d9 Phase 0: architecture docs, ADRs, and repo scaffold
Establish the design foundation for the heleosv2 multi-tenant hosting
platform before any implementation code:

- Monorepo skeleton: docs/, platform-infra/, site-templates/,
  deployments/, control-panel/ with orientation READMEs.
- docs/: roadmap index, architecture + threat model, naming conventions,
  site profiles, provisioning workflow, backup & DR runbook, repo/GitOps
  layout, and the approved architecture plan.
- docs/adr/: 9 ADRs recording the rationale for single-host Compose,
  Traefik edge, nginx+fpm split, shared MariaDB, ZFS-per-customer,
  decoupled backup streams, Forgejo, CLI-first, and SFTP-only.
- Secrets hygiene: .gitignore (only *.enc.* committed) and .gitattributes
  (LF for scripts/Dockerfiles/YAML run on the Linux host).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:26:00 +02:00

1.7 KiB

ADR 0006 — Decoupled backup streams (files vs DB vs logs)

Status: Accepted

Context

An earlier proposal wrote per-database dumps into each customer's ZFS dataset just before snapshotting, so one snapshot contained files + DB. Problem: the dump changes every night even for a static site, so every incremental zfs send would ship a fresh dump — defeating the cheap-incremental goal of ADR 0005. Logs in the dataset cause the same bloat.

Decision

Back up each data type with the mechanism that fits its change pattern, in three decoupled streams:

  1. Web files → ZFS snapshot + zfs send offsite (truly incremental; near-zero when unchanged).
  2. Databasesautomysqlbackup produces per-database dumps with daily/weekly/monthly rotation into tank/platform/db-backups/<customer>, then rsync offsite. Per-database dumps preserve per-customer restore granularity.
  3. Logs → shipped to Loki via Promtail; never stored in the customer dataset.

Consequences

  • Unchanged sites cost almost nothing to back up offsite.
  • Each stream is independently tunable (retention, cadence, target).
  • Restoring a customer is a two-step operation (files from ZFS, DB from the dump repo) rather than a single snapshot rollback — documented in the DR runbook.
  • ⚠️ The streams are crash-consistent, not atomically consistent. Schedule the nightly DB dump close to the ZFS snapshot; the small window is harmless for PHP/WordPress (files-on-disk + DB-rows).
  • ↔️ Optional future: place db-backups on its own dataset and zfs send it for checksummed/immutable offsite instead of rsync.