Skip to content

Latest commit

 

History

History
192 lines (145 loc) · 6.22 KB

File metadata and controls

192 lines (145 loc) · 6.22 KB
title Render

import { Aside, TabItem, Tabs } from "@astrojs/starlight/components";

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

Two ways to do it

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

Render → New +Web Service → connect your repo, then:

Field Value
Root Directory app
Runtime Python
Build Command pip install uv && uv sync --frozen
Start Command uv run fastapi run main.py --port $PORT
Instance Type Free
Health Check Path /health

The blueprint, explained

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.

Environment variables

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

If a free key dies mid-demo, changing LLM_PROVIDER, LLM_API_KEY and LLM_MODEL here and redeploying moves you to another provider without a code change. /health reports the provider back so you can confirm it took.

Model providers →

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.

Setting a variable doesn't restart the service. If you get Missing environment variable after adding one, trigger a manual deploy.

What the free tier actually gives you

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.

Reading the deploy log

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.

Keeping it warm

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.

See also