platform/CLAUDE.md
Bart Van Geyt 0ade1c740f Phase 5: backup/DR automation (backup Ansible role)
Implements the two decoupled backup streams from docs/06 as an idempotent
Ansible role wired into the host playbook:

- Files: sanoid takes/prunes ZFS snapshots per policy (sanoid_datasets) on
  its packaged timer; syncoid replicates offsite (heleos-zfs-offsite),
  enabled only when zfs_offsite_target is set.
- DB: heleos-db-backup (nightly systemd timer) walks the deployments dir and
  dumps each DB-backed site via `docker exec mariadb-dump` into
  db-backups/<customer>/<site>/{daily,weekly,monthly} with rotation
  (automysqlbackup-style, adapted for the containerized DB; MYSQL_PWD keeps
  the password out of the process list). heleos-db-offsite rsyncs offsite
  when db_offsite_target is set.

Streams and schedules are configured in group_vars/all.yml; offsite is
opt-in via the two target vars. Updates doc 06 (implementation note), the
ansible README, and CLAUDE.md status. YAML + templates validated by render.

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

4.1 KiB
Raw Blame History

CLAUDE.md — heleosv2 platform

Multi-tenant web hosting platform: container-per-site + ZFS-dataset-per-site, CLI-driven provisioning, decoupled per-site backup/restore. Single bare-metal host, Docker Compose per site, no orchestrator.

Read docs/ first — it is the source of truth. Start with docs/00-roadmap.md. This file is just the orientation map so a fresh session gets up to speed cheaply.

Core philosophy

  1. The customer is the boundary — isolation, backup, restore align on it.
  2. Two levels: a customer owns many sites. Isolation/containers/DB/network are per site; grouping (one SFTP login, recursive backup, billing) is per customer. See docs/03-naming-conventions.md.
  3. Containers isolate, they don't secure by themselves — non-root FPM, read-only rootfs, per-site networks, egress filtering, least-priv DB users.
  4. Each backup stream matches its change pattern — web files via ZFS snapshot/send; DB via automysqlbackup + rsync; logs via Loki. Never fold the DB dump into the web dataset (it bloats every incremental).
  5. CLI/templates before UI. Prove the platform, then wrap it in a panel.

Status

  • Phase 0 — design docs, ADRs, scaffold (docs/)
  • Phase 1 — host baseline Ansible (platform-infra/ansible/)
  • Phase 2 — platform services (Traefik, MariaDB, Forgejo) in platform-infra/stacks/
  • Phase 3 — site templates & base images in site-templates/
  • Phase 4 — provisioning CLI heleosctl in control-panel/ (provision/deprovision/list/render/backup/restore; --dry-run; 22 tests)
  • Phase 5 — backup/DR automation in the backup Ansible role (sanoid snapshots, per-DB dump timer, optional syncoid/rsync offsite)
  • 🚧 Phase 6 — observability (Prometheus/Grafana + cAdvisor/node_exporter/Traefik metrics, Loki/Promtail, Uptime-Kuma)
  • Phases 78 — migration, customer panel

Phase 4 follow-ups (not yet done)

  • reconfigure + rotate-secret commands; git-commit of deployments/ on provision; php-fpm umask for two-way SFTP editing (see control-panel/README.md).

Repo map

Path Purpose
docs/ Architecture, ADRs, runbooks, conventions (authoritative).
platform-infra/ansible/ Phase 1 host baseline (ZFS, Docker, firewall, SSH).
platform-infra/stacks/ Phase 2 base compose projects (Traefik/MariaDB/Forgejo).
site-templates/ Dockerfiles + per-profile compose templates (Phase 3).
deployments/ Rendered per-site configs <customer>/<site>/ (GitOps state).
control-panel/ Provisioning CLI (Phase 4), panel later.

Naming quick-ref (see docs/03)

  • customer + site ids; slug = <customer>-<site> (Docker/DB key).
  • ZFS: tank/customers/<customer>/<site>/web; platform on tank/platform/*.
  • DB: db_<customer>_<site> + user u_<customer>_<site> on shared MariaDB.
  • Networks: shared proxy (edge) + platform (DB); per-site <slug>_net.

Common commands

# Phase 1 — host baseline (from platform-infra/ansible, against the VM)
ansible-galaxy collection install -r requirements.yml
ansible-playbook site.yml            # tags: base|zfs|docker|firewall|ssh

# Phase 2 — platform services (on the host, once Phase 1 is applied)
platform-infra/stacks/bootstrap-networks.sh
cd platform-infra/stacks/<svc> && cp .env.example .env && docker compose up -d

Working agreements

  • Docs are source of truth. When a design decision changes, update the relevant docs/ file and add/adjust an ADR in docs/adr/.
  • Never commit plaintext secrets. Only *.enc.* (SOPS/age) are allowed; .env is git-ignored (.env.example is committed). See .gitignore.
  • Line endings: LF for scripts/Dockerfiles/YAML (enforced by .gitattributes) — these run on Linux.
  • Commit per phase/step with a descriptive message; the repo is the memory.
  • Environment is Windows (authoring) → Linux host (runtime). Prefer the Bash tool for POSIX one-offs.