Containerizing ML Models with Docker
Docker packages your model, its dependencies, and a serving application into a single portable container image that runs identically in development, staging, and production — eliminating the classic "it works on my machine" problem.
Writing a Dockerfile for an ML Model
A Dockerfile is a text recipe that specifies the base image, system dependencies, Python packages, model artifacts, and the command to start the server.
Example Dockerfile
<pre><code class="language-python"># Dockerfile
# Use an official slim Python image
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Copy dependency file first (layer caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy model artifact and application code
COPY model.joblib .
COPY app.py .
# Expose the port the API will listen on
EXPOSE 8000
# Start the FastAPI server
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]</pre>
requirements.txt
<pre><code class="language-python">fastapi==0.111.0
uvicorn==0.29.0
scikit-learn==1.4.2
joblib==1.4.0
numpy==1.26.4
pandas==2.2.2</pre>
Building and Running the Container
Use the Docker CLI to build an image from the Dockerfile and run it as a container, mapping the container's port to a port on the host machine.
Build and Run Commands
<pre><code class="language-python"># Build the image (run in the directory containing the Dockerfile)
# docker build -t my-ml-model:v1 .
# Run the container, mapping host port 8000 to container port 8000
# docker run -d -p 8000:8000 --name ml-api my-ml-model:v1
# Test the running container
# curl http://localhost:8000/health
# View logs
# docker logs ml-api
# Stop and remove
# docker stop ml-api && docker rm ml-api</pre>
Environment Variables for Configuration
<pre><code class="language-python"># Pass configuration at runtime via environment variables
# docker run -e MODEL_PATH=/app/model.joblib -e LOG_LEVEL=info -p 8000:8000 my-ml-model:v1
# In app.py, read the env var:
import os
MODEL_PATH = os.getenv("MODEL_PATH", "model.joblib")</pre>
Multi-Stage Builds for Smaller Images
Multi-stage builds use a heavy builder image to install packages, then copy only the runtime artifacts into a slim final image — reducing image size significantly.
Multi-Stage Dockerfile Pattern
<pre><code class="language-python"># Stage 1: builder
FROM python:3.11 AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --target=/build/deps -r requirements.txt
# Stage 2: slim runtime
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /build/deps /usr/local/lib/python3.11/site-packages
COPY model.joblib app.py ./
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]</pre>