Containers and Docker — Illustrated Guide

You've probably heard someone say "it works on my machine." Containers solve that problem.

The Problem

Without containers, every developer needs to:

  • Install the exact same Python version
  • Install the exact same system libraries
  • Configure their OS just right
  • Hope nothing conflicts with other projects

One difference in environment = "it works on my machine but not yours."

What Is a Container?

Think of a shipping container:

  • A shipping container holds cargo of any kind
  • It works the same on a ship, a truck, or a train
  • You don't care what's inside — you just move the container

A software container is the same idea:

  • It holds your app + all its dependencies (Python, libraries, config)
  • It runs the same on your laptop, your teammate's laptop, or a cloud server
  • You don't need to install Python — it's inside the container

Container vs Virtual Machine

┌─────────────────────────┐    ┌─────────────────────────┐
│  App A  │  App B        │    │  App A     │  App B     │
│  Libs    │  Libs         │    │  Libs       │  Libs      │
│  Guest OS│  Guest OS     │    │  ──── shared ────       │
│  ──── Hypervisor ────   │    │  Host OS                  │
│  Host OS                  │    │  Hardware                 │
│  Hardware                 │    └─────────────────────────┘
└─────────────────────────┘    Container (Docker)
   Virtual Machine
Virtual Machine Container
OS Each VM has its own OS Shares host OS
Startup Minutes Seconds
Size GBs MBs
Isolation Full Process-level

Docker

Docker is the most popular container platform. Three key concepts:

1. Dockerfile — The Recipe

A Dockerfile is like a recipe for baking a cake. It lists every ingredient and step:

FROM python:3.13-slim    # Start with this base (like "start with flour")
WORKDIR /app              # Set working directory
COPY app/requirements.txt .  # Add the ingredient list
RUN pip install -r requirements.txt  # Install ingredients
COPY app/ ./app/          # Add your code
EXPOSE 5000               # Open the door (port)
CMD ["python", "app/__init__.py"]  # Start the app

2. Image — The Baked Cake

An image is what you get after building the Dockerfile. It's a frozen, ready-to-run package. You can share it, store it, and run it anywhere.

3. Container — The Cake Being Eaten

A container is a running instance of an image. You can have multiple containers from the same image (just like multiple people can eat slices of the same cake).

Build → Run Flow

docker build -t my-app .   # Bake the cake (build image)
docker run my-app           # Eat the cake (start container)

Docker Compose — Multiple Containers

Real apps need more than one service: a web server + a database + maybe a cache. Docker Compose lets you define all of them in one file:

services:
  app:
    build: .              # Build from Dockerfile
    ports:
      - "5000:5000"       # Map port 5000 inside → outside
    volumes:
      - .:/app            # Live code reload
    env_file:
      - app/.env          # Load secrets

Start everything with one command:

docker compose up

Stop everything:

docker compose down

How Docker Is Used in This Project

Environment File Purpose
Development backend/Dockerfile Runs Flask with debug mode, live reload via volume mount, SQLite
Development backend/docker-compose.yml Single service: the Flask app with port mapping + .env file
Production backend/Dockerfile.prod Runs Flask with Gunicorn (production server), no debug mode
RAG advanced/docker-compose.rag.yml Adds PostgreSQL + pgvector alongside the Flask app

Key Docker Commands

Command What it does When to use
docker compose build Build or rebuild images After changing Dockerfile or requirements
docker compose up Start all services When you want to run the app
docker compose down Stop all services When you're done
docker compose down -v Stop + delete volumes To reset the database
docker compose logs View service logs To debug errors
docker compose exec app bash Open a shell inside the container To inspect the environment
docker compose run app python -c "..." Run one command inside the container Quick tests

Volumes — Saving Data

Containers are temporary. When you stop a container, all its files disappear. Volumes persist data outside the container:

volumes:
  - .:/app               # Mount your code → container (live reload)
  - sqlite_data:/app/instance  # Persist SQLite database

volumes:
  sqlite_data:           # Named volume (survives restarts)
  • Bind mount (. :/app) — Links a folder on your computer to a folder in the container. Changes in one appear in the other.
  • Named volume (sqlite_data:/app/instance) — Docker manages the storage. Survives docker compose down but not docker compose down -v.

Docker in Production (Hugging Face Spaces)

When you deploy to Hugging Face Spaces, the same Docker image runs in the cloud:

Your laptop:    docker compose up  → runs container with your code
Hugging Face:   reads Dockerfile.prod → builds → runs container at hf.space

Same image, same behavior, different location.

Visual Summary

┌───────────────────────────────────────────────────────┐
│                   Docker Architecture                  │
│                                                       │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐        │
│  │ Docker    │    │ Docker   │    │ Container│        │
│  │ Client    │───▶│ Daemon   │───▶│ (running │        │
│  │ (docker   │    │ (engine) │    │  image)  │        │
│  │  command) │    │          │    └──────────┘        │
│  └──────────┘    │          │    ┌──────────┐        │
│                  │          │    │ Image    │        │
│                  │          │◀───│ (stored  │        │
│                  │          │    │  recipe) │        │
│                  └──────────┘    └──────────┘        │
│                                                       │
│  ┌──────────────┐    ┌─────────────────────────┐      │
│  │ Dockerfile   │───▶│ Image (immutable snapshot)│    │
│  │ (recipe)     │    │ with Python + deps + code│     │
│  └──────────────┘    └──────────┬──────────────┘      │
│                                 ▼                     │
│                          ┌──────────────┐             │
│                          │ Container    │             │
│                          │ (running app)│             │
│                          └──────────────┘             │
└───────────────────────────────────────────────────────┘