GitHub Actions is the only deployment system. There are no preview deployments per pull request.
pull request -> CI: npm run check + production build. Deploys nothing.
push to staging -> AWS deploy to test.deltechmun.in + automatic staging migration
push to main -> AWS deploy to www.deltechmun.in. Production migration is manual.
every 30 minutes -> health check of both sites and both databases
The workflows
| Workflow | Trigger | Does |
|---|---|---|
check.yml | Pull request, manual | npm ci, npm run check, npm run build. Dummy database URLs; never connects. |
deploy.yml | Push to staging or main, manual rollback | Builds the image, ships it over SSH, switches the container, verifies HTTPS and the database. |
health.yml | Minutes 17 and 47 of every hour, manual | Calls /api/health on both hosts and checks environment and database. |
staging-migrate.yml | Schema change pushed to staging, manual | prisma migrate deploy through an SSH tunnel to the staging box. |
staging-seed.yml | Manual, requires typing RESET | Wipes and reseeds staging. |
cron.yml | Daily schedules, manual | Calls the production cron routes. See cron jobs. |
The flow a change takes
-
Branch from
staging, open a pull request intostaging. CI runs. -
Merge.
deploy.ymlbuildsmun:staging-<sha>and puts it ontest.deltechmun.in. If the change has a migration,staging-migrate.ymlapplies it. -
Check it on staging. Staging is a full copy with its own database and test payment keys.
-
Release: a pull request from
stagingintomain. Merging deploys production.
What a deploy actually does
-
Build on the runner.
docker buildwithAPP_ENV,NEXT_PUBLIC_APP_URLandAPP_VERSION. A 1 GB box never builds. -
Sync the box's config.
deploy/compose.yml,deploy/deploy.shanddeploy/Caddyfile.<env>are copied to/srv/mun, and Caddy is reloaded. So a Caddy change ships with the commit that makes it. -
Ship.
docker save | gzip | ssh ... docker load. There is no registry. -
Switch.
deploy/deploy.shpoints theappservice at the new tag and waits up to 90 seconds for the container healthcheck. If it is not healthy, it switches back to the previous tag and the job fails. -
Verify from outside.
curl --resolvepins the public hostname to that box's address and requires/api/healthto reportstatus: ok,database: ok, the right environment and this commit's SHA.
The box keeps the five newest images per environment.
Note
Recreating the container drops requests for the roughly ten seconds Next.js takes to boot.
That is a known trade of running one container; see the comment in deploy/deploy.sh.
Rollback
Code only; schemas do not roll back.
ssh deploy@<box> /srv/mun/deploy.sh prod <older-sha>
Or run the AWS deploy workflow by hand with an older SHA.
Migrations
Staging applies automatically. Production does not, on purpose:
ssh -f -N -L 55432:127.0.0.1:5432 deploy@<production-box>
DIRECT_URL='postgresql://mun_prod:<password>@127.0.0.1:55432/mun_prod' npm run db:deploy
Apply a production migration before merging code that needs it. Prefer additive changes, and expand then contract for anything destructive, so the running container and the new one both work against the schema in between.
Safety properties
DEPLOY_HOSTandDEPLOY_KNOWN_HOSTSare Environment secrets. The staging job cannot learn the production box's address.- Runtime secrets never enter the image. They live in
/srv/mun/app.envon the box. deploy.ymldoes nothing unless the repository variableAWS_DEPLOYistrue./api/healthruns a realSELECT 1, so a page that renders with a broken database does not count as healthy.
The docs subdomain
docs.deltechmun.in is the production container. deploy/Caddyfile.prod has a site block for
it, and a host-matched beforeFiles rewrite in next.config.ts maps it onto /docs. Its DNS A
record must point at the production box before Caddy can issue its certificate.
Before opening a pull request
npm run check && npm run build
Exactly what CI runs. See contributing, and the fuller operational notes in docs/CI.md.