Other hosts
This page is about Track 1 — hosting the
agent. Render is the primary path and
FastAPI Cloud is the second; both are verified end to end
with this project. Neither is special — nothing in app/ knows which platform
it’s on. This is the wider map, and more usefully, the one test to apply to
any host you’re considering.
Looking for somewhere to put the frontend instead? That’s Track 2 — Deploy the client, and the answer is much shorter.
The question that decides everything
Section titled “The question that decides everything”This project depends on work continuing after the HTTP response is sent. So:
Does my code still get CPU after it has returned a response?
Ask that first. Free tiers, cold starts, and dashboards are details; this one answers whether the architecture works at all. Backend hosts fall into two families.
| Family | Examples | BackgroundTasks works? |
|---|---|---|
| Long-lived process — a container that stays running between requests | Render, FastAPI Cloud, Fly.io, Koyeb, Railway, Hugging Face Spaces | Yes. This is what the workshop assumes |
| Request-scoped serverless — CPU is allocated per invocation | Vercel, Netlify, AWS Lambda | No, not safely. Background work is capped by the function timeout |
Everything below is a variation on those two rows.
The map
Section titled “The map”| Platform | Family | Free tier | Good for |
|---|---|---|---|
| Render | long-lived | yes, no card | Primary. Boring in the right way |
| FastAPI Cloud | long-lived (scale-to-zero) | yes, no card | Second target. GitHub or one command; public beta |
| Hugging Face Spaces | long-lived | yes, no card | A public, shareable demo with a URL people trust |
| Fly.io / Koyeb / Railway | long-lived | varies, often needs a card | Regions, persistent disks, growing past free |
| Vercel | request-scoped | yes, no card | The frontend. See the caveat |
| GitHub Pages | static | yes | The built client/, and making CORS real |
Hugging Face Spaces
Section titled “Hugging Face Spaces”Free CPU hosting with a URL people already recognise. Use the Docker SDK — the Gradio and Static SDKs won’t run a FastAPI app.
Put this at the top of the Space’s README.md:
---title: Research Agentemoji: 🔎colorFrom: bluecolorTo: purplesdk: dockerapp_port: 8000---app_port must match what your server binds. Spaces defaults to 7860, so
either set it as above or start uvicorn on 7860.
FROM python:3.12
RUN useradd -m -u 1000 userWORKDIR /app
COPY --chown=user . /appRUN pip install uv && uv sync --frozen
CMD ["uv", "run", "fastapi", "run", "main.py", "--host", "0.0.0.0", "--port", "8000"]Same rule as everywhere: bind 0.0.0.0, never 127.0.0.1.
What it costs. Free CPU hardware (cpu-basic), and Spaces sleep after a
configurable idle period — same cold-start trade as Render. The filesystem is
ephemeral; Hugging Face has retired its persistent storage add-on, so treat disk
as scratch space and keep state in Supabase. That’s already how this project
works, so nothing changes.
Vercel
Section titled “Vercel”This section is about running the backend on Vercel. Running the client there is a different question with a boring answer — it’s a static bundle, it works fine, and it’s the bonus track.
Vercel runs FastAPI, and it will happily serve /health, /docs, and
/runs/naive. The problem is the endpoint the whole workshop is about.
You control the ceiling in vercel.json:
{ "$schema": "https://openapi.vercel.sh/vercel.json", "functions": { "app/main.py": { "maxDuration": 60 } }}And there’s an API for work that outlives the response — waitUntil() — but
read its documented behaviour carefully:
Promises passed to
waitUntilare subject to the function’s overall timeout.
So the wall doesn’t disappear; it moves. A 40-second agent run under a 60-second
cap works right up until the run that takes 70 seconds, and then it dies
halfway with the run row stuck at running. That’s a worse failure than the 504
we started with, because it looks like it’s working.
This is the modern version of the old “Vercel has a 10-second limit” line. The number was raised and will move again. The shape of the constraint — CPU is rented per request — is the part that doesn’t expire.
Test it yourself, in two minutes
Section titled “Test it yourself, in two minutes”Documentation about background execution is often vague. Measure instead:
- Deploy, then
POST /runswith a real question. - Close the tab. Wait a minute.
GET /runs/{id}.
Steps kept appearing while nobody was polling? The host gives your process CPU between requests — the architecture works.
Steps stalled and only resumed when you polled? CPU is being allocated per request. Workable for a demo, but you now know exactly why it’ll break, and it won’t be a surprise at the worst moment.
Write down what you measured. A number you observed beats a paragraph you read.
The honest summary
Section titled “The honest summary”Portability isn’t luck — it’s a consequence of the app knowing nothing about its host. The only file here that mentions Render is
deploy/render.yaml. Pick a host by asking whether your code runs after the response, not by comparing free-tier tables that expire.
See also
Section titled “See also”- Render — the primary agent host
- FastAPI Cloud — the second agent host
- Deploy the client — the other track: Vercel and GitHub Pages
- Model providers — the same idea, for the LLM
- Troubleshooting — when it doesn’t work