Chapter 10 Images & Builds
Docker on Ubuntu and what a container really is, images, tags and digests, the Dockerfile, history forensics, the build cache and the context, multi-stage builds for Java, Node, Python and Go, ENTRYPOINT and PID 1, base images, non-root users and leaked secrets, healthchecks, registries and multi-arch.
In plain words
Imagine packing a lunchbox the night before. You put in exactly what you will need, close the lid, and tomorrow anyone can open it anywhere and get the same lunch. Nobody has to cook in the school kitchen, and it does not matter which kitchen the school has.
A container image is that lunchbox for a program: the program plus every file it needs, sealed and labelled with a fingerprint. A container is the lunchbox opened on some machine: an ordinary Linux process that the kernel fences off with namespaces and limits with cgroups. This chapter is about packing the box well: writing the Dockerfile, reading docker build and docker history, keeping images small, fast to build, non-root and free of secrets, and pushing them to a registry under names that never lie.
Why it matters on call
Almost everything a platform team runs ships as an image, and you will review Dockerfiles constantly. Concretely: a teammate's PR has COPY . . above npm ci and a shell-form ENTRYPOINT, and you can say exactly what that costs (every build re-downloads dependencies, every deploy SIGKILLs in-flight requests). A container exits with 137 on every deploy and you know to check who PID 1 is. A CI build takes 8 minutes and you find the one cache-busting ARG. Security flags a password in an image and you know docker history --no-trunc proves it and that deleting the file in a later layer never fixed it. An image built on your Mac dies with exec format error on an amd64 server and you know it is arm64 vs amd64. These are daily tickets and standard interview questions, and the next chapter, running containers, assumes all of it.
Lessons
- Installing Docker, the daemon, and the socket
- What a container actually is
- Images, tags and digests
- The Dockerfile, and reading a build
- Reading docker history: where the bytes went
- The build cache, and why instruction order decides build time
- Cache mounts, remote caches, and builds in CI
- The build context and .dockerignore
- Multi-stage builds for Java: from 1GB to under 200MB
- ENTRYPOINT, CMD, and who is PID 1
- Multi-stage for Node, Python and Go
- Choosing a base: slim, alpine, distroless, scratch
- Non-root users, and secrets that leak into images
- HEALTHCHECK
- Reviewing a Dockerfile in five minutes
- Image anatomy: manifest, config, layers, digests
- Registries, tagging strategy and multi-arch images
42 hands-on labs (missions, incidents and drills) run in the terminal: Open this chapter in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.
Questions people ask
Is a container a lightweight VM?
No, and interviewers check this. A VM has its own kernel on virtual hardware. A container is a normal process on the host kernel, isolated by namespaces (its own PID tree, network, mounts, hostname) and limited by cgroups. That is why it starts in milliseconds, why uname -r inside prints the host's kernel, why an amd64 binary will not run on this arm64 box without emulation, and why a kernel exploit is not contained by Docker.
What is the difference between an image and a container?
An image is read-only: an ordered stack of layers plus a config (entrypoint, user, env). It never changes; a new version is a new image with a new digest. A container is an image plus one thin writable layer and runtime settings (ports, mounts, limits). Everything the process writes outside a volume lands in that writable layer and is destroyed by docker rm.
Is Docker the only tool that can build and run images?
No. Images follow the OCI standard, so what docker build produces runs unchanged under containerd directly, Podman and other OCI tools. Docker with BuildKit is simply the most common way to build and test images on laptops and build servers. Some features are Docker's own, such as the --init flag and acting on HEALTHCHECK, so other tools may ignore them.
Why do image size and build time matter so much?
Size is paid on every pull: every server that starts the container, every extra server added during a traffic spike, every scanner run. Big images also carry more packages, so more CVEs to triage. Build time is paid on every commit by every developer and every CI job. A well-ordered Dockerfile turns a 70-second rebuild into 15; a multi-stage build turns a 1GB image into under 200MB. Both are cheap to fix once you can read the build output.
Is Docker on my Mac the same as on this Ubuntu box?
The CLI is the same, but on macOS Docker Desktop runs a hidden Linux VM because containers need a Linux kernel. Here the daemon runs directly on the host under systemd, so you can see containers in ps, their cgroups under /sys/fs/cgroup and their namespaces in /proc. Both your Mac and this VM are arm64, which hides the architecture problem until something runs on amd64 servers.