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>