# Airavata

> Render deployment standards for the FastAPI backend — render.yaml blueprints, health checks, zero-downtime deploys, pre-deploy migrations, env groups, workers, and Render Postgres connections. Use when deploying to Render, writing or changing render.yaml, configuring Render services, cron jobs, or debugging a Render deploy.

- Skill: `arjuncrevathi/airavata` (Agent Skill)
- Install (CLI): `npx skillmds@latest add arjuncrevathi/airavata`
- Raw SKILL.md: https://api.skillmd.com/api/skills/arjuncrevathi/airavata/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: arjuncrevathi (https://skillmd.com/u/arjuncrevathi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/arjuncrevathi/airavata

---


# 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.yaml` at the repo root: services, workers, cron jobs, env groups, the Postgres instance. Dashboard-only configuration is infrastructure you can't review or reproduce (see `indra`).
- 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.yaml` are PRs like any code: reviewed, and stated in the description as "changes infra".

## Service configuration

- Start command runs uvicorn with explicit workers (`--workers` sized to the plan's CPU; start with 2 on a small plan) — never a bare `uvicorn app.main:app` with defaults you haven't chosen.
- `healthCheckPath: /healthz` — the cheap, dependency-free liveness route (see `vayu`). 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 (`exec` your 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 in `render.yaml` values, never in the repo (see `kubera`).
- 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 `lakshmi` for 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_task` in the web service that dies on deploy (see `hanuman`).
- Cron jobs are `render.yaml` cron services with one command, one purpose, and an alert on failure (see `narada`) — silent cron death is data quietly rotting.

## Deploy flow & rollback

- Auto-deploy from `main` mirrors the Vercel side (see `pushpaka`); 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 (see `surya`).

## Before going live — checklist

- [ ] `render.yaml` in repo covers every service; region matches Postgres
- [ ] `/healthz` configured 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

