Airavata — Bearer of the King (Render Backend Deploys)
Airavata carries Indra without stumbling. The backend deploy carries every live conversation: it must take the new weight with all four feet on the ground — health-checked, migrated, and reversible.
Blueprint as code
- The entire Render setup lives in
render.yamlat the repo root: services, workers, cron jobs, env groups, the Postgres instance. Dashboard-only configuration is infrastructure you can't review or reproduce (seeindra). - One blueprint, explicit per-service config: plan, region (same region as the Postgres instance — cross-region DB latency is self-inflicted), branch
main,autoDeploy: true. - Changes to
render.yamlare PRs like any code: reviewed, and stated in the description as "changes infra".
Service configuration
- Start command runs uvicorn with explicit workers (
--workerssized to the plan's CPU; start with 2 on a small plan) — never a bareuvicorn app.main:appwith defaults you haven't chosen. healthCheckPath: /healthz— the cheap, dependency-free liveness route (seevayu). Render only shifts traffic when the new instance answers it; a missing health check turns every deploy into a gamble.- Graceful shutdown: handle SIGTERM — uvicorn does by default; don't wrap it in shell scripts that swallow signals (
execyour process if you must use a script). In-flight SSE streams get a bounded drain, not an instant kill. - Instance count ≥ 2 in production once real users exist; single-instance production means every deploy and every crash is downtime.
Migrations — before traffic, never on import
preDeployCommand: alembic upgrade head. Migrations run once, before the new code takes traffic — never in app startup code, never on import, never manually over SSH.- Every migration is additive-first and compatible with the currently running code, because old code serves traffic while the migration runs (expand → migrate → contract; see
hanuman). - A migration that fails aborts the deploy and leaves the old version serving — that's the feature. Test every migration against a production-like snapshot first (see
varuna).
Environment & secrets
- Shared vars in an env group (e.g.
backend-shared); service-specific overrides on the service. Secrets marked as secret, set via dashboard/CLI, never inrender.yamlvalues, never in the repo (seekubera). - The service connects to Render Postgres via the internal connection string — same private network, no public egress. The external URL is for your laptop's psql and for nothing that runs in production.
- Same key discipline as everywhere: LLM provider keys and the Supabase service-role key exist only here and are rotatable without a code change.
Free/starter tier honesty
- Free-tier services spin down when idle; the first request eats a cold start measured in tens of seconds. That's fine for a demo, disqualifying for a chat product — production runs on an always-on plan, and the decision is written down (see
lakshmifor the cost side). - Know your plan's memory ceiling: an OOM-killed worker looks like random 502s. Watch memory in the Render dashboard before adding workers (see
garuda).
Workers & cron
- Anything slower than a few seconds that isn't the chat stream (re-embedding, exports, batch jobs) runs in a background worker service consuming a queue — never inline in a request, never fire-and-forget
asyncio.create_taskin the web service that dies on deploy (seehanuman). - Cron jobs are
render.yamlcron services with one command, one purpose, and an alert on failure (seenarada) — silent cron death is data quietly rotting.
Deploy flow & rollback
- Auto-deploy from
mainmirrors the Vercel side (seepushpaka); the two are independent, so every change stays compatible one version in each direction. - Rollback = redeploy the previous successful deploy from the dashboard/CLI — one action, rehearsed once before you need it. If a migration has already run, roll forward with a fix-migration instead of rolling schema back (see
dhanvantari). - Deploy notifications go to the team channel (see
narada): a deploy nobody noticed is a deploy nobody can correlate with the incident graph (seesurya).
Before going live — checklist
-
render.yamlin repo covers every service; region matches Postgres -
/healthzconfigured as health check; SIGTERM handled; ≥2 instances in prod - Migrations via
preDeployCommand, additive-first, tested on a snapshot - Internal DB URL in use; secrets in env groups, none in blueprint or repo
- Long work in workers/cron with failure alerts, not in request handlers
- Rollback rehearsed; deploy notifications wired