platform/docs/05-provisioning-workflow.md
Bart Van Geyt e5ff798dd4 Refine to nested customer -> site model
Adopt a two-level ownership model: a customer owns many sites, with
isolation/containers/DB/backup at the site level and grouping (one SFTP
login, recursive backup, billing, bulk delete) at the customer level.

- 03-naming-conventions: rewritten for customer/site ids and slug =
  <customer>-<site>; nested ZFS, per-customer SFTP chroot, deployments
  mirror the nesting.
- Propagated paths through docs 01, 04, 05, 06, 07 and deployments/README.
- ADR 0005 retitled/updated to per-site datasets nested under customer.
- architecture-plan.md: note pointing to doc 03 as authoritative on naming.

Clarifies subdomains: same-app subdomains are aliases on one site; a
separate-app subdomain is its own isolated site under the same customer.

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

3.9 KiB
Raw Permalink Blame History

05 — Provisioning Workflow

How a site goes from request to live, and back off again. All steps are operator-driven via the control-panel CLI in Phase 07; the customer panel (Phase 8) calls the same operations.

Design principle

The CLI is declarative and idempotent: site.yaml is the source of truth, everything else is rendered from it, and re-running a command converges to the desired state rather than erroring. Each step is individually re-runnable so a failed provision can be resumed.

New site — provision

Input: customer, site, profile, domains, profile options (see 04 site.yaml). The flattened slug = <customer>-<site>.

  1. Validate — customer/site charset + reserved-word check (03), domains resolvable/owned, profile known.
  2. ZFS — ensure tank/customers/<customer> exists, then create tank/customers/<customer>/<site>/web (and db-backups/<customer>/<site> if the profile uses a DB).
  3. Database (DB profiles) — create db_<customer>_<site> + u_<customer>_<site> with least privilege on shared MariaDB; generate password; write to secrets.enc.yaml (SOPS/age).
  4. Render — produce deployments/<customer>/<site>/docker-compose.yml + .env from the profile template and site.yaml.
  5. SFTP — ensure the customer's chrooted sftp_<customer> account exists (chroot tank/customers/<customer>/); the new site appears as a sub-folder.
  6. Commit — commit deployments/<customer>/<site>/ to the deployments repo (audit trail; secrets committed only in encrypted form).
  7. Deploydocker compose up -d in deployments/<customer>/<site>/. Traefik discovers the route from labels; ACME issues the certificate.
  8. Verify — HTTPS reachability + valid cert; for WordPress, run WP-CLI install. Print access details.

DNS: the operator ensures the domain points at the host (pre- or post-provision; the cert completes once DNS resolves).

Change a site — reconfigure

Edit site.yaml (e.g. add an alias domain, bump PHP version, adjust limits), then re-run: the CLI re-renders, re-commits, and compose up -d applies the delta. TLS for new domains is automatic.

Remove a site — deprovision

  1. docker compose down (optionally --remove-orphans).
  2. Final backup — take a last ZFS snapshot + final DB dump, retained per the deprovision retention policy before deletion.
  3. Drop db_<customer>_<site> + u_<customer>_<site> (after the final dump).
  4. Remove the site's SFTP sub-folder access; remove the sftp_<customer> account only when the customer has no remaining sites.
  5. Destroy tank/customers/<customer>/<site> (and db-backups/<customer>/<site>) after the retention window — never immediately. Destroy the customer dataset only when their last site is removed.
  6. Remove deployments/<customer>/<site>/ and commit.

Destroys are gated: the CLI refuses to delete data younger than the retention window without an explicit --force, and always snapshots before destroying.

Backup / restore

Routine backups run on a schedule (not per-command); restore is on demand. Both are specified in the Backup & DR runbook. CLI surface: backup <customer> <site> (ad-hoc), restore <customer> <site> --snapshot <name> --db <dump>.

CLI command summary

Command Action
provision Create a new site end-to-end (steps 18).
reconfigure Apply changes from an edited site.yaml.
deprovision Tear down a site with gated, backed-up deletion.
list Show all sites, profiles, status.
backup Ad-hoc snapshot + DB dump for a site.
restore Two-step restore (files + DB) for a site.
rotate-secret Regenerate DB/SFTP credentials for a site.

Every command is idempotent and logs to the platform log stream (Loki).