Developing a FastAPI Application in a Docker Container
Tags: docker, container, fastapi
Published: 2023-04-22 19:01:19

---
title: Developing a FastAPI Application in a Docker Container
published: true
description: Developing a FastAPI Application in a Docker Container
tags: docker, container, fastapi
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-22 19:00 +0000
---
In this tutorial, we will learn how to develop a Python FastAPI application in a Docker container. We will also use docker-compose to orchestrate the containerization of the application.

## Prerequisites

Before we start, make sure you have the following installed on your machine:

- Docker
- docker-compose
- Poetry
- Python 3.11.2

[Follow this tutorial to install prerequisites](https://dev.to/abbazs/how-to-install-the-latest-docker-and-docker-compose-on-debian-based-linux-systems-3o9j).

1. Setting up the Project

   Let's start by creating a new project directory and navigating to it:

   ```bash
   mkdir fastapi-docker
   cd fastapi-docker
   ```

1. Set the virtual env to be created inside the project directory

   ```bash
   poetry config virtualenvs.in-project true
   ```

1. Initialize the project using `poetry` and follow the instructions

   ```bash
   poetry init
   ```

   At the end of this step, you should have a pyproject.toml file that looks like this:

   ```toml
   [tool.poetry]
   name = "fastapi-docker-tutorial"
   version = "0.1.0"
   description = ""
   authors = ["Your Name <your@email.com>"]
   readme = "README.md"

   [tool.poetry.dependencies]
   python = "^3.11.2"
   fastapi = "^0.95.1"
   uvicorn = "^0.21.1"
   Jinja2 = "^3.1.2"

   [tool.poetry.group.dev.dependencies]
   black = "^23.3.0"
   mypy = "^1.2.0"

   [build-system]
   requires = ["poetry-core"]
   build-backend = "poetry.core.masonry.api"
   ```

1. Create a directory called `app`

   ```bash
   mkdir app
   cd app
   ```

1. Create a new file called `app.py` inside the `app` directory. This file will contain the FastAPI application code.

   ```python
   from fastapi import FastAPI, Request
   from fastapi.responses import HTMLResponse
   from fastapi.templating import Jinja2Templates

   app = FastAPI()
   templates = Jinja2Templates(directory="templates")

   @app.get("/", response_class=HTMLResponse)
   async def read_root(request: Request):
       return templates.TemplateResponse("index.html", {"request": request})
   ```

1. Create a new directory called `templates` in the same directory as your `app.py` file and add a new file called `index.html`. Add the following code to the file:

   ```html
   <!DOCTYPE html>
   <html>
     <head>
       <title>FastAPI Docker Tutorial</title>
     </head>
     <body>
       <h1>FastAPI Docker Tutorial</h1>
       <p>Welcome to the FastAPI Docker Tutorial!</p>
     </body>
   </html>
   ```

1. Install the dependencies for the project:

   ```bash
   poetry install
   ```

   This will install all the required dependencies for the project inside a virtual environment managed by Poetry.

1. Create the Dockerfile

   Now that we have the project set up with its dependencies, create a Dockerfile to containerize the application.

   Create a new file called `Dockerfile` in the project directory with the following content:

   ```Dockerfile
   FROM python:3.11.2-buster
   ENV DEBIAN_FRONTEND='noninteractive'
   RUN apt-get update && apt install -y curl
   RUN curl -sSL https://install.python-poetry.org | python
   ENV PATH="${PATH}:/root/.local/bin"
   RUN poetry config virtualenvs.in-project true
   WORKDIR /app
   COPY . .
   RUN poetry install
   EXPOSE 8000
   ```

   This Dockerfile is based on the official Python 3.11.2 image and installs Poetry inside the container. It also sets the working directory to `/app` and copies the project files into the container. Lastly, it installs the project dependencies with Poetry and exposes port 8000, which is the port our FastAPI application will listen on.

1. Create the `docker-compose.yml` file

   In this step, we will create a `docker-compose.yml` file to define and run our container. `docker-compose` is a tool that allows us to define and run multi-container Docker applications. We will use it to define our `tutorial` service.

   Create a new file called `docker-compose.yml` in the project directory with the following content:

   ```yaml
   version: "3.8"
   services:
     tutorial:
       build:
         context: .
         dockerfile: Dockerfile
       ports:
         - "8000:8000"
       volumes:
         - .:/app
         - /app/.venv
       command:
         [
           "poetry",
           "run",
           "uvicorn",
           "--host",
           "0.0.0.0",
           "--port",
           "8000",
           "app.app:app",
           "--reload",
         ]
   ```

   This `docker-compose.yml` file defines a service called `tutorial`. It uses the `build` keyword to specify that the Dockerfile for this service is located in the current directory (`.`) and has a name `Dockerfile`. And it will be exposed on port `8000` of the host machine (`ports: - "8000:8000"`).

   Two volumes are mounted to the container, the current working directory (`.`) is mapped to `/app` directory in the container, and the `.venv` directory located in `/app` is mounted to an anonymous volume. This allows us to persist the virtual environment even after the container is deleted, but also allows us to access the project directory from within the container.

   We are maintaining the local `.venv` because the local .venv is required for VSCode to provide syntax highlighting and code type checking based on the installed packages in the virtual environment. Additionally, if you have type annotations in your code, tools like mypy can use the virtual environment to perform static type checking. So maintaining the local .venv in sync with the container's virtual environment allows for better development experience and code quality.

   The `command` section specifies the command to be run when the container starts. Here, we are using Poetry to run the `uvicorn` server, which is the ASGI server that we will use to serve our FastAPI app. We specify the host and port number for the server, as well as the module and app that should be run. The `--reload` option enables live reloading of the server, so that any changes made to the code will automatically restart the server.

1. Start the container

   With the Dockerfile and `docker-compose.yml` file in place, we can start our container using the following command:

   ```bash
   docker-compose up --build
   ```

   This command will build and start our `tutorial` service in a container. The `--build` option is used to build the container before starting it. Once the container is running, you should see output similar to the following:

   ```bash
   tutorial_1  | INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
   tutorial_1  | INFO:     Started reloader process [1] using watchgod
   tutorial_1  | INFO:     Started server process [3]
   tutorial_1  | INFO:     Waiting for application startup.
   tutorial_1  | INFO:     Application startup complete.
   ```

1. Test the application

   With the container running, you can test the application by opening a web browser and navigating to `http://localhost:8000`. You should see the following output:

   ```text
   FASTAPI Docker Tutorial

   Welcome to the FASTAPI Docker Tutorial!
   ```

   Congratulations! You have successfully containerized a Python FastAPI application development using Docker and `docker-compose`.

1. Stop the container

   To stop the container, use the following command in the same terminal session where you ran `docker-compose up`:

   ```bash
   docker-compose down
   ```

   This command will stop and remove the containers and networks created by `docker-compose up`.