Why Docker for ML?
ML models depend on a precise combination of Python version, library versions (scikit-learn 1.3 vs 1.4 can differ in serialisation format), and system libraries. Docker packages your code, model, and all dependencies into a container image — a single artefact that runs identically on any machine, in any cloud.
ImageA read-only template defining
the filesystem: base OS, libraries, your code. Built once, reused anywhere.
ContainerA running instance of an
image. Isolated from the host. Starts in milliseconds. Stateless by default.
DockerfileA recipe for building an
image. Step-by-step instructions: start from base image → install deps → copy code → set
entrypoint.
RegistryA repository of images.
Docker Hub (public), AWS ECR, GCP Artifact Registry (private). Push/pull like git.
Writing the Dockerfile
Python
# Dockerfile
# ── Base image: slim Python (removes docs/tests from full image, saves ~200MB)
FROM python:3.11-slim
# ── Set working directory inside container
WORKDIR /app
# ── Install system dependencies first (changes rarely → cached layer)
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
# ── Install Python dependencies (copy requirements first for better caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# ── Copy application code and model
COPY app.py .
COPY models/ ./models/
# ── Create non-root user (security best practice)
RUN useradd -m appuser && chown -R appuser /app
USER appuser
# ── Expose the port FastAPI listens on
EXPOSE 8000
# ── Health check: Docker will restart container if this fails
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD curl -f http://localhost:8000/health || exit 1
# ── Run command
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
Python
# requirements.txt
fastapi==0.111.0
uvicorn[standard]==0.29.0
pydantic==2.7.1
scikit-learn==1.4.2
joblib==1.4.0
numpy==1.26.4
Building and Running
Bash
# ── Build the image
docker build -t zerotoml-api:1.0.0 .
# -t: tag (name:version)
# . : build context (current directory)
# ── List images
docker images | grep zerotoml
# ── Run locally
docker run -d --name ml-api -p 8000:8000 zerotoml-api:1.0.0
# -d: detached (background)
# -p HOST_PORT:CONTAINER_PORT
# ── Test it
curl http://localhost:8000/health
# {"status":"ok","model_loaded":true}
# ── View logs
docker logs ml-api --follow
# ── Stop and remove
docker stop ml-api && docker rm ml-api
Docker Compose — Multi-Service Setup
Python
# docker-compose.yml
# Orchestrates the ML API + Redis cache + monitoring together
services:
api:
build: .
image: zerotoml-api:1.0.0
ports:
- "8000:8000"
environment:
- ENV=production
- MODEL_PATH=/app/models/v1/pipeline.joblib
volumes:
- ./models:/app/models:ro # mount models dir as read-only
depends_on:
- redis
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
# Optional: monitoring with Prometheus
# prometheus:
# image: prom/prometheus
Bash
# Start all services
docker compose up -d
# Scale API to 3 replicas
docker compose up -d --scale api=3
# View running services
docker compose ps
# Teardown
docker compose down
Pushing to a Registry
Bash
# ── Docker Hub
docker login
docker tag zerotoml-api:1.0.0 Muhammad-waqas1/zerotoml-api:1.0.0
docker push Muhammad-waqas1/zerotoml-api:1.0.0
# ── AWS ECR
aws ecr create-repository --repository-name zerotoml-api
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
docker tag zerotoml-api:1.0.0 123456789.dkr.ecr.us-east-1.amazonaws.com/zerotoml-api:1.0.0
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/zerotoml-api:1.0.0
Summary
- Docker solves the "it works on my machine" problem by packaging code + dependencies into a portable image.
- Layer your Dockerfile: system deps → Python deps → app code. Rarely-changed layers are cached and re-used.
- Always run as a non-root user. Add
HEALTHCHECKfor production reliability. - Docker Compose orchestrates multi-container setups (API + DB + cache) locally.
- Push to Docker Hub, AWS ECR, or GCP Artifact Registry for cloud deployment.