Deployment
Production runs on Render. The whole stack (4 Go services + 2
static frontends + managed Postgres + managed Redis) is described
declaratively in render.yaml at the repo root — one Blueprint apply
brings it all up.
The full playbook lives in
docs/deploy.md.
This page is the tour, not the replacement.
What Render provisions
civicos-gateway— the api-gateway Web Service (port3000).civicos-identity— identity-service Private Service.civicos-community— community-service Private Service.civicos-organization— organization-service Private Service.civicos-web— citizen web Static Site (apps/webbuild output).civicos-admin— admin console Static Site (apps/adminbuild output).- Managed Postgres, managed Redis.
Only the gateway and the two Static Sites are public. The three backend services are private — they're reachable inside Render's network from the gateway but never from the public internet.
Estimated cost at launch
~$34/mo on Render's minimum plans. Breakdown in docs/deploy.md.
First-time deploy — the short version
- Connect Render to the GitHub repo.
- In Render, New + → Blueprint → point at the repo → confirm.
- Wait ~20–30 minutes for the first Docker builds.
- Set the following env vars in the Render dashboard on each service
(Blueprint provides defaults for most, but some are secrets):
JWT_SECRET(32+ chars) — must be the same on gateway, identity, community, organization.SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASSWORD/SMTP_FROMon identity (once you have a Resend / Postmark account).APP_URLon identity — the public URL where email links land.
- Wait for services to go healthy (each has
/health). - Register the first user via the citizen web app, verify the email,
then bump their role to
PLATFORM_ADMINviarender psql:UPDATE users SET role='PLATFORM_ADMIN' WHERE email='<you>';
Zero-downtime deploys
Render does rolling deploys per service by default. Because each
service has /health, Render waits for the new instance to answer
200 before shifting traffic. AutoMigrate runs at startup — if you're
making an additive schema change, that's fine. If you're making a
destructive schema change, apply the SQL migration first (via
render psql) then push the code.
Environment variables — production checklist
Set on every backend service:
DATABASE_URL— the Render Postgres string (Blueprint wires this).JWT_SECRET— 32+ chars, same across all four services.PORT— Render sets this; don't override.
Set on the gateway:
IDENTITY_SERVICE_URL,COMMUNITY_SERVICE_URL,ORGANIZATION_SERVICE_URL— Blueprint wires these to the private URLs.REDIS_URL— Render Redis (Blueprint wires this).
Set on identity:
SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASSWORD/SMTP_FROM— real email.APP_URL— used in email link generation.
Set on the frontends (build-time — configured in apps/*/render.yaml
section or via Static Site env):
VITE_API_URL— the gateway's public URL.
Custom domain
Once the platform is live at the Render-issued URL:
- Add the custom domain in Render on the gateway service and both Static Sites.
- Point DNS as Render instructs.
- Update
APP_URLon identity andVITE_API_URLon the frontends to the custom domain. - Redeploy the frontends so the new env is baked in.
Backups
- Render Postgres runs daily snapshots. Retention is plan-dependent — check the current plan before relying on it for compliance.
- User uploads live on the community-service Web Service's disk
(
uploads/directory). Render's ephemeral disks don't survive a redeploy on the free tier — for production you must either bind a persistent disk to the community-service or move uploads to S3 / R2 before launch.
Rollback
Each Render service keeps a small history of past deploys. Roll back via the Render dashboard on the specific service — the previous image comes back up in a couple of minutes. Roll back the schema only if you had a destructive migration; additive changes are safe to leave.