Back to Scaling Python Applications guides

Containerizing FastAPI Applications with Docker

Stanley Ulili
Updated on May 21, 2025

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:

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:

main.py
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
Output
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:

Screenshot of the welcome message in the browser

You can also check out the automatic API documentation at http://127.0.0.1:8000/docs - one of FastAPI's great features:

Screenshot of the FastAPI automatic API documentation

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:

Dockerfile
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:

.dockerignore
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:

Screenshot of the FastAPI app 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
Output
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:

docker-compose.yml
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:

Output
[+] 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:

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:

Screenshot of the browser showing the version page

The logs will show Uvicorn detecting the change:

Output
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:

Got an article suggestion? Let us know
Next article
Get Started with Job Scheduling in Python
Learn how to create and monitor Python scheduled tasks in a production environment
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

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
Writer of the month
Marin Bezhanov
Marin is a software engineer and architect with a broad range of experience working...
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.com

or submit a pull request and help us build better products for everyone.

See the full list of amazing projects on github