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. Survivesdocker compose downbut notdocker 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)│ │
│ └──────────────┘ │
└───────────────────────────────────────────────────────┘