Oct 23, 202610 min read

How to Deploy FastAPI to AWS Without Kubernetes or Terraform

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:

  • A container image in Amazon ECR and something that runs it, such as ECS on Fargate.
  • A load balancer, a VPC with public and private subnets and security groups that only let the right traffic through.
  • A Postgres database on Amazon RDS, with credentials stored in Secrets Manager.
  • IAM roles that let each task pull its image, write logs and read the resources it uses.
  • A way to run the same setup locally, which usually ends up being a separate Docker Compose file.

FastAPI on AWS at a glance

OptionHow it worksBest fit
EncoreInfrastructure SDK added to your existing FastAPI app, provisioned in your AWS accountApps with a database, queues or buckets, where you want local infra and AWS from the same code
ECS on Fargate by handYou build an image, push it to ECR and configure the service, load balancer and RDS in the console or CLIOne service with a team comfortable in the AWS console
AWS Lambda with MangumFastAPI runs as a Lambda function behind API Gateway or a function URLLow or spiky traffic and short requests
Elastic BeanstalkAWS manages EC2 instances and a load balancer for a Python or Docker appA single web app that fits Beanstalk's environment model
Compare what each option covers
CapabilityEncoreECS on Fargate by handLambda with MangumElastic Beanstalk
ComputeECS Fargate or EKS, chosen per environmentECS FargateLambdaEC2 instances
PostgresDeclared in code, RDS in productionCreated separately in RDSCreated separately in RDSCreated separately, or attached to the environment
Networking and IAMVPC, load balancer and a scoped IAM role per service set up for youYou configure VPC, ALB, security groups and rolesAPI Gateway, execution role and VPC access configured by youMostly managed, extra resources configured by you
Local developmentencore run starts Postgres, Pub/Sub and your servicesDocker Compose you maintainEmulators or deploy to testDocker Compose you maintain
Infrastructure codeNone to write for the declared resourcesClick-ops, CLI scripts or IaCSAM, IaC or the console.ebextensions and platform hooks

Encore: add an infrastructure SDK to your FastAPI app

Encore

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.

Application code passes through Encore and infrastructure guardrails to resources provisioned in your own AWS or GCP account.
Encore provisions the infrastructure your application needs in your own AWS or GCP account.

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"]}
The database becomes RDS Postgres and the topic becomes SNS/SQS when the app is deployed to AWS.

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.

Key features

  • Each service runs on ECS Fargate or EKS and gets its own IAM role scoped to the resources it uses.
  • Postgres becomes RDS, Pub/Sub becomes SNS/SQS, buckets become S3 and secrets are stored in Secrets Manager, all in your own account.
  • Existing RDS databases, S3 buckets and SNS topics can be imported instead of creating new ones.
  • Deploys to GCP use Cloud Run or GKE from the same declarations.
  • If you prefer to run the containers yourself, encore build docker produces a standard Docker image.

Good to know

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.

Try Encore →

ECS on Fargate by hand

Amazon ECS

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:

~/app
$ aws ecr create-repository --repository-name fastapi-app$ aws ecr get-login-password | docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com$ docker build -t <account>.dkr.ecr.<region>.amazonaws.com/fastapi-app:v1 .$ docker push <account>.dkr.ecr.<region>.amazonaws.com/fastapi-app:v1

Key features

  • No servers or cluster nodes to patch, since Fargate runs each task on capacity AWS manages.
  • ECS Express Mode creates the Fargate service, load balancer and scaling from an image and two IAM roles.
  • Works with any Python web server and any library, because it is just a container.

Good to know

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.

Try AWS Fargate →

AWS Lambda with Mangum

AWS Lambda

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)

Key features

  • You pay per request and duration, which suits low or uneven traffic.
  • Scaling to zero and back is handled by Lambda.
  • The same FastAPI code can still run under Uvicorn locally.

Good to know

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.

Try AWS Lambda →

Elastic Beanstalk

AWS Elastic Beanstalk

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.

Key features

  • It has a managed Python platform as well as a Docker platform.
  • Rolling deployments, health checks and auto scaling are built in.
  • Configuration lives in .ebextensions files in the repository.

Good to know

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.

Try Elastic Beanstalk →

How to choose

  • Encore if the FastAPI app has a database, queues or buckets and you want them running locally and in your AWS account from the same Python code.
  • ECS on Fargate by hand for a single container when someone on the team is comfortable owning the AWS resources around it.
  • Lambda with Mangum for small APIs with spiky traffic and short requests.
  • Elastic Beanstalk for one web app that fits its environment model.

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.

Frequently asked questions

What is the easiest way to deploy FastAPI to AWS?

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.

Do I need Kubernetes to run FastAPI on AWS?

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.

Can I deploy FastAPI to AWS Lambda?

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.

How do I connect FastAPI to RDS Postgres on AWS?

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.

Does Encore support Python?

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.

How do I add a database and queues to my FastAPI app?

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.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders