3.9 KiB
| project | type | status | path | tags | created | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| staging-environment-setup | reference | active | Tech/Reference |
|
2026-04-15 | 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/Referencefolder for evergreen architecture docs (vs.Tech/Sessionsfor point-in-time notes orTech/Projectsfor 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.comin 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
.envfiles. - 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/versionreturns 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/Referenceis the right folder for this kind of doc, or move toTech/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