Skip to content

Render

The primary deployment path for this project. Free web services, no credit card, deploys straight from a GitHub repo.

The repo already contains deploy/render.yaml, so the settings are in version control rather than in someone’s memory.

  1. Render → New +Blueprint
  2. Pick your repo
  3. Render reads the file and asks you for the three secret values
  4. Apply
deploy/render.yaml
services:
- type: web
name: research-agent
runtime: python
plan: free
rootDir: app
buildCommand: pip install uv && uv sync --frozen
startCommand: uv run fastapi run main.py --port $PORT
healthCheckPath: /health
envVars:
- key: LLM_API_KEY
sync: false
- key: SUPABASE_URL
sync: false
- key: SUPABASE_SECRET_KEY
sync: false
- key: TAVILY_API_KEY
sync: false
- key: LLM_MODEL
value: gemini-flash-latest
- key: LLM_PROVIDER
value: google
- key: MAX_AGENT_STEPS
value: "12"
- key: MAX_THREAD_TURNS
value: "10"
- key: ALLOWED_ORIGINS
value: "*"

Four things in there are worth knowing:

  • rootDir: app points Render at the uv project — pyproject.toml, uv.lock, and the app code all live in app/, not the repo root. Every other path in the blueprint, including buildCommand and startCommand, resolves relative to it.
  • uv sync --frozen means “use uv.lock exactly, don’t re-resolve”. Your deploy gets the versions you tested, not whatever is newest today.
  • sync: false means “ask me for this value in the dashboard, and never store it in the repo”. This is how secrets stay out of git — the four keys above it are secret, so none of them carry a value here. TAVILY_API_KEY is the one that’s also optional: leave it empty in the dashboard and the agent runs on Wikipedia alone.
  • The variables that do carry a value (LLM_MODEL, LLM_PROVIDER, MAX_AGENT_STEPS, MAX_THREAD_TURNS, ALLOWED_ORIGINS) aren’t secret, so committing them is fine. Change ALLOWED_ORIGINS to your real frontend origin before you go public.

Dashboard → your service → Environment.

Variable Required Notes
LLM_API_KEY yes from AI Studio, or whichever provider you chose
SUPABASE_URL yes no trailing slash
SUPABASE_SECRET_KEY yes the sb_secret_… key, not the publishable one. On a pre-rename project, the legacy service_role key works here too
LLM_MODEL on Google only defaults to gemini-flash-latest; required for every other provider
LLM_PROVIDER no google (default), cerebras, openrouter, openai-compatible
LLM_BASE_URL only for openai-compatible e.g. https://api.groq.com/openai/v1
TAVILY_API_KEY no enables web search, so the agent can answer questions about recent events
MAX_AGENT_STEPS no defaults to 12
MAX_THREAD_TURNS no defaults to 10 — the cap on one conversation
ALLOWED_ORIGINS no comma-separated, no trailing slash

The old GEMINI_API_KEY and GEMINI_MODEL names still work when LLM_PROVIDER is google, so a service deployed before this change keeps running untouched.

Instance hours 750 / month / workspace
Credit card not required
Spin-down after ~15 minutes idle
Cold start ~1 minute
CPU a small shared slice
Filesystem ephemeral — wiped on every restart

Two of these bite people:

The cold start. Hit /health ten minutes before any demo. A judge or interviewer waiting 60 seconds on a blank page will assume it’s broken.

The ephemeral filesystem. Anything written to disk is gone on restart. This is why the project keeps everything in Postgres and nothing on disk — it’s not an accident.

The small CPU slice is fine here because agent work is I/O-bound; you’re waiting on the model anyway. It would be bad for anything CPU-heavy.

Render shows you the build in real time. Worth knowing what you’re looking at:

==> Cloning from https://github.com/...
==> Running build command 'pip install uv && uv sync --frozen'...
Resolved 94 packages in 12ms
Installed 94 packages in 1.2s
==> Build successful 🎉
==> Deploying...
==> Running 'uv run fastapi run main.py --port $PORT'
INFO: Uvicorn running on http://0.0.0.0:10000
==> Your service is live 🎉

If it stops after “Deploying…” and eventually says something about ports, your start command isn’t binding 0.0.0.0.

The free tier sleeps. If you need it responsive:

  • Hit /health before demos, manually.
  • Or schedule a GitHub Action to ping it. Note this consumes your 750 monthly instance hours faster — pinging every 10 minutes keeps it awake permanently, which is roughly 720 hours a month. That’s within budget for one service, and over it if you run two.

Don’t ping more often than you need to.