Run a FastAPI app with a Postgres database on AWS, from the manual ECS Fargate route to provisioning the infrastructure from your Python code.
FastAPI gets you from an empty file to a typed, documented API in an afternoon, and running it with Uvicorn on your laptop is one command. Putting the same app on AWS takes a lot more, since a production deploy needs a container registry, an ECS service with a task definition, an Application Load Balancer, an RDS Postgres instance in private subnets, security groups between them and IAM roles for each piece, and most guides tell you to write all of that in Terraform or to put it on Kubernetes. This guide goes through the routes that avoid both, starting with provisioning the infrastructure from your Python code and then the manual ECS Fargate, Lambda and Elastic Beanstalk options.
What a FastAPI app on AWS usually needs:
Encore combines the FastAPI app you already have with the infrastructure it needs, as an SDK you add with uv add encore-dev rather than a new app to build, because the Postgres database, Pub/Sub topics, buckets and secrets are declared as module-level variables in Python, encore run starts them locally (Postgres runs in Docker), and a deploy provisions the same resources in your own AWS account on ECS Fargate or EKS. FastAPI keeps doing the routing and validation, since the SDK only deals with infrastructure.

A FastAPI app that writes to Postgres and publishes an event declares both resources at the top of the module and uses them inside ordinary route handlers:
from encore import pubsub
from encore.storage import postgres
from fastapi import FastAPI
from pydantic import BaseModel
# Encore finds these declarations and provisions them
# locally, in preview environments, and in your cloud.
db = postgres.Database("users", migrations="./migrations")
signups = pubsub.Topic("signups")
app = FastAPI()
class Signup(BaseModel):
email: str
@app.post("/users", status_code=201)
async def create_user(req: Signup) -> dict:
row = await db.query(
"INSERT INTO users (email) VALUES ($1) RETURNING id", req.email
).one()
await signups.publish({"user_id": row["id"], "email": req.email})
return {"id": row["id"]}
Encore finds the service through an encore.service.json file in its directory, and setting expose makes its HTTP API reachable through the public gateway rather than only from other services:
{
"name": "users",
"lang": "python",
"api": [
{ "type": "http", "prefix": "/", "name": "users", "expose": true }
]
}
Attaching the SDK to an existing FastAPI app takes one call, since the app starts the Encore runtime with await encore.start() in its lifespan hook and binds to the address Encore hands it, so the same entry point works under encore run and when started on its own with uv run main.py:
import os
from contextlib import asynccontextmanager
import encore
import uvicorn
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
await encore.start()
yield
app = FastAPI(lifespan=lifespan)
def main() -> None:
# Encore's local run sets ENCORE_LISTEN_ADDR to the "host:port" to bind.
addr = os.environ.get("ENCORE_LISTEN_ADDR")
if addr:
host, _, port = addr.rpartition(":")
else:
host, port = "0.0.0.0", os.environ.get("PORT", "8000")
uvicorn.run(app, host=host or "0.0.0.0", port=int(port))
if __name__ == "__main__":
main()
Running encore run in the project starts a local Postgres, applies the migrations in ./migrations and starts the app, and connecting an AWS account in the Encore dashboard lets the next deploy create the VPC, load balancer, ECS services, RDS instance and SNS/SQS in that account. An AI coding agent working on the app can add a bucket for uploads in the same change as the endpoint and try it under encore run before anything touches AWS.
encore build docker produces a standard Docker image.The SDK covers infrastructure and leaves HTTP to FastAPI, so request validation, dependencies and OpenAPI docs work as they do today, and database access goes through the SDK's query and exec methods with $1 style parameters rather than through an ORM session. The same setup works across several services, as the Python microservices guide shows, and with Django or Flask in place of FastAPI, which FastAPI vs Django vs Flask and the best Python backend frameworks compare.
Running FastAPI on Fargate without Terraform means building the image yourself, pushing it to ECR and creating the cluster, task definition and service with the console or the AWS CLI. The Dockerfile itself is short:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN useradd --create-home --uid 1000 app
COPY . .
USER app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
The USER line keeps the app from running as root, and a .dockerignore next to the Dockerfile stops COPY . . from baking local secrets and clutter into the image:
.env .git __pycache__ *.pyc .venv
The image is then pushed to a repository in ECR before the service can reference it:
Building the image is not much work, but the RDS instance, its security group, the secret holding its password, the task role and the log group are all separate resources that you create and keep in sync by hand, and that is usually when teams start writing Terraform anyway.
Mangum adapts an ASGI app such as FastAPI to the event format that AWS Lambda passes in, so the whole app runs as one function behind API Gateway or a function URL.
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI()
@app.get("/health")
async def health() -> dict:
return {"ok": True}
handler = Mangum(app)
Cold starts grow with the size of your dependencies, a function can run for up to 15 minutes while a request through an API Gateway REST API times out after 29 seconds by default, and many concurrent functions opening Postgres connections usually calls for RDS Proxy, which is one more resource to set up.
Elastic Beanstalk runs a Python or Docker app on EC2 instances behind a load balancer that it creates and scales for you, which makes it the closest AWS equivalent to a classic platform as a service.
.ebextensions files in the repository.Each environment is one application, so a second service or a worker means another environment, and anything outside the environment, such as SQS or S3, is still set up separately. The Elastic Beanstalk alternatives guide covers where teams usually go from there.
None of these need Kubernetes, and only the manual routes leave you with AWS resources that have to be described somewhere, which is the work Terraform usually ends up doing. Python support in Encore is in beta, and you can sign up for access if you want to try it with a FastAPI app in your own AWS account.
For a single container, ECS on Fargate is the usual starting point, since you push an image to ECR and run it as a service behind a load balancer without managing servers. When the app also needs a database, queues and buckets, Encore adds them to the FastAPI app you already have as an infrastructure SDK, so you declare them in Python next to your routes and Encore provisions them in your own AWS account, so you do not write Terraform or Kubernetes manifests.
No. ECS on Fargate runs containers without a cluster of servers to manage, AWS Lambda can run FastAPI through the Mangum adapter, and Elastic Beanstalk has a Python platform. Kubernetes on EKS only pays off when you already run many services and have people to operate the cluster.
Yes. The Mangum library wraps a FastAPI app as a Lambda handler behind API Gateway or a function URL. It suits low or spiky traffic, but cold starts, the 29 second default timeout of API Gateway REST APIs and connection handling for Postgres need care.
Create an RDS instance in private subnets, allow traffic from the security group of your ECS tasks, store the credentials in Secrets Manager and pass them to the container. With Encore you declare postgres.Database in Python and the RDS instance, networking and credentials are set up for you.
Yes. Encore is an infrastructure SDK you install into your existing Python app with pip or uv as the encore-dev package. It declares Postgres databases, Pub/Sub, object storage, Redis, cron jobs, secrets and metrics in Python and works with whichever web framework you use, such as FastAPI or Django, which keeps the HTTP routing. Python support is in beta, and you can sign up for access at encore.dev/py.
Install the encore-dev package in the FastAPI app you already have, await encore.start() in its lifespan handler, and declare postgres.Database and pubsub.Topic as module-level variables next to your routes. encore run then starts the database and topic locally, and a deploy provisions them in your own AWS account as RDS and SNS/SQS. Encore works with whichever framework you use, and FastAPI keeps handling routing and validation.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.