Cracking ML Interviews

Open in the interactive study hub

Docker

Docker is the standard for packaging applications into portable, reproducible containers. It is used for deploying microservices, running local dev environments, and building CI/CD pipelines, and ML model serving is almost always shipped as a container image.


Table of Contents

  1. What is Docker?
  2. Core Concepts
  3. Installation & Setup
  4. Dockerfile
  5. Images & Containers
  6. Volumes & Networking
  7. Docker Compose
  8. Multi-Stage Builds
  9. Docker in CI/CD
  10. Security Best Practices
  11. Docker in 2026
  12. Cheat Sheet

What is Docker?

Docker packages an application and all its dependencies (libraries, runtime, config) into a container: a lightweight, isolated process that runs identically on any machine.

Without Docker:                    With Docker:
"It works on my machine!"   →     "It works everywhere."

Docker vs VMs

Feature Virtual Machine Docker Container
Isolation Full OS Process-level
Startup time Minutes Milliseconds
Size GBs MBs
Overhead High (hypervisor) Minimal
Portability Medium High

Containers share the host OS kernel: they're not VMs. They use Linux namespaces and cgroups for isolation.


Core Concepts

The Big Picture

Dockerfile  →  docker build  →  Image  →  docker run  →  Container
                                  ↕
                           Docker Registry
                         (Docker Hub, ECR, GCR)
Concept Description
Dockerfile Blueprint for building an image (text file with instructions)
Image Read-only snapshot of a filesystem + metadata. Immutable.
Container A running instance of an image. Ephemeral by default.
Registry Remote store for images (Docker Hub, AWS ECR, GitHub GHCR)
Layer Each Dockerfile instruction creates a cached, reusable layer
Volume Persistent storage that survives container restarts
Network Virtual network allowing containers to communicate

Installation & Setup

Linux

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER   # run Docker without sudo
newgrp docker

Verify

docker version
docker run hello-world

Docker Desktop (Mac/Windows)

Download from docker.com/products/docker-desktop. Includes Docker Engine + Compose + Kubernetes.


Dockerfile

A Dockerfile is a script of instructions that Docker executes layer by layer to build an image.

Basic Structure

# Base image
FROM python:3.12-slim

# Set working directory
WORKDIR /app

# Copy dependency files first (cache optimization)
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose port (documentation only, doesn't publish)
EXPOSE 8000

# Default command to run
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Key Instructions

Instruction Purpose Example
FROM Base image FROM python:3.12-slim
WORKDIR Set working directory WORKDIR /app
COPY Copy files from host COPY src/ /app/src/
ADD Like COPY + supports URLs/tar ADD archive.tar.gz /app/
RUN Execute command in new layer RUN apt-get install -y curl
ENV Set environment variable ENV PORT=8080
ARG Build-time variable ARG VERSION=1.0
EXPOSE Document port EXPOSE 5432
CMD Default run command CMD ["python", "app.py"]
ENTRYPOINT Fixed command (CMD appended) ENTRYPOINT ["gunicorn"]
USER Switch to non-root user USER appuser
HEALTHCHECK Container health check See below
LABEL Metadata LABEL version="1.0"

CMD vs ENTRYPOINT

# CMD: overridable default command
CMD ["python", "app.py"]
# docker run myimage python other.py   ← overrides CMD

# ENTRYPOINT: fixed executable, CMD becomes default args
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myimage other.py   ← runs "python other.py"

.dockerignore

Like .gitignore: prevents unnecessary files from being sent to the Docker build context:

.git
__pycache__
*.pyc
.env
node_modules
*.log
.DS_Store

Images & Containers

Building Images

# Build from Dockerfile in current directory
docker build -t myapp:1.0 .

# Build with specific Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .

# Build with build args
docker build --build-arg VERSION=2.0 -t myapp:2.0 .

# Build for specific platform (cross-compile)
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .

Image Management

# List local images
docker images
docker image ls

# Pull from registry
docker pull python:3.12-slim
docker pull ghcr.io/myorg/myapp:latest

# Push to registry
docker tag myapp:1.0 myrepo/myapp:1.0
docker push myrepo/myapp:1.0

# Inspect image layers
docker history myapp:1.0
docker inspect myapp:1.0

# Remove image
docker rmi myapp:1.0
docker image prune -a   # remove all unused images

Running Containers

# Run interactively
docker run -it ubuntu bash

# Run in background (detached)
docker run -d --name my-postgres postgres:16

# Run with environment variables
docker run -d \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=mydb \
  --name postgres \
  postgres:16

# Run with port mapping (host:container)
docker run -d -p 8080:80 nginx

# Run with volume mount
docker run -d \
  -v /host/data:/container/data \
  myapp:1.0

# Run with resource limits
docker run -d \
  --memory="512m" \
  --cpus="1.5" \
  myapp:1.0

# Auto-remove when stopped
docker run --rm -it myapp:1.0 bash

Container Management

# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# Stop / start / restart
docker stop my-app
docker start my-app
docker restart my-app

# Remove container
docker rm my-app
docker rm -f my-app  # force remove running container

# View logs
docker logs my-app
docker logs -f my-app   # follow (tail -f)
docker logs --tail 100 my-app

# Execute command in running container
docker exec -it my-app bash
docker exec my-app cat /etc/hosts

# Copy files to/from container
docker cp ./config.yaml my-app:/app/config.yaml
docker cp my-app:/app/logs/app.log ./logs/

# Inspect container
docker inspect my-app

# Stats (CPU, memory, network)
docker stats

# Clean up everything stopped
docker container prune

Volumes & Networking

Volumes

Volumes are the preferred way to persist data. They're managed by Docker and survive container restarts/removal.

# Create volume
docker volume create pgdata

# Use volume in container
docker run -d \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

# Bind mount (maps host directory)
docker run -v $(pwd)/data:/app/data myapp

# List volumes
docker volume ls

# Inspect volume
docker volume inspect pgdata

# Remove volume
docker volume rm pgdata
docker volume prune   # remove all unused

Volumes vs Bind Mounts vs tmpfs:

Type Use Case Managed By
Volume Production data persistence Docker
Bind mount Dev: sync host code into container Host OS
tmpfs Sensitive temp data (in memory only) Memory

Networking

# List networks
docker network ls

# Create network
docker network create my-network

# Connect containers on same network
docker run -d --name db --network my-network postgres:16
docker run -d --name app --network my-network myapp:1.0
# app can reach db via hostname "db"

# Inspect network
docker network inspect my-network

# Connect/disconnect running container
docker network connect my-network my-app
docker network disconnect my-network my-app

Built-in Networks:

Network Description
bridge Default. Containers communicate via IP.
host Container shares host network stack.
none No networking.
Custom bridge User-defined. Containers reach each other by name.

Docker Compose

Docker Compose defines and runs multi-container applications from a single compose.yaml file (the older docker-compose.yml name is still read). Use the Compose v2 plugin, docker compose (space); the standalone Python docker-compose v1 binary is end-of-life.

Example: Full-Stack App

# compose.yaml
# No top-level "version:" key: it is obsolete in the Compose Specification
# and current Compose prints a warning and ignores it.

services:
  # PostgreSQL database
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis cache
  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

  # Backend API
  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://appuser:secret@db:5432/appdb
      REDIS_URL: redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    volumes:
      - ./backend:/app   # dev hot-reload
    restart: unless-stopped

  # Frontend
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    environment:
      VITE_API_URL: http://localhost:8000
    depends_on:
      - api

volumes:
  pgdata:

Compose Commands

# Start all services (detached)
docker compose up -d

# Start and rebuild images
docker compose up -d --build

# Stop all services
docker compose down

# Stop and remove volumes
docker compose down -v

# View logs
docker compose logs -f
docker compose logs -f api

# Scale a service
docker compose up -d --scale api=3

# Run one-off command
docker compose run --rm api python manage.py migrate

# Exec into service
docker compose exec api bash

# Status
docker compose ps

# Restart single service
docker compose restart api

# Pull latest images
docker compose pull

Multi-Stage Builds

Multi-stage builds keep production images small by separating build-time dependencies from runtime.

Python Example

# Stage 1: Build
FROM python:3.12 AS builder

# Install dependencies into a virtualenv that is easy to copy as one directory
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Stage 2: Production (lean)
FROM python:3.12-slim AS production

# Copy only the installed packages from builder (no compilers, no build cache)
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

WORKDIR /app
COPY . .

# Create non-root user
RUN useradd -r -s /bin/false appuser
USER appuser

EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Node.js Example

# Stage 1: Dependencies
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

# Stage 2: Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 3: Production
FROM node:22-alpine AS production
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json .

EXPOSE 3000
CMD ["node", "dist/index.js"]

Docker in CI/CD

GitHub Actions Example

name: Build and Push

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

Security Best Practices

# 1. Use specific image tags, not :latest
FROM python:3.12.3-slim-bookworm   # not FROM python:latest

# 2. Run as non-root user
RUN useradd --create-home --shell /bin/bash appuser
USER appuser

# 3. Minimize layers and clean up in same RUN step
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

# 4. Don't copy secrets into images
# Use --secret or runtime environment variables

# 5. Use COPY over ADD (more explicit)
COPY requirements.txt .

# 6. Set read-only filesystem
# docker run --read-only myapp (with tmpfs for /tmp)

# 7. Scan images for vulnerabilities
# docker scout cves myapp:latest
# trivy image myapp:latest

Secrets Management

# Build-time secret (never stored in image layer)
docker build --secret id=mysecret,src=./secret.txt .
# In Dockerfile - use secret without storing it
RUN --mount=type=secret,id=mysecret \
    cat /run/secrets/mysecret | pip install --extra-index-url ...

Docker in 2026

What's Changed

Feature Status in 2026
Docker Scout Built-in vulnerability scanning in Docker Desktop
Buildx / BuildKit Default build backend; multi-platform builds standard
Docker Init docker init generates Dockerfile + Compose automatically
Testcontainers Standard for integration testing with real containers
Wasm support Docker Desktop shipped Wasm workloads as a beta feature, since announced for deprecation; treat it as experimental
OCI standard All major runtimes (containerd, podman) are OCI-compliant

Alternatives to Know

Tool Description
Podman Daemonless, rootless Docker alternative (Red Hat)
nerdctl Docker-compatible CLI for containerd
Finch AWS's open-source container CLI for macOS
Buildpacks Build images from source without Dockerfiles (Cloud Native Buildpacks)

Cheat Sheet

# === IMAGES ===
docker build -t name:tag .              # Build image
docker pull image:tag                   # Pull image
docker push repo/image:tag             # Push image
docker images                          # List images
docker rmi image:tag                   # Remove image
docker image prune -a                  # Remove all unused images

# === CONTAINERS ===
docker run -d -p host:container name    # Run detached with port
docker run -it image bash              # Interactive shell
docker run --rm image command          # Auto-remove on exit
docker ps                              # Running containers
docker ps -a                           # All containers
docker stop/start/restart name         # Lifecycle
docker rm name                         # Remove container
docker logs -f name                    # Tail logs
docker exec -it name bash              # Shell into running container
docker stats                           # Resource usage

# === VOLUMES ===
docker volume create vol               # Create volume
docker volume ls                       # List volumes
docker run -v vol:/path image          # Attach volume
docker run -v $(pwd):/path image       # Bind mount

# === NETWORKING ===
docker network create net              # Create network
docker network ls                      # List networks
docker run --network net image         # Use network

# === COMPOSE ===
docker compose up -d                   # Start all services
docker compose up -d --build          # Start and rebuild
docker compose down                    # Stop all
docker compose down -v                 # Stop + remove volumes
docker compose logs -f [service]       # Logs
docker compose exec service bash       # Shell into service
docker compose ps                      # Status

# === CLEANUP ===
docker system prune                    # Remove all unused
docker system prune -a --volumes       # Nuclear cleanup
docker container prune                 # Remove stopped containers
docker volume prune                    # Remove unused volumes
docker image prune -a                  # Remove unused images

View or edit this guide on GitHub