Deploy a standard FastAPI app to AWS Lambda serverlessly in two commands. No Docker. No handler code. No code changes.
How do I deploy FastAPI to AWS Lambda without code changes?
You add with sam build && sam deploy. The same code you run locally with uvicorn goes straight to production without any modifications. No handler wrapper, no
Your app receives normal HTTP requests and returns normal HTTP responses. It has no idea it's running inside a Lambda function. This means the same FastAPI app runs on Lambda, in a Docker container on ECS, or on your laptop with uvicorn. Zero changes between environments.
With that in mind, let's look at what the actual code looks like.
Can I use my existing FastAPI app on Lambda without changes?
Yes. And that's the whole point. Here's the complete application. Take a look and notice what's not there: no Lambda imports, no handler function, no Mangum wrapper. This is a standard FastAPI app you could run anywhere.
main.py
import asyncio
from typing import Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="Items API")
_items: dict[int, dict] = {}
_next_id = 1
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
class ItemResponse(Item):
id: int
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/items", response_model=list[ItemResponse])
def list_items():
return [{"id": k, **v} for k, v in _items.items()]
@app.post("/items", response_model=ItemResponse, status_code=201)
def create_item(item: Item):
global _next_id
item_id = _next_id
_next_id += 1
_items[item_id] = item.model_dump()
return {"id": item_id, **_items[item_id]}
@app.get("/items/{item_id}", response_model=ItemResponse)
def get_item(item_id: int):
if item_id not in _items:
raise HTTPException(status_code=404, detail="Item not found")
return {"id": item_id, **_items[item_id]}
@app.delete("/items/{item_id}", status_code=204)
def delete_item(item_id: int):
if item_id not in _items:
raise HTTPException(status_code=404, detail="Item not found")
del _items[item_id]
@app.get("/async-demo")
async def async_demo():
await asyncio.sleep(1)
return {"message": "done", "waited_seconds": 1}
A CRUD API with an async endpoint. Nothing special. That's the point.
The only other piece is run.sh, a tiny shell script that starts uvicorn. This is the entrypoint Lambda will call:
#!/bin/bash
export PYTHONPATH=/var/task:$PYTHONPATH
exec python -m uvicorn main:app --host 0.0.0.0 --port 8080
And requirements.txt with three dependencies:
fastapi
uvicorn[standard]
pydantic
That's the entire application. You can run it locally right now with uvicorn main:app --reload --port 8080 and get the same behavior you'll get on Lambda. No adapter, no layer, no SAM. Locally, it's a normal FastAPI app.
So where does the Lambda configuration actually go? That brings us to the one file that makes the deployment work.
What does the SAM template look like?
All the Lambda-specific configuration lives in a single file, and it's not your application code. It's the HTTP API (v2). This one line gives you a lot: a publicly accessible URL, automatic stage deployment, built-in CORS support, and request routing to your Lambda function. HTTP APIs are ~70% cheaper than REST APIs ($1.00 vs $3.50 per million requests) and have lower latency because they skip the request/response transformation layer. For a framework like FastAPI that handles its own routing, HTTP API is the right choice.
And that's it. The whole template is 30 lines. Your app code has zero lines of Lambda-specific anything.
Now that the code and configuration are in place, let's deploy it.
How do I deploy FastAPI to Lambda using SAM CLI?
Now for the fun part. You need , and Python 3.12.
No Docker required. That's unusual for Lambda deployments with custom dependencies, but Lambda Web Adapter works as a zip deployment with a layer. SAM handles the packaging.
First deployment (sets up your stack name and region):
sam build && sam deploy --guided
SAM asks you a few questions: stack name, region, whether to allow IAM role creation. Answer them once, and it creates a samconfig.toml file so subsequent deploys need no prompts.
Every deployment after that:
sam build && sam deploy
Two commands. That's the "60 seconds" in the title. The API URL is printed at the end of the deploy output:
Outputs
---------------------------------------------------------------------------
Key ApiUrl
Description API Gateway endpoint URL
Value https://abc123xyz.execute-api.us-east-1.amazonaws.com
---------------------------------------------------------------------------
The URL format is https://<api-id>.execute-api.<region>.amazonaws.com. Grab it and you're ready to test.
Teardown
When you're done experimenting:
sam delete
Removes everything: the Lambda function, the API Gateway, the IAM role. Clean slate, no lingering costs.
How do I test and run FastAPI locally?
Once you have the deployed URL, try it out:
BASE_URL=https://<api-id>.execute-api.<region>.amazonaws.com
# Health check
curl $BASE_URL/health
# List items (empty)
curl $BASE_URL/items
# Create an item
curl -X POST $BASE_URL/items \
-H "Content-Type: application/json" \
-d '{"name": "Widget", "description": "A fine widget", "price": 9.99}'
# Get item by ID
curl $BASE_URL/items/1
# Delete item
curl $BASE_URL/items/1 -X DELETE
# Async endpoint - demonstrates non-blocking I/O
curl $BASE_URL/async-demo
And here's a nice bonus: FastAPI's interactive docs work too. Open $BASE_URL/docs in a browser and you get the full Swagger UI, served from Lambda. No extra configuration needed.
Local development
But here's the thing about this setup: you don't need Lambda running to develop. The local workflow is identical to any other FastAPI project:
cd app
pip install -r requirements.txt
uvicorn main:app --reload --port 8080
Open covers 1 million requests and 400,000 GB-seconds per month, and it's always free (not time-limited). The HTTP API (API Gateway v2) free tier adds another 1 million requests/month for the first 12 months. Between the two, most side projects and early-stage APIs cost effectively zero. You'll start paying meaningful amounts when you cross roughly 5-10 million requests per month.
What are the cold start times for FastAPI with Lambda Web Adapter?
Cold starts are the single most common concern people raise about running web frameworks on Lambda. I covered this topic in depth in and general Python 3.12 runtime observations, not formal benchmarks):
| Phase | Duration |
|---|---|
| Lambda init (runtime + dependencies) | ~300-500ms |
| Lambda Web Adapter + uvicorn startup | ~100-200ms |
| Total cold start | ~400-700ms |
After the first request, subsequent invocations are warm and respond in single-digit milliseconds. Lambda keeps the execution environment alive for several minutes between requests, so moderate traffic rarely sees cold starts. For an API handling steady traffic throughout the day, cold starts affect maybe 1-2% of requests.
If cold starts matter for your use case, you have options. Enable . Clone it, deploy it, break it. Make it yours.
Once you have the basic setup working, here are some natural next steps:
Custom domain: Add a custom domain name via API Gateway custom domain mappings so your API lives atapi.yourdomain.cominstead of the generated URL.
CI/CD pipeline: Set up tracing and Amazon CloudWatch alarms.
Lambda Web Adapter works with any HTTP framework in any language. FastAPI today, Flask tomorrow, Express next week. The pattern is the same: write a standard web app, add the layer, deploy with SAM.
The serverless tax of rewriting your app for Lambda is gone. Your framework code stays framework code.
SOCIAL SHARE CARD GENERATOR