No reason to mint a new system account for every project on the same Pi - one shared, host-wide account works, namespaced by directory per project underneath it.
3.8 KiB
Deployment Plan
Date: 2026-07-28
Decided at: Deployment Setup, per deployment-setup-plan.md (Q1 = A, Q2 = A)
Method
Gitea Actions CI/CD, already built in Construction:
.gitea/workflows/continuous_integration.yaml(U5) — six blocking gates, then a per-environmentdotnet publish, then invokes the deploy workflow.gitea/workflows/deploy-scp.yaml(U6) — reusableworkflow_callworkflow: backup (production only) → upload → link persistent website → atomic switch → restart → health check → prune
This stage does not change that mechanism — it documents the operational side Construction deliberately left out: host setup, real domains, the database backup script, and the rollback procedure.
Environments
| Environment | Host | Domain | Trigger |
|---|---|---|---|
| Test | Raspberry Pi (linux-arm64) |
test.slpsoftware.nl |
Automatic on push to master, or any workflow_dispatch |
| Production | Same Pi, separate directory | slpsoftware.nl |
Only workflow_dispatch with deploy_production = true |
Same physical host for both (per infrastructure-design.md § 1) — separated by directory,
systemd --user unit, and local port, never by anything the workflow manages directly.
Account model (revised at Deployment Setup): the Pi's existing webadmin account is FileZilla/
SFTP-only, used by website-workspace authors uploading customer sites under
/mnt/storage1/www/html/ (WEBSITE_WORKSPACE.md's role) — it cannot SSH in and stays that way. The
deploy pipeline uses a separate, generic, host-wide SSH-capable account instead
(deployment-instructions.md § 1.2) — not one created specifically for this project, since it may
serve other projects' deploy pipelines on the same Pi too (reuse the reference project's existing
account if it already has SSH access). Deploy paths live under that account's own home directory,
namespaced per project, not under html/ — this was a deliberate choice to avoid the CMS's
release/current/shared structure interfering with the other websites hosted there.
Rationale for What's Documented Here vs. Already Decided
| Already decided (Construction) | Documented here (Operations) |
|---|---|
| Atomic switch mechanism, retention count, transport | Real domains, real ports |
deploy-scp.yaml's step sequence |
The one-time host setup that sequence assumes already exists |
| That a DB backup step runs before production deploys | The actual backup script's content |
That transport is reserved for FTPS (D-02, NFR-09) |
What actually changes operationally when that day comes |
Automation Level
Fully automated for test (push to master → live on test.slpsoftware.nl with no human step).
Production requires an explicit, manual workflow_dispatch run with the flag checked — this manual
trigger is the approval gate (D-09); no separate approval workflow step exists or is needed.
Rollback Strategy
Summarized here; full procedure in rollback-plan.md. Two releases are always retained
(infrastructure-design.md § 3), so rolling back is a re-point-and-restart, never a rebuild, for the
most recent deploy. Migrations are required to be forward-compatible and non-destructive (D-26), so
redeploying an older commit remains valid further back than just one release.
Secrets and Configuration
Per D-16, runtime configuration lives in host environment variables that the workflow never
writes — see deployment-instructions.md § "Runtime Configuration" for the full list and where it
lives on the host (a single shared/env file per environment, outside the swapped release
directory, chmod 600). Gitea Actions secrets are limited to what the workflow itself needs to
reach the host: SSH credentials (PI_MAIN_*). No application secret is ever a Gitea secret or
variable.