FastAPI Docker Deployment
Introduction
Docker has revolutionized how we deploy applications by providing a consistent environment across development, testing, and production. When combined with FastAPI, Docker offers a powerful solution for creating scalable, maintainable microservices and web applications.
In this tutorial, we'll learn how to containerize a FastAPI application using Docker. This approach ensures that your application runs in the same environment regardless of where it's deployed, eliminating the infamous "but it works on my machine" problem.
Prerequisites
Before we start, you should have:
- Basic knowledge of FastAPI
- Docker installed on your system
- A simple FastAPI application to containerize
Understanding Docker Basics
Docker allows you to package your application with all its dependencies into a standardized unit called a container. These containers are isolated from each other and bundle their own software, libraries, and configuration files.
Key Docker concepts:
- Docker Image: A read-only template with instructions for creating a Docker container
- Dockerfile: A text document with commands to assemble a Docker image
- Container: A runnable instance of an image
- Docker Compose: A tool for defining and running multi-container applications
Creating a Simple FastAPI Application
Let's start by creating a simple FastAPI application that we'll later dockerize.
# main.py
from fastapi import FastAPI
app = FastAPI(title="DockerizedAPI")
@app.get("/")
async def read_root():
return {"message": "Hello from FastAPI in Docker"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, query_param: str = None):
return {"item_id": item_id, "query_param": query_param}
This is a basic FastAPI application with two endpoints. You would typically run this using Uvicorn:
uvicorn main:app --host 0.0.0.0 --port 8000
Creating a Dockerfile
Now let's create a Dockerfile
to containerize our FastAPI application:
# Use official Python runtime as a parent image
FROM python:3.9-slim
# Set the working directory
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the current directory contents into the container
COPY . .
# Make port 8000 available to the world outside this container
EXPOSE 8000
# Define environment variable
ENV MODULE_NAME="main"
ENV VARIABLE_NAME="app"
ENV PORT=8000
# Run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Let's also create a requirements.txt
file:
fastapi>=0.68.0
uvicorn>=0.15.0
Understanding the Dockerfile
Let's break down our Dockerfile:
-
FROM python:3.9-slim
: This is our base image. We're using Python 3.9 with the slim variant to keep the image size smaller. -
WORKDIR /app
: Sets the working directory inside the container. -
COPY requirements.txt .
: Copies the requirements file into the container. -
RUN pip install --no-cache-dir -r requirements.txt
: Installs the Python dependencies. -
COPY . .
: Copies the application code into the container. -
EXPOSE 8000
: Documents that the container listens on port 8000. -
ENV MODULE_NAME...
: Sets environment variables. -
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
: The command that runs when the container starts.
Building the Docker Image
Now that we have our Dockerfile, let's build our Docker image:
docker build -t fastapi-app .
This command builds an image and tags it as fastapi-app
. The .
tells Docker to look for the Dockerfile in the current directory.
Running the Docker Container
Once the image is built, we can run it as a container:
docker run -d -p 8000:8000 --name my-fastapi-container fastapi-app
Let's break this down:
-d
: Run the container in detached mode (in the background)-p 8000:8000
: Map port 8000 of the host to port 8000 in the container--name my-fastapi-container
: Name our containerfastapi-app
: The image to use
Now your FastAPI application should be running inside a Docker container! You can access it at http://localhost:8000
.
Using Docker Compose for More Complex Applications
For applications with multiple services (like FastAPI + Database), Docker Compose is ideal. Let's create a docker-compose.yml
file:
version: '3'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/app
depends_on:
- db
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_DB=app
ports:
- "5432:5432"
volumes:
postgres_data:
With this setup, you can start both services with:
docker-compose up -d
Advanced Dockerization Tips
1. Multi-stage Builds
For more efficient Docker images, you can use multi-stage builds:
# Build stage
FROM python:3.9-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Runtime stage
FROM python:3.9-slim
WORKDIR /app
# Copy only the installed packages from the builder
COPY --from=builder /usr/local/lib/python3.9/site-packages/ /usr/local/lib/python3.9/site-packages/
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
2. Using Environment Variables for Configuration
Configure your app to read from environment variables:
import os
from fastapi import FastAPI
app = FastAPI(title=os.getenv("APP_TITLE", "DockerizedAPI"))
@app.get("/")
async def read_root():
environment = os.getenv("ENVIRONMENT", "development")
return {"message": f"Hello from {environment} environment"}
Then set these in your Dockerfile or docker-compose.yml:
ENV ENVIRONMENT="production"
ENV APP_TITLE="Production API"
3. Health Checks
Adding health checks to your Dockerfile ensures Docker can monitor your application's health:
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/health || exit 1
This assumes you have a /health
endpoint in your FastAPI app:
@app.get("/health")
async def health_check():
return {"status": "healthy"}
Deploying Your Dockerized FastAPI App
To a Cloud Provider
Most cloud providers support Docker containers:
- AWS: Use Elastic Container Service (ECS) or Elastic Kubernetes Service (EKS)
- Google Cloud: Use Google Kubernetes Engine (GKE) or Cloud Run
- Azure: Use Azure Kubernetes Service (AKS) or Container Instances
Example for deploying to AWS ECS:
- Push your image to Amazon ECR:
# Create a repository
aws ecr create-repository --repository-name fastapi-app
# Login to ECR
aws ecr get-login-password | docker login --username AWS --password-stdin <your-aws-account-id>.dkr.ecr.<region>.amazonaws.com
# Tag the image
docker tag fastapi-app:latest <your-aws-account-id>.dkr.ecr.<region>.amazonaws.com/fastapi-app:latest
# Push the image
docker push <your-aws-account-id>.dkr.ecr.<region>.amazonaws.com/fastapi-app:latest
- Create an ECS task definition and service to run your container.
To Kubernetes
- Create a Kubernetes deployment file
deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
spec:
replicas: 3
selector:
matchLabels:
app: fastapi-app
template:
metadata:
labels:
app: fastapi-app
spec:
containers:
- name: fastapi-app
image: fastapi-app:latest
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: fastapi-app
spec:
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
type: LoadBalancer
- Apply the deployment:
kubectl apply -f deployment.yaml
Common Issues and Troubleshooting
1. Container Fails to Start
Check the logs of your container:
docker logs my-fastapi-container
2. Can't Connect to the API
Ensure ports are correctly mapped and the container is running:
docker ps
3. Database Connection Issues
When using Docker Compose, ensure service names match connection strings. For example, if your database service is named db
, your connection string should use db
as the host, not localhost
.
Summary
In this tutorial, you've learned how to:
- Create a Dockerfile for a FastAPI application
- Build and run Docker containers
- Use Docker Compose for multi-service applications
- Implement advanced Docker techniques
- Deploy your containerized application
Containerizing your FastAPI applications with Docker provides consistency across environments, simplifies deployment, and makes scaling easier. This approach is particularly valuable in microservices architectures and when working in teams where environment consistency is crucial.
Additional Resources
- FastAPI Official Documentation
- Docker Official Documentation
- Docker Compose Documentation
- FastAPI Project Templates with Docker
Exercises
- Modify the Dockerfile to use a non-root user for better security.
- Add a Redis service to your docker-compose.yml and integrate it with your FastAPI app.
- Create a production-ready Dockerfile that includes proper logging configuration.
- Set up a CI/CD pipeline to automatically build and deploy your Docker container.
- Implement environment-specific configuration using Docker environment variables.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)