Docker has become the standard for deploying modern applications. Combined with FastAPI’s speed and simplicity, you get a powerful foundation for building production-ready APIs. This guide walks you through containerizing a FastAPI application from scratch.
Prerequisites
- Docker installed on your machine
- Basic understanding of FastAPI
- Python 3.11+ (we’ll use uv for package management)
Project Structure
Let’s start with a simple project structure:
fastapi-docker/ |
Creating the FastAPI Application
First, let’s create a simple FastAPI app:
# app/main.py |
And the project configuration:
# pyproject.toml |
Writing the Dockerfile
Here’s a production-ready Dockerfile using multi-stage builds and uv:
# Dockerfile |
Understanding the Dockerfile
Let’s break down the key parts:
Multi-stage Build
We use two stages:
- Builder stage: Installs dependencies and creates the virtual environment
- Production stage: Contains only the runtime essentials
This reduces the final image size significantly.
Using uv
We copy uv directly from its official image, which is faster than installing via pip. The --mount=type=cache directive caches downloaded packages between builds.
Environment Variables
PYTHONUNBUFFERED=1: Ensures Python output is sent straight to the terminalPYTHONDONTWRITEBYTECODE=1: Prevents Python from writing .pyc files
Building and Running
Build the Docker image:
docker build -t fastapi-app . |
Run the container:
docker run -d -p 8000:8000 --name my-api fastapi-app |
Test it:
curl http://localhost:8000 |
Using Docker Compose
For more complex setups with databases and other services, use Docker Compose:
# compose.yaml |
Start all services:
docker compose up -d |
View logs:
docker compose logs -f api |
Stop services:
docker compose down |
Development with Hot Reload
For development, you want hot reload when code changes. Create a separate compose file:
# compose.dev.yaml |
Run with:
docker compose -f compose.dev.yaml up |
Now changes to your code will automatically reload the server.
Production Best Practices
1. Use a Non-Root User
Add this to your Dockerfile for better security:
# Create non-root user |
2. Add Health Checks
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ |
3. Use .dockerignore
Create a .dockerignore file to exclude unnecessary files:
__pycache__ |
4. Pin Your Base Image
Instead of python:3.12-slim, use a specific digest:
FROM python:3.12-slim@sha256:... |
This ensures reproducible builds.
5. Use Gunicorn in Production
For production, use Gunicorn with Uvicorn workers:
CMD ["gunicorn", "app.main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000"] |
Add gunicorn to your dependencies:
dependencies = [ |
Complete Production Dockerfile
Here’s the complete production-ready Dockerfile with all best practices:
FROM python:3.12-slim AS builder |
Conclusion
Containerizing your FastAPI application with Docker provides consistency across environments, simplified deployments, and easy scaling. The combination of multi-stage builds, uv for fast dependency installation, and proper production configurations gives you a solid foundation for deploying your APIs.
Key takeaways:
- Use multi-stage builds to minimize image size
- Use uv for fast, reproducible dependency installation
- Run as non-root user in production
- Add health checks for container orchestration
- Use Gunicorn with Uvicorn workers for production workloads