Docker Config

Part 1, Chapter 4

Let's containerize the FastAPI app.

Start by ensuring that you have Docker and Docker Compose:

$ docker -v
Docker version 24.0.7, build afdd53b

$ docker-compose -v
Docker Compose version v2.23.3-desktop.2

Make sure to install or upgrade them if necessary.

Add a Dockerfile to the "project" directory, making sure to review the code comments:

# pull official base image
FROM python:3.12.1-slim-bookworm

# set working directory
WORKDIR /usr/src/app

# set environment variables

# install python dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# add app
COPY . .

Here, we started with a slim-bookworm-based Docker image for Python 3.12.1. We then set a working directory along with two environment variables:

  1. PYTHONDONTWRITEBYTECODE: Prevents Python from writing pyc files to disc (equivalent to python -B option)
  2. PYTHONUNBUFFERED: Prevents Python from buffering stdout and stderr (equivalent to python -u option)

We then installed the dependencies and copied over the app.

Depending on your environment, you may need to add RUN mkdir -p /usr/src/app just before you set the working directory:

# set working directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

Add a .dockerignore file to the "project" directory as well:


Like the .gitignore file, the .dockerignore file lets you exclude specific files and folders from being copied over to the image.

Review Docker Best Practices for Python Developers for more on structuring Dockerfiles as well as some best practices for configuring Docker for Python-based development.

Then add a docker-compose.yml file to the project root:

version: '3.8'

    build: ./project
    command: uvicorn app.main:app --reload --workers 1 --host --port 8000
      - ./project:/usr/src/app
      - 8004:8000
      - ENVIRONMENT=dev
      - TESTING=0

This config will create a service called web from the Dockerfile.

So, when the container spins up, Uvicorn will run with the following settings:

  • reload enables auto reload so the server will restart after changes are made to the code base.
  • workers 1 provides a single worker process.
  • host defines the address to host the server on.
  • port 8000 defines the port to host the server on.

The volume is used to mount the code into the container. This is a must for a development environment in order to update the container whenever a change to the source code is made. Without this, you would have to re-build the image each time you make a change to the code.

Take note of the Docker compose file version used -- 3.8. Keep in mind that this version does not directly relate back to the version of Docker Compose installed; it simply specifies the file format that you want to use.

Build the image:

$ docker-compose build

This will take a few minutes the first time. Subsequent builds will be much faster since Docker caches the results. If you'd like to learn more about Docker caching, review the Order Dockerfile Commands Appropriately section.

Once the build is done, fire up the container in detached mode:

$ docker-compose up -d

Navigate to http://localhost:8004/ping. Make sure you see the same JSON response as before:

  "ping": "pong!",
  "environment": "dev",
  "testing": false

If you run into problems with the volume mounting correctly, you may want to remove it altogether by deleting the volume config from the Docker Compose file. You can still go through the course without it; you'll just have to re-build the image after you make changes to the source code.

Windows Users: Having problems getting the volume to work properly? Review the following resources:

  1. Docker on Windows — Mounting Host Directories
  2. Configuring Docker for Windows Shared Drives

You also may need to add COMPOSE_CONVERT_WINDOWS_PATHS=1 to the environment portion of your Docker Compose file. Review Declare default environment variables in file for more info.

You should now have:

├── .gitignore
├── docker-compose.yml
└── project
    ├── .dockerignore
    ├── Dockerfile
    ├── app
    │   ├──
    │   ├──
    │   └──
    └── requirements.txt

Mark as Completed