diff --git a/Tech/Reference/staging-environment-setup.md b/Tech/Reference/staging-environment-setup.md new file mode 100644 index 0000000..56d9464 --- /dev/null +++ b/Tech/Reference/staging-environment-setup.md @@ -0,0 +1,119 @@ +--- +project: staging-environment-setup +type: reference +status: active +path: Tech/Reference +tags: + - pbs + - infrastructure + - staging + - traefik + - cloudflare + - docker +created: 2026-04-15 +updated: 2026-04-15 +--- + +# Staging Environment — Architecture Reference + +Reference doc for how the PBS staging environment is structured. Captured +retroactively from the original Feb 24 build session, before the n8n email +pipeline existed. + +> **Note on path:** Filed under a proposed new `Tech/Reference` folder for +evergreen architecture docs (vs. `Tech/Sessions` for point-in-time notes or +`Tech/Projects` for active work). Flag if you'd rather move it. + +## Domain & DNS Strategy + +Staging lives at `staging.plantbasedsoutherner.com`, with all supporting +tools nested as further subdomains under it. + +``` +staging.plantbasedsoutherner.com ← WordPress +n8n.staging.plantbasedsoutherner.com ← n8n +portainer.staging.plantbasedsoutherner.com ← Portainer +status.staging.plantbasedsoutherner.com ← Uptime Kuma +db.staging.plantbasedsoutherner.com ← phpMyAdmin +gitea.staging.plantbasedsoutherner.com ← Gitea +``` + +**Cloudflare DNS uses a wildcard A record** so new subdomains require zero +DNS work: + +``` +Type: A +Name: *.staging +Value: +Proxy: DNS only (grey cloud) +``` + +The wildcard must remain **DNS only** (grey cloud) for Let's Encrypt HTTP +challenges to succeed. With Cloudflare proxy on, ACME hits Cloudflare's IP +instead of the origin and TLS handshake fails. + +## SSL / Traefik + +Traefik handles all routing and SSL via Let's Encrypt **HTTP challenge** +(not TLS challenge — TLS challenge is incompatible with Cloudflare in +front). + +Each container declares its hostname via Traefik labels; Traefik handles +cert issuance and renewal automatically per hostname. + +## Migration Pattern (Prod → Staging Clone) + +The original staging build used a full prod clone. Key gotchas: + +- **Database URL replacement:** Use WP-CLI `search-replace` (not raw SQL) +to handle serialized Elementor data correctly. Run from inside the +wordpress container. +- **Avoid "staging.staging" loops:** Replace `plantbasedsoutherner.com` → ` +staging.plantbasedsoutherner.com` in one direction only; double-check +existing staging references before running. +- **Redis password:** Must be present in the container env for both prod +and staging — easy to forget when moving env vars between `.env` files. +- **DB user recreation:** Fresh MySQL inits won't carry over user grants +from the prod dump. + +## Resource Sizing + +Staging started on a 1GB Linode Nanode and was upgraded to 2GB after +hitting memory ceilings during full-stack runs (WordPress + MySQL + Redis + +n8n + Traefik + monitoring). 2GB is the practical minimum for the full +stack. + +## Healthchecks + +All Docker containers have healthchecks defined. A few patterns learned: + +- **Traefik** requires an IPv6 loopback target in some configurations. +- **Containers without `curl`** (slim images, Gitea, Python slim) need +alternative healthcheck commands — wget, a Python one-liner, or the +application's own CLI. +- **Gitea healthcheck on `/api/v1/version`** returns 404 before the setup +wizard completes, blocking initial Traefik routing. Remove the healthcheck +for first-time setup, then add it back. + +## Inventory & Ansible (added later) + +The Ansible inventory eventually evolved to keep staging and production +strictly separated — see the GitHub deploy key project for the final +structure with `bootstrap_staging`, `bootstrap_production`, `staging`, and +`production` groups (no meta-groups that could hit both environments). + +## Open Items + +- [ ] Confirm `Tech/Reference` is the right folder for this kind of doc, or +move to `Tech/Sessions` / `Tech/Projects` +- [ ] Cross-link this from the GitHub deploy key project plan once both are +in the vault + +## Related + +- GitHub private repo + Ansible inventory restructure (April 2026) +- PBS Security Hardening (Crowdsec + Authelia, staged on staging first) +- WP-Cron / Supercronic deploy (March 2026) + + +...sent from Jenny & Travis \ No newline at end of file