Docker from First PrinciplesPost 16 of 18 · 18 of 18 written
- How to use this guide
- 01The problem Docker solves
- 02What a container really is
- 03Images: layers, hashes and registries
- 04Docker's architecture
- 05Container lifecycle
- 06Dockerfiles in depth
- 07Storage: where data lives
- 08Networking
- 09Docker Compose for multi-service AI apps
- 10GPUs in containers
- 11Building good images for AI workloads
- 12Serving and production patterns
- 13Security
- 14Debugging and introspection
- 15Beyond Docker
- 16Hands-on learning path
- 17Question bank
- 18Glossary and cheat sheet
16. Hands-on learning path
Docker · 16 of 184 min readOct 2026
Docker · Post 16 of 184 min readOct 2026
Fourteen labs, each proving one idea from the guide with your own hands. Labs 1 to 10 run on your Mac; 11 and 12 need a rented Linux GPU machine; 13 and 14 run anywhere. Keep a short lab notebook: the command, what you expected, what happened.
- Lab 1 - A container is a process. Get a Linux shell with namespaces you can inspect: on the Mac,
docker run --rm -it --privileged --pid=host ubuntu:24.04 bashshows the VM’s processes. Startdocker run -d --name s alpine sleep 1000in another terminal and find thesleepprocess in the first one. Compareuname -rin an Ubuntu and an Alpine container. Done when you can explain why the kernel version is identical. - Lab 2 - Build a container by hand. Export a filesystem with
docker export $(docker create alpine) -o alpine.tar. In a privileged Ubuntu container with the tar mounted, runmkdir rootfs && tar -xf alpine.tar -C rootfs, thenunshare --mount --uts --ipc --net --pid --fork chroot rootfs /bin/sh. Inside,mount -t proc proc /proc, then tryps,hostname fooandip addr. Done when you’ve seen PID 1, a private hostname and an empty network: a container with no Docker involved. - Lab 3 - Cgroups kill. Run
docker run --name oom --memory 100m --memory-swap 100m python:3.12-slim python -c "x = bytearray(300_000_000)", thenecho $?anddocker inspect -f '{{.State.OOMKilled}}' oom. Done when you’ve seen exit 137 andtrue. - Lab 4 - Layers don’t forget. Write one Dockerfile that creates a 500 MB file in one
RUNand deletes it in the next, and another that does both in oneRUN. Compare sizes withdocker image lsand inspect withdocker historyanddive. Done when you can explain the difference by pointing at a layer. - Lab 5 - The cache. Build a small FastAPI image with the slow ordering from Part 6, edit one
.pyfile, and time the rebuild; then reorder and repeat. Add a uv or pip cache mount and time a dependency change. Done when a code-only rebuild takes seconds. - Lab 6 - PID 1 and signals. Write a Python loop that prints a message and exits on SIGTERM. Build it once with
CMD ["python", "app.py"]and once withCMD echo start && python app.py(shells often hand a single command straight over, so the second command keeps the shell as PID 1). Timedocker stopfor each. Done when you’ve seen the 10-second hang and the clean exit. - Lab 7 - Networking. Run two containers on the default bridge and try to reach one by name; repeat on a user-defined network. Run a server bound to
127.0.0.1, publish its port and fail to reach it; rebind to0.0.0.0. Done when you can predict each result before running it. - Lab 8 - Storage. Create a named volume for a Hugging Face cache, download a small model in one container and load it offline in a second. Bind-mount a dataset read-only and confirm a write fails. Done when the second container loads the model with no network (
--network none). - Lab 9 - A Compose RAG stack. Build the stack from Part 9 with FastAPI, pgvector and Redis, with Ollama running natively on the Mac. Add an ingestion command run with
docker compose run --rm. Done whendocker compose down && docker compose up -dbrings everything back, data included. - Lab 10 - Multi-architecture. Build your API image for
linux/amd64,linux/arm64, push it to GHCR, and check the index withdocker buildx imagetools inspect. Done when the same tag runs on your Mac and on an amd64 cloud VM. - Lab 11 - GPU basics. On a rented GPU VM, install the driver and the NVIDIA Container Toolkit and run the
nvidia-smismoke test from Part 10, driven from your Mac through a Docker context. Train a small model withnum_workers=4without--shm-size, watch it fail, then fix it. Done when you can explain each of the four pieces in the GPU table. - Lab 12 - Serving. On the GPU VM, run
compose.gpu.yamlwith vLLM, add a readiness healthcheck, put Caddy in front with TLS, and keep the model port bound to127.0.0.1. Send a request duringdocker compose restartand observe what happens. Done when a redeploy drops no requests. - Lab 13 - Hardening. Take the API image: non-root user,
--read-onlywith a tmpfs,--cap-drop ALL,no-new-privileges. Scan it with Trivy or Docker Scout and generate an SBOM. Done when the service still works and the scan has no critical findings you can’t explain. - Lab 14 - Beyond Docker. Either convert your training image to Apptainer and run it on a cluster you have access to, or deploy the API to a local
kindcluster with a Deployment, a Service and readiness probes. Done when you can map each Kubernetes object to its Docker equivalent from Part 15.