Deployment & operations
How MotorPH gets from a git checkout to something a browser can reach — in development, in demos, and in production.
Read this first: deployment of the hosted product is fully automated.
Every push to main builds both images once (GHCR, sha-tagged), writes
the VPS configuration from GitHub Secrets, deploys staging
(stage.<domain>), and — once staging's health gate passes — promotes the
exact same tag to production in the same run, with auto-rollback at
each step (ci.md, vps-guide.md §10,
../git-workflow.md). The manual, script-driven path
(scripts/onboard-client.sh,
scripts/upgrade-all.sh) remains only for
the legacy per-client on-prem shape.
The six deployment shapes
The same two application images (backend, frontend) power everything; only the compose file changes.
| Shape | Compose file(s) | Ports | Doc |
|---|---|---|---|
| Local dev | docker-compose.yml | 5173 (app), 8081 (API direct), 5434 (Postgres), 5050 (pgAdmin), 8025 (Mailpit) — the last two loopback-only | docker.md |
| Demo sandbox | docker-compose.demo.yml | 5174 (app), 8082 (API), 5435 (Postgres) — fully isolated from dev | docker.md |
| Observability overlay | docker-compose.monitoring.yml (layered on dev, stage or prod with a second -f) | Loopback-only: 3000 (Grafana), 9090 (Prometheus), 9093 (Alertmanager) | monitoring.md |
| Staging (VPS) | deploy/docker-compose.stage.yml behind the shared edge | Mailpit UI on loopback; else internal — served at stage.<domain> | vps-guide.md |
| Production (shared SaaS) | deploy/docker-compose.prod.yml behind deploy/docker-compose.edge.yml (Caddy) + deploy/Caddyfile | Only Caddy's 80/443 — everything else internal | vps-guide.md |
| Legacy / on-prem (per client) | docker-compose.client.template.yml + infra/traefik/docker-compose.yml | None on the host except Traefik's :80 | ADR-0007 |
The hosted product is one shared stack serving every tenant — isolation is
rows (Hibernate tenant filter + Postgres RLS,
ADR-0013), not containers. The
per-client shape remains valid for single-client on-prem installs, but its
template predates the tenancy work (no MAIL_*, APP_DB_*, or
PLATFORM_ADMIN_* variables) — treat it as legacy until it's refreshed.
The docs in this directory
| Doc | Read it when |
|---|---|
| docker.md | You need to understand any compose file, Dockerfile, the nginx proxy topology, or startup order |
| ci.md | You want to know what GitHub Actions actually gates (and what it deliberately does not) |
| manual-deploy.md | You are deploying by hand — the pipeline isn't wired up yet, a Secret is wrong, or you're bringing up a fresh VPS |
| monitoring.md | You want metrics, logs, traces, dashboards and alerting — what to run, what it watches, and what to set in production |
| vps-guide.md | You are standing up (or operating) the production VPS, end to end |
| scaling.md | You are sizing a deployment, or asking whether this can run on more than one server — the honest limits, and what breaks first |
Who this is for
- Developers — docker.md explains the stack you run every day and why a frontend change needs a rebuild. Runtime problems are in ../troubleshooting.md.
- Whoever operates production — vps-guide.md is a from-scratch, copy-paste guide: one Ubuntu VPS, Cloudflare in front, Caddy terminating TLS, GitHub Actions deploying with rollback. It assumes no prior knowledge of this repo's conventions.
- Reviewers / auditors — ci.md and monitoring.md state plainly which controls are automated and which are manual.