Deploying FastAPI on AWS Lambda with Lambda Web Adapter
Containerizing a FastAPI backend and deploying it to a single Lambda function with Lambda Web Adapter and AWS CDK.
This example containerizes an API backend written with FastAPI and deploys it to AWS Lambda using Lambda Web Adapter.
The traditional AWS design using API Gateway and multiple Lambda functions can become unwieldy when managing many APIs.
Lambda Web Adapter simplifies the architecture, requiring only a single API Gateway route and one Lambda function.
Getting Started
Bootstrapping AWS CDK Environment
First, bootstrap your AWS CDK environment. If you’ve already done this, you can skip this step.
Run the following commands to install AWS CDK locally and bootstrap your environment:
npm i -D aws-cdknpx cdk bootstrap aws://<AWS_ACCOUNT_ID>/<AWS_REGION>Initializing CDK Project
Create a directory for your CDK project and initialize it:
mkdir cdk && cd cdknpx cdk init app --language typescriptInstalling FastAPI
Install FastAPI with the following commands:
python -m venv .venvsource .venv/bin/activatepip install "fastapi[standard]"mkdir srcpip freeze > ./src/requirements.txtBuilding Backend
Defining APIs
Here’s an example FastAPI application:
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")def read_root(): return {"Hello": "World"}
@app.get("/items/{item_id}")def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id, "q": q}Start the FastAPI server with:
fastapi dev ./src/main.pyTest the API with your preferred tool:
curl "http://127.0.0.1:8000/"{"Hello":"World"}
curl "http://127.0.0.1:8000/items/1?q=keyword"{"item_id":1,"q":"keyword"}Containerizing
To containerize the FastAPI backend, write the ./docker/Dockerfile. This Dockerfile is designed to handle both development and production environments effectively.
- Base Image: We use
public.ecr.aws/docker/library/python:3.12-alpineas our lightweight and secure base image (line 2). - Lambda Web Adapter: The Lambda Web Adapter is added for seamless AWS Lambda integration (line 22).
- Port Configuration: The backend listens on port 8080 by default for production to match the Lambda Web Adapter’s expected configuration (line 25).
For more details on Lambda Web Adapter usage, refer to its GitHub repository.
# Base image: Python 3.12 AlpineFROM public.ecr.aws/docker/library/python:3.12-alpine AS baseENV APP_ROOT=/code
# Copy requirements and install dependenciesCOPY ./src/requirements.txt $APP_ROOT/RUN pip install --no-cache-dir --upgrade -r $APP_ROOT/requirements.txt
# Development stageFROM base AS devENV ENV=devEXPOSE 8000CMD ["sh", "-c", "fastapi run $APP_ROOT/main.py --port 8000"]
# Production stageFROM baseENV ENV=prodEXPOSE 8080COPY ./src $APP_ROOT
# Copy Lambda Web AdapterCOPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
# Run FastAPI backend on port 8080 for Lambda Web AdapterCMD ["sh", "-c", "fastapi run $APP_ROOT/main.py --port 8080"](Optional) Docker Compose
While this post does not include database usage, in real-world scenarios, you often work with databases such as DynamoDB, MySQL, or others. To facilitate local development and testing, we can set up Docker Compose.
- Build Context: Points to the project root (
../) (line 4). - Build Target: Uses the development stage of the Dockerfile (
target: dev) (line 6). - Ports Mapping: Maps the container’s port
8000to the host’s port8000for local access (line 8). - Volume: Mounts the local
srcdirectory at/codein the container so code changes are available during local development (line 10).
services: api: build: context: ../ dockerfile: ./docker/Dockerfile target: dev ports: - "8000:8000" volumes: - ../src:/codeTo start the backend service locally using Docker Compose, run the following commands:
cd dockerdocker compose upWhen the service starts successfully, you should see logs similar to:
api-1 | INFO: Started server process [1]api-1 | INFO: Waiting for application startup.api-1 | INFO: Application startup complete.api-1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)You can now access the API at http://127.0.0.1:8000 and test it as usual using tools like curl, Postman, or a browser.
Deploying to AWS
Defining AWS Resources
To deploy the FastAPI backend to AWS, define the necessary resources using AWS CDK.
#!/usr/bin/env nodeimport 'source-map-support/register';import * as cdk from 'aws-cdk-lib';import { CdkStack } from '../lib/cdk-stack';
const app = new cdk.App();new CdkStack(app, 'App');- Memory Size: This example allocates 512 MB (line 20). Memory also determines CPU allocation in Lambda, so tune this value after measuring startup time and workload behavior.
- Platform Configuration:
Platform.LINUX_AMD64(line 23) makes the image architecture match the function’s defaultx86_64architecture, including when the image is built on Apple Silicon. An architecture mismatch can causeError: fork/exec /opt/extensions/lambda-adapter: exec format error Extension.LaunchError.
import * as cdk from 'aws-cdk-lib';import type { Construct } from 'constructs';import { LambdaRestApi } from 'aws-cdk-lib/aws-apigateway';import { DockerImageCode, DockerImageFunction, LoggingFormat,} from 'aws-cdk-lib/aws-lambda';import * as path from 'node:path';import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
export class CdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props);
// Dockerized Lambda Function const lambda = new DockerImageFunction(this, 'function', { functionName: 'fast-api-app-function', loggingFormat: LoggingFormat.JSON, memorySize: 512, // Tune after measuring the workload code: DockerImageCode.fromImageAsset(path.join(__dirname, '..', '..'), { file: path.join('docker', 'Dockerfile'), platform: Platform.LINUX_AMD64, // Match Lambda's default x86_64 architecture exclude: ['*', '!src', '!docker'], }), });
// API Gateway REST API new LambdaRestApi(this, 'api', { handler: lambda, deploy: true, }); }}Deploying the Stack
Navigate to your CDK project directory and run the following command to deploy:
cd cdknpx cdk deployDuring deployment, you may be prompted to confirm resource creation. Respond with y to proceed:
Do you wish to deploy these changes (y/n)? yApp: deploying... [1/1]App: creating CloudFormation changeset...
✅ App
✨ Deployment time: 52.67s
Outputs:App.apiEndpoint9349E63C = https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/Stack ARN:arn:aws:cloudformation:<AWS_REGION>:<AWS_ACCOUNT_ID>:stack/App/<UUID>
✨ Total time: 55.42sThe deployment will output the API Gateway endpoint, which you can use to test your API.
Testing APIs
Test the deployed APIs:
curl "https://<API_GATEWAY_ENDPOINT>/prod/"{"Hello":"World"}
curl "https://<API_GATEWAY_ENDPOINT>/prod/items/1?q=keyword"{"item_id":1,"q":"keyword"}Conclusion
Containerizing a FastAPI app with Lambda Web Adapter and deploying it via CDK put the entire API behind a single Lambda function and one API Gateway route.
Lambda Web Adapter’s real contribution is letting FastAPI’s own routing replace what would otherwise be a growing pile of individual Lambda functions and API Gateway route mappings — the whole application ships as one container behind a single proxy route, so adding an endpoint is a code change rather than an infrastructure change.
The two CDK settings should be treated as explicit choices rather than universal requirements. The 512 MB memory allocation is a starting point to benchmark, while Platform.LINUX_AMD64 ensures that an image built on an Apple Silicon host still matches the function’s default x86_64 architecture. Lambda Web Adapter also offered arm64 images in 2024, so an arm64 deployment is valid when the container image and Lambda function are both configured for arm64.
Related posts
Sign in with Slack Using Cognito User Pools and OIDC
Federating Cognito user pools with Slack over OIDC and wiring "Sign in with Slack" into a Next.js app with Amplify.

Integrating AWS Amplify with Eclipse Mosquitto for MQTT Messaging
Testing AWS Amplify's PubSub module against a local Eclipse Mosquitto broker before pointing it at AWS IoT Core.
API Gateway WebSocket: Implementing a Mock Integration
Building an API Gateway WebSocket API entirely with mock integrations, returning canned responses with no backend Lambda involved.
Uploading to S3 Through CloudFront Signed URLs
CloudFront signed URLs let you upload to S3 through a custom domain—useful when direct S3 pre-signed URLs are not an option.
AWS EventBridge Scheduler: Starting and Stopping EC2 on a Schedule
Starting and stopping EC2 instances on a cron schedule with EventBridge Scheduler calling the EC2 API directly, no Lambda involved.
