Docker packages your app with everything it needs, so it runs the same anywhere. FastAPI lets you build fast, efficient APIs with Python.
Together, they make development simple and deployment reliable. FastAPI keeps things fast and easy, while Docker ensures consistency across environments.
In this guide, you'll learn how to containerize a FastAPI application with Docker.
Prerequisites
Before starting, make sure you have:
- Python 3.8 or newer
- Docker Desktop or Engine installed
- Docker Compose also installed
- Basic understanding of FastAPI concepts
Setting up the project structure
Let's create a straightforward FastAPI project demonstrating key features while being Docker-friendly.
First, create a new directory for your project:
mkdir fastapi-docker-app && cd fastapi-docker-app
Set up a virtual environment to isolate your dependencies:
python3 -m venv venv
Activate the virtual environment:
source venv/bin/activate
Install FastAPI and Uvicorn (an ASGI server required by FastAPI):
pip install fastapi uvicorn
Create a requirements file to track dependencies:
pip freeze > requirements.txt
Now, create a simple main.py
file as your application entry point:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="FastAPI Docker Example")
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.get("/")
def read_root():
return {"message": "Welcome to FastAPI in Docker!"}
@app.get("/health")
def health_check():
return {"status": "healthy"}
@app.post("/items/")
def create_item(item: Item):
return {"item_name": item.name, "price_with_tax": item.price * 1.1}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
This FastAPI application includes:
- A root endpoint returning a welcome message
- A health check endpoint for container monitoring
- A POST endpoint demonstrating data validation with Pydantic models
- Configuration to listen on all interfaces (needed for Docker)
Let's test the application before containerizing it:
uvicorn main:app --reload
INFO: Will watch for changes in these directories: ['/Users/stanley/fastapi-docker-app']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28190] using StatReload
INFO: Started server process [28192]
INFO: Waiting for application startup.
INFO: Application startup complete.
Visit http://127.0.0.1:8000/
in your browser to see the welcome message:
You can also check out the automatic API documentation at http://127.0.0.1:8000/docs
- one of FastAPI's great features:
After confirming it works, stop the server with CTRL
+ C
.
Creating a Docker container for FastAPI
Now let's containerize our FastAPI application to make it portable and consistent across environments.
Create a Dockerfile
in your project root:
touch Dockerfile
Add the following configuration:
FROM python:3.13.3-slim-bookworm
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY main.py .
EXPOSE 8000
# Option 1: Run with Uvicorn directly
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# Option 2: Use FastAPI CLI (alternative)
# CMD ["fastapi", "run", "main.py", "--port", "8000"]
This Dockerfile:
- Uses a lightweight Python 3.13.3 base image
- Sets up a working directory for our application
- Installs Python dependencies first (for better caching)
- Copies our application code
- Exposes port
8000
for incoming connections - Starts the application with Uvicorn (or alternatively with the FastAPI CLI)
Create a .dockerignore
file to exclude unnecessary files:
touch .dockerignore
In your .dockerignore
file, add the following content:
venv/
__pycache__/
*.pyc
.git/
.gitignore
.idea/
.vscode/
Next, build your Docker image by running:
docker build -t fastapi-app .
This command creates an image called fastapi-app
using the current folder as the build context.
Once the image is built, start a container with:
docker run --name fastapi-container -d -p 8000:8000 fastapi-app
This launches the container in the background (-d
) and connects port 8000 on your machine to port 8000 inside the container.
Now open your browser and go to http://localhost:8000/
to make sure your FastAPI app is up and running inside the container:
With that, your containerized FastAPI app is live and ready to go.
Working with container logs
Logs are essential for monitoring and debugging your FastAPI app. Docker captures all output from your application, so you can easily check what's happening.
To view logs from your running container:
docker logs fastapi-container
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 192.168.215.1:45272 - "GET / HTTP/1.1" 200 OK
These messages confirm that Uvicorn has started and is handling requests on port 8000.
To follow the logs in real time (great for development and troubleshooting):
docker logs -f fastapi-container
Visit a few endpoints in your browser to see new log entries appear as they happen.
To view only the latest log lines:
docker logs --tail 10 fastapi-container
And to see logs from a specific time:
docker logs --since 2025-05-10T10:00:00 fastapi-container
When you're done with the container, stop and remove it:
docker stop fastapi-container
docker rm fastapi-container
Or, if you prefer to have the container clean itself up automatically when it stops, use the --rm
flag:
docker run --name fastapi-container --rm -d -p 8000:8000 fastapi-app
This helps keep your system tidy by removing the container as soon as it exits.
Using Docker Compose for development
While basic Docker commands are fine for simple apps, Docker Compose is better for managing more complex development setups. It lets you define and run multi-container applications using a single configuration file, making managing things easier.
First, stop any container you started earlier:
docker stop fastapi-container
Then, create a docker-compose.yml
file in the root of your project:
touch docker-compose.yml
Add the following configuration:
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- ./main.py:/app/main.py
environment:
- ENVIRONMENT=development
- LOG_LEVEL=debug
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
This setup comes with several advantages:
- Live code reloading applies changes to
main.py
instantly without rebuilding - Environment variables make it easy to manage development-specific settings
- Health checks automatically monitor your app’s status
- Restart policy helps your container recover if it crashes
- Simplified commands make it easier to manage the whole environment
Start the development environment with:
docker-compose up
You'll see output similar to:
[+] Building 4.4s (11/11) FINISHED docker:orbstack
...
=> => writing image sha256:897fb54cc17205ef097a81e53d50d32bace9cdecc40cfe2ca0882dd444e9dc84 0.0s
=> => naming to docker.io/library/fastapi-docker-app-api 0.0s
=> [api] resolving provenance for metadata file 0.0s
[+] Running 2/1
✔ Network fastapi-docker-app_default Created 0.0s
✔ Container fastapi-docker-app-api-1 Created 0.1s
Attaching to api-1
api-1 | INFO: Will watch for changes in these directories: ['/app']
api-1 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
api-1 | INFO: Started reloader process [1] using StatReload
api-1 | INFO: Started server process [8]
api-1 | INFO: Waiting for application startup.
api-1 | INFO: Application startup complete.
The most powerful feature of this setup is modifying code without container restarts. Try adding a new endpoint to main.py
:
from fastapi import FastAPI
from pydantic import BaseModel
...
@app.get("/version")
def get_version():
return {
"version": "1.0.0",
"framework": "FastAPI",
"python": "3.13.3"
}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
Save the file and visit http://localhost:8000/version
in your browser to see the changes take effect immediately:
The logs will show Uvicorn detecting the change:
api-1 | WARNING: StatReload detected changes in 'main.py'. Reloading...
api-1 | INFO: Shutting down
api-1 | INFO: Waiting for application shutdown.
api-1 | INFO: Application shutdown complete.
api-1 | INFO: Finished server process [8]
api-1 | INFO: Started server process [39]
api-1 | INFO: Waiting for application startup.
api-1 | INFO: Application startup complete.
api-1 | INFO: 192.168.107.1:57592 - "GET /version HTTP/1.1" 200 OK
api-1 | INFO: 192.168.107.1:57592 - "GET /favicon.ico HTTP/1.1" 404 Not Found
To run Docker Compose in the background:
docker-compose up -d
View logs from all services:
docker-compose logs
Or follow logs in real-time:
docker-compose logs -f
When you're done, shut everything down with:
docker-compose down
This workflow brings together Docker’s reliability and the quick feedback loop needed for development, making it a great fit for FastAPI projects of any scale.
Final thoughts
Using FastAPI with Docker and Docker Compose gives you a solid foundation for building and running web APIs. It simplifies development, ensures consistency across environments, and scales easily as your project grows.
For more details and best practices, visit the official docs:
Make your mark
Join the writer's program
Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.
Write for us
Build on top of Better Stack
Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.
community@betterstack.comor submit a pull request and help us build better products for everyone.
See the full list of amazing projects on github