Skip to main content

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.

ShapeCompose file(s)PortsDoc
Local devdocker-compose.yml5173 (app), 8081 (API direct), 5434 (Postgres), 5050 (pgAdmin), 8025 (Mailpit) — the last two loopback-onlydocker.md
Demo sandboxdocker-compose.demo.yml5174 (app), 8082 (API), 5435 (Postgres) — fully isolated from devdocker.md
Observability overlaydocker-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 edgeMailpit 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/CaddyfileOnly Caddy's 80/443 — everything else internalvps-guide.md
Legacy / on-prem (per client)docker-compose.client.template.yml + infra/traefik/docker-compose.ymlNone on the host except Traefik's :80ADR-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

DocRead it when
docker.mdYou need to understand any compose file, Dockerfile, the nginx proxy topology, or startup order
ci.mdYou want to know what GitHub Actions actually gates (and what it deliberately does not)
manual-deploy.mdYou are deploying by hand — the pipeline isn't wired up yet, a Secret is wrong, or you're bringing up a fresh VPS
monitoring.mdYou want metrics, logs, traces, dashboards and alerting — what to run, what it watches, and what to set in production
vps-guide.mdYou are standing up (or operating) the production VPS, end to end
scaling.mdYou 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

  • Developersdocker.md explains the stack you run every day and why a frontend change needs a rebuild. Runtime problems are in ../troubleshooting.md.
  • Whoever operates productionvps-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 / auditorsci.md and monitoring.md state plainly which controls are automated and which are manual.