# 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 0–7; 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: `slug`, `profile`, `domains`, profile options (see [04](04-site-profiles.md) `site.yaml`). 1. **Validate** — slug charset/reserved-word check ([03](03-naming-conventions.md)), domains resolvable/owned, profile known. 2. **ZFS** — create `tank/customers//web` (and `db-backups/` if the profile uses a DB). 3. **Database** (DB profiles) — create `db_` + `u_` with least privilege on shared MariaDB; generate password; write to `secrets.enc.yaml` (SOPS/age). 4. **Render** — produce `deployments//docker-compose.yml` + `.env` from the profile template and `site.yaml`. 5. **SFTP** — create chrooted `sftp_` account bound to the web root. 6. **Commit** — commit `deployments//` to the `deployments` repo (audit trail; secrets committed only in encrypted form). 7. **Deploy** — `docker compose up -d` in `deployments//`. 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_` + `u_` (after the final dump). 4. Remove the SFTP account. 5. Destroy `tank/customers//web` (and `db-backups/`) **after** the retention window — never immediately. 6. Remove `deployments//` 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](06-backup-and-dr.md). CLI surface: `backup ` (ad-hoc), `restore --snapshot --db `. ## CLI command summary | Command | Action | |---------|--------| | `provision` | Create a new site end-to-end (steps 1–8). | | `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).