LESSON 83 · ADVANCED4 min read

Docker Fundamentals

Build small, reproducible Python images and run multi-service stacks with Docker Compose.

Python 3.12+Free to readModule 19 of 19
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Explain how an image, container, and layer relate.
  • Write a Dockerfile for a Python service.
  • Keep images small and free of secrets.
  • Describe a local stack with Docker Compose.

01An image is a reproducible filesystem

A Docker image is an immutable filesystem plus metadata. A container is a running process using that image. Each Dockerfile instruction creates a layer, and layers are cached. Put the least frequently changing instructions first so a source edit does not reinstall every dependency.

A reproducible image starts from a pinned base, copies only the files it needs, and runs one clear command. The goal is not merely 'it works on my machine'; it is that the same inputs produce the same runnable artifact on another machine.

DockerfileDockerfile
FROM python:3.12-slimENV PYTHONDONTWRITEBYTECODE=1 \    PYTHONUNBUFFERED=1WORKDIR /appCOPY requirements.txt .RUN python -m pip install --no-cache-dir -r requirements.txtCOPY app.py .RUN useradd --create-home appuserUSER appuserEXPOSE 8000CMD ["python", "app.py"]

02Build and run a service

docker build creates an image from the Dockerfile and a context. The context is the directory sent to the daemon, so a large .dockerignore can dramatically reduce build time. Tag images with a version or commit SHA, not only latest, so a deployment can identify exactly what it is running.

Publish ports only when the service must be reachable from the host. Inside a Compose network, services reach each other by service name and internal port. Environment variables carry configuration; secrets should be mounted or injected by the platform, not baked into the image.

docker_commands.txtShell
docker build -t task-api:1.0.0 .docker run --rm -p 8000:8000 --env-file .env task-api:1.0.0docker psdocker logs <container>

03Keep images small and safe

Use a slim base, install only runtime dependencies, and remove build caches. A multi-stage build can compile wheels in one stage and copy the installed environment into a smaller runtime stage. Do not copy the entire repository when a few files are enough.

Run as a non-root user, pin base image versions, and scan the image for known vulnerabilities as part of CI. Never put a token in an ARG or ENV instruction: layers are inspectable. A .dockerignore should exclude .git, tests, local virtual environments, and secrets. A smaller image also reduces the attack surface and the time every deploy spends pulling layers.

.dockerignorePlain text
.git.venv__pycache__.pytest_cache.envtests*.md

04Compose a local stack

Docker Compose describes multiple services, their build or image, environment, ports, and volumes. It is ideal for a web service plus a database. Named volumes preserve database files across container recreation; bind mounts are convenient for local source during development.

Add a healthcheck so dependent services wait for readiness, not merely for process start. depends_on controls start order but does not prove the database accepts connections. A healthcheck plus an application retry policy is a more honest readiness strategy.

compose.yamlYAML
services:  api:    build: .    ports:      - "8000:8000"    environment:      DATABASE_URL: postgresql://app:secret@db:5432/app    depends_on:      db:        condition: service_healthy  db:    image: postgres:16    environment:      POSTGRES_USER: app      POSTGRES_PASSWORD: secret      POSTGRES_DB: app    volumes:      - db_data:/var/lib/postgresql/data    healthcheck:      test: ["CMD-SHELL", "pg_isready -U app"]      interval: 5s      retries: 10volumes:  db_data:
LET IT CLICK

The ideas worth keeping.

  • Images are built from cached layers; order instructions for reuse.
  • Run as a non-root user and keep secrets out of layers.
  • Compose describes relationships, volumes, and readiness checks.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Dockerize a standard-library service

Write a Dockerfile for a Python service with no external dependencies. Run as a non-root user, expose port 8000, and set unbuffered output.

your_turn_1.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyDockerfile
FROM python:3.12-slimENV PYTHONUNBUFFERED=1WORKDIR /appCOPY app.py .RUN useradd --create-home appuserUSER appuserEXPOSE 8000CMD ["python", "app.py"]

No pip install is needed for a standard-library service. The non-root user limits the damage if the process is compromised, and unbuffered output makes container logs timely.

02

Compose a web service and database

Write a compose file with an api service built from the current directory and a postgres service with a named volume and a healthcheck. Make the api wait for the database to become healthy.

your_turn_2.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyYAML
services:  api:    build: .    ports: ["8000:8000"]    depends_on:      db:        condition: service_healthy  db:    image: postgres:16    environment:      POSTGRES_PASSWORD: secret    volumes:      - db_data:/var/lib/postgresql/data    healthcheck:      test: ["CMD-SHELL", "pg_isready -U postgres"]      interval: 5s      retries: 10volumes:  db_data:

The healthcheck makes readiness observable. A named volume keeps the database alive when the container is recreated; an anonymous volume would be harder to identify and back up.

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01What is the difference between an image and a container?
02Why put COPY requirements.txt before COPY .?
03What does depends_on with a healthcheck condition provide?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Dockerize a standard-library service

Write a Dockerfile for a Python service with no external dependencies. Run as a non-root user, expose port 8000, and set unbuffered output.

EXPECTED OUTPUT
[Process exited with code 0 - No output]
main.pyPython 3
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this lesson, when you want a second angle or the full details.

One lesson closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock lesson completion.