Deploy a FastAPI App on AWS ECS

Tom Sharp πŸ’»
Aspiring Data Scientist
5 min readApr 11, 2024
Photo by Jean-Philippe Delberghe on Unsplash

FastAPI has become Python’s de-facto API framework; many new apps and prototypes are written with it due to it’s flexibility, β€œfreebies” such as built-in docs, and lack of boiler plate code.

However, deploying these apps on AWS is not so straightforward. There are several challenges including using a different production server, deploying the app as a container, and getting it to run on an EC2 instance or Elastic Container Service (ECS) cluster.

In this tutorial I am going to walk through setting up a FastAPI app on AWS ECS using Fargate. The code will create all the necessary infrastucture using Terraform, including setting up the Elastic Container Registry (ECR) repository, deploying the app in a Docker container, deploying the network infrastructure (VPC, subnets, route table, elastic IP, NAT gateway, etc.), and finally deploying the ECS cluster, task, service, and all other necessary components.

If you’d like to skip the step-by-step and just deploy your own FastAPI app right away, feel free to check out the repo on my GitHub.

FastAPI App

Let’s create a simple API app that we can deploy. Nothing fancy here; we can modify this later.

main.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
return {"message": "Welcome to the API"}

In order to get this on ECS we’ll need to containerize the app using Docker. We will write our Dockerfile to install dependencies, expose port 80, and run the FastAPI app. To run our app, we will use uvicorn instead of the pre-installed flask development server, since our application is going into production. More on that here.

Dockerfile:

FROM python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 80
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

requirements.txt

fastapi==0.109.2
uvicorn==0.27.1

Now we just need a way to deploy this code. We can write a generic deployment script that takes in a few environment variables and uses those to log into ECR, build the Docker image, tags the image for AWS, and pushes the image to ECR.

deploy.sh

#!/bin/bash
echo "Logging in to ECR"
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

echo "Building image"
docker build --no-cache --platform=linux/amd64 -t $REGISTRY_NAME .

echo "Tagging image"
docker tag $REGISTRY_NAME:$TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REGISTRY_NAME:$TAG

echo "Pushing image to ECR"
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REGISTRY_NAME:$TAG

And our FastAPI app is complete. Here’s an overview of what our repo looks like so far.

fastapi-on-ecs/
β”œβ”€ app/
β”‚ β”œβ”€ Dockerfile
β”‚ β”œβ”€ deploy.sh
β”‚ β”œβ”€ main.py
β”‚ β”œβ”€ requirements.txt

Infrastructure

We will use Terraform to manage all of our infrastructure. We can break the infrastructure into two parts: setup and app. The setup will be a one time creation of our ECR repository. The app will deploy all of the network components needed plus all of the ECS components needed.

Let’s take a look at the breakdown of the infra directory.

fastapi-on-ecs/
β”œβ”€ ...
β”œβ”€ infra/
β”‚ β”œβ”€ setup/
β”‚ β”‚ β”œβ”€ main.tf
β”‚ β”‚ β”œβ”€ output.tf
β”‚ β”‚ β”œβ”€ variable.tf
β”‚ β”œβ”€ app/
β”‚ β”‚ β”œβ”€ ecs
β”‚ β”‚ β”‚ β”œβ”€ main.tf
β”‚ β”‚ β”‚ β”œβ”€ output.tf
β”‚ β”‚ β”‚ β”œβ”€ variable.tf
β”‚ β”‚ β”œβ”€ network
β”‚ β”‚ β”‚ β”œβ”€ main.tf
β”‚ β”‚ β”‚ β”œβ”€ output.tf
β”‚ β”‚ β”‚ β”œβ”€ variable.tf
β”‚ β”‚ β”œβ”€ main.tf
β”‚ β”‚ β”œβ”€ variable.tf

Optionally, if we want to store our Terraform state in an S3 bucket we need to create a backend.tf file for each of the setup and app infrastructure.

Examples of these are below:

infra/setup/backend.tf

terraform {
backend "s3" {
region = "<AWS_REGION>"
bucket = "<BUCKET_NAME>"
key = "<APP_NAME>/terraform.tfstate"
}
}

/infra/app/backend.tf

terraform {
backend "s3" {
region = "<AWS_REGION>"
bucket = "<BUCKET_NAME>"
key = "<APP_NAME>/terraform.tfstate"
}
}

You can forgo this option and Terraform will store the state locally in each directory.

Using a Makefile

To make deploying our app easier, we will use a Makefile. We can then call short make commands to preform each deployment task.

To do so, we will create a .env file that will store our AWS Account ID and AWS region we wish to deploy to. The Makefile will then pick up those variables using the include keyword.

.env

AWS_ACCOUNT_ID=<YOUR_AWS_ACCOUNT_ID>
AWS_REGION=<YOUR_AWS_REGION>

Makefile

include .env 

.EXPORT_ALL_VARIABLES:
APP_NAME=my-app-name

TAG=latest
TF_VAR_app_name=${APP_NAME}
REGISTRY_NAME=${APP_NAME}
TF_VAR_image=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REGISTRY_NAME}:${TAG}
TF_VAR_region=${AWS_REGION}


setup-ecr:
cd infra/setup && terraform init && terraform apply -auto-approve

deploy-container:
cd app && sh deploy.sh

deploy-service:
cd infra/app && terraform init && terraform apply -auto-approve

destroy-service:
cd infra/app && terraform init && terraform destroy -auto-approve

Let’s take a look at our final file structure:

fastapi-on-ecs/
β”œβ”€ app/
β”‚ β”œβ”€ ...
β”œβ”€ infra/
β”‚ β”œβ”€ ...
β”œβ”€ .env
β”œβ”€ Makefile

Deploying the App

Let’s deploy our app!

WARNING: When you are doing using your API service, be sure to tear down all of your infrastructure. Leaving the API running can incur a large AWS bill.

  1. Setup your ECR Repository (one time setup)
make setup-ecr

2. Build and deploy your container

make deploy-container

3. Deploy your API Service on ECS

make deploy-service

Note: The URL for your endpoint will be printed by Terraform once the above command is done executing. Example: alb_dns_name = "<APP_NAME>-alb-123456789.<AWS_REGION>.elb.amazonaws.com". Navigate to that URL in your browser to ensure the API is working. You can also check out the API docs at the <URL>/docs endpoint.

4. Destroy (Tear Down) your Infrastructure

make destroy-service

Conclusion

Using this starter code you can easily deploy a FastAPI app to your AWS account without having to write any infrastructure or click through the console. Simply adapt the main.py to your needs and you’ll be up and running with your own app.

The code for this post can be found on my GitHub page.

If you liked this post or have any questions/comments, please leave a comment below!

Want updates on what I’m writing, side projects I’m building, or articles I find interesting? Feel free to follow me or checkout my publication, Aspiring Data Scientist.

Thanks for reading! πŸ‘¨πŸΌβ€πŸ’»

--

--