Containerization creates consistent environments for applications. The core component in this process that defines how an application is packaged is the Dockerfile. Think of a Dockerfile as a recipe or a set of instructions that Docker follows to build an image containing your FastAPI application, its dependencies, necessary data files (like your ML model), and the information needed to run it.
A Dockerfile is simply a text file named Dockerfile (with no extension) placed in the root directory of your project. Inside this file, you write a series of commands, one per line, that specify the steps for assembling the image.
While there are many possible instructions you can use in a Dockerfile, a typical setup for a FastAPI application usually involves a few common ones:
FROM: Specifies the base image to build upon. Every Dockerfile must start with a FROM instruction. For Python applications, you'll typically use an official Python image (e.g., python:3.9). This provides a starting Linux environment with Python and pip pre-installed.WORKDIR: Sets the working directory for subsequent instructions like RUN, CMD, COPY, and ADD. If the directory doesn't exist, WORKDIR will create it. It helps organize your container's filesystem.COPY: Copies files or directories from your local machine (the build context) into the filesystem of the container image. You'll use this to get your application code, requirements.txt file, and ML model artifacts into the image.RUN: Executes commands in a new layer on top of the current image. This is commonly used to install Python dependencies using pip install. Each RUN instruction creates a new image layer, which has implications for caching and image size.EXPOSE: Informs Docker that the container listens on the specified network ports at runtime. This doesn't actually publish the port; it functions as documentation between the person who builds the image and the person who runs the container. For FastAPI applications run with Uvicorn's default settings, this is typically port 8000.CMD: Provides the default command to execute when a container starts from the image. There can only be one CMD instruction in a Dockerfile. If you specify an executable, it should be the first parameter. For running FastAPI, this command usually starts the Uvicorn server.Let's put these instructions together into a practical Dockerfile for the ML prediction service we've been building. Assume your project structure looks something like this:
.
├── app/
│ ├── main.py
│ ├── models/
│ │ └── your_model.joblib
│ └── ... (other python files)
├── requirements.txt
└── Dockerfile
Here’s a sample Dockerfile:
# Use an official Python runtime as a parent image
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Copy the requirements file into the container at /app
COPY requirements.txt .
# Install any needed packages specified in requirements.txt
# Use --no-cache-dir to reduce image size
# Use --compile to byte-compile python files for faster startup (optional)
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir --compile -r requirements.txt
# Copy the application code and model files into the container at /app
COPY ./app /app/app
COPY ./app/models /app/app/models
# Make port 8000 available outside this container
EXPOSE 8000
# Define environment variable (optional, can be set at runtime too)
ENV MODEL_PATH=/app/app/models/your_model.joblib
# Run main.py when the container launches using Uvicorn
# Listen on 0.0.0.0 to be accessible from outside the container
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Let's break down the example Dockerfile step-by-step:
FROM python:3.9-slim: We start with a slim version of the official Python 3.9 image. Using slim variants often results in smaller image sizes compared to the full image, as they include only the minimal packages needed to run Python.WORKDIR /app: We set the default directory inside the container to /app. All subsequent commands will run relative to this path.COPY requirements.txt .: We copy only the requirements.txt file from our project directory into the /app directory inside the container. Note the . indicating the destination directory (/app because of WORKDIR).RUN pip install ...: This is a critical step. We first upgrade pip itself and then install all the libraries listed in requirements.txt.
--no-cache-dir: This flag tells pip not to store the download cache, which helps keep the image layer smaller.--compile: This pre-compiles Python source files to bytecode (.pyc), which can slightly speed up application startup.requirements.txt and run pip install before copying the rest of the application code. This uses Docker's build cache. If requirements.txt hasn't changed, Docker can reuse the layer created by this RUN instruction, speeding up subsequent builds even if your application code changes.COPY ./app /app/app: We copy the contents of our local app directory (containing main.py, etc.) into a subdirectory named app inside the container's /app directory. The destination path becomes /app/app.COPY ./app/models /app/app/models: Similarly, we copy the ML model artifacts from the local app/models directory into /app/app/models within the container. You might adjust this depending on where your models are stored relative to your Dockerfile.EXPOSE 8000: We document that the application inside the container will listen on port 8000.ENV MODEL_PATH=...: This sets an environment variable inside the container. Your FastAPI application can then read this environment variable to know where to load the model file from, making the path configurable.CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]: This defines the command that will run when a container is started from this image. It executes the uvicorn server, telling it to run the app object found in the app/main.py module.
--host 0.0.0.0: This is important. It makes Uvicorn listen on all available network interfaces inside the container, allowing connections from outside the container (when the port is published). Using the default 127.0.0.1 would only allow connections from within the container itself.--port 8000: Matches the port specified in the EXPOSE instruction..dockerignore FileSimilar to .gitignore, you can create a .dockerignore file in the same directory as your Dockerfile. This file lists patterns of files and directories that should be excluded from the build context sent to the Docker daemon. This prevents unnecessarily large build contexts and avoids copying sensitive information or irrelevant files (like virtual environments, .pyc files, build artifacts, .git directories) into your image.
A typical .dockerignore might look like:
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
.git/
.pytest_cache/
By defining this Dockerfile, you provide a clear, repeatable set of instructions for packaging your FastAPI ML application. The next step is to use this file to build the Docker image and run it as a container.
Was this section helpful?
© 2026 ApX Machine LearningEngineered with