Docker: images, containers and volumes
Lesson 1 of 3
1Docker: images, containers and volumes
The big pictureDocker puts your app with everything it needs (Python, the libraries, the settings) in one box that runs the same way on your machine, your colleague's and the server.
What the picture shows: From recipe to container. Lasting data is mounted from outside the container so it survives deletion.
Map of this part
The concepts in order, click any one to jump to it:
Two ways into the same idea
An image is like a cake tin, and a container is a cake from it. You can bake ten cakes from the same tin, all identical, and throw one away without touching the tin. But write something on a cake and bin it, and it is gone, so you keep what matters in an outside notebook: that is the volume.
An image is read-only layers built from Dockerfile instructions, each instruction a cached layer. Order from least to most changing: copy requirements.txt and install first, then copy the code, so installs are not repeated on every edit. A container adds a temporary writable layer on top. Lasting data: a named volume Docker manages, or a bind mount of a folder from your machine (for development). Small, safe images: a -slim base, multi-stage builds, .dockerignore, a non-root user and pinned versions. Compose defines several services, their network and volumes in one file, and services call each other by name.
Before You Start
⌨️The terminal
The developer toolkit track.
🧱A web server
A Flask, FastAPI or Express app running locally.
The Core Idea
1A Dockerfile with ordered layers
Each line is a layer. What rarely changes goes at the top, and what changes often (the code) at the bottom:
# Dockerfile FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN useradd --create-home app USER app EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker build -t cafe-api . docker run --rm -p 8000:8000 cafe-api docker ps # running containers docker logs <id> # what it printed
COPY . . before installing the libraries. Any code edit invalidates the cached layer, so the whole install reruns on every build.2Volumes: data that lasts
Delete a database container without a volume and all the data is gone. With a volume the data stays and attaches to any new container:
docker volume create cafe-data docker run -d --name db -e POSTGRES_PASSWORD=secret \ -v cafe-data:/var/lib/postgresql/data postgres:17 docker rm -f db # container gone docker run -d --name db -e POSTGRES_PASSWORD=secret \ -v cafe-data:/var/lib/postgresql/data postgres:17 # data still there
3Compose: the app and its database together
One file describes both services, and the app reaches the database by the service name db:
# compose.yaml
services:
api:
build: .
ports: ["8000:8000"]
environment:
DATABASE_URL: postgresql://cafe:secret@db:5432/cafe
depends_on:
db:
condition: service_healthy
db:
image: postgres:17
environment:
POSTGRES_USER: cafe
POSTGRES_PASSWORD: secret
POSTGRES_DB: cafe
volumes: ["cafe-data:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD", "pg_isready", "-U", "cafe"]
interval: 5s
volumes:
cafe-data:docker compose up --build builds and starts everything, docker compose down stops without removing volumes, and down -v removes them too, so be careful.Worked example
Problem: the build takes 4 minutes on every one-line edit. Analysis: COPY . . sits at the top, and .venv and node_modules get copied too. Fix: copy the requirements file and install before copying the code, and add a .dockerignore listing .venv, .git and __pycache__. A build after a code edit now takes 5 seconds, and the image is hundreds of megabytes smaller.
Myth vs Reality
| Myth | Reality |
|---|---|
| A container is a small virtual machine. | It shares the host's kernel, so it is much lighter and starts much faster. |
| Data inside a container is safe. | It is lost when the container is deleted. Lasting data goes in a volume. |
Where you meet it at work
🧑💻One dev environment
The whole team runs the same versions with one command.
🚀Deployment
The same tested image goes to production.
Test your understanding
What separates an image from a container?
From one image you run many containers.
Why copy requirements.txt before the code?
Layers after the first change are rebuilt, so put what changes often at the bottom.
How does the app reach the database in Compose?
Compose creates a network where services call each other by name.
Quick review cards
Check Your Understanding
Why a non-root user in the container?
If someone breaks into the app their permissions are limited.
What does docker compose down -v remove?
The containers, the network and the volumes, meaning the data too.
Why a slim image?
Smaller, faster to pull, and fewer vulnerabilities.
Resources for this lesson
Grouped by type. Start with the official docs for precision, or an article or video for the explanation.
Done with this lesson?
Sign in to save your progress.