Images and Dockerfiles¶
Play it
Layer Cake: Arrange Dockerfile lines and watch which layers the build cache reuses.
The game links back to this page from: Quick reference, Dockerfile tips, Debugging a build.
A container image is a stack of read-only layers plus metadata (entrypoint, env,
exposed ports). docker build turns a Dockerfile into layers, one per instruction that
changes the filesystem; docker run adds a thin writable layer on top and starts a
process in it.
Quick reference¶
docker build -t myapp:dev . # build an image from ./Dockerfile
docker run --rm -p 8080:80 myapp:dev # run it, remove the container on exit
docker image ls # list local images
docker history myapp:dev # inspect the layer stack and their sizes
docker exec -it <container> sh # shell into a running container
docker system prune -f # reclaim space (dangling images/containers/cache)
Try it: examples/hello-nginx¶
cd topics/docker/examples/hello-nginx
docker build -t hello-nginx .
docker run --rm -p 8080:80 hello-nginx
curl localhost:8080 # -> index.html
Read the Dockerfile alongside docker history hello-nginx — each RUN/COPY line
maps to one layer.
Dockerfile tips¶
- Pin base image tags (
nginx:1.27-alpine, notnginx:latest) so builds are reproducible. - Order instructions least- to most-frequently-changing: dependency manifests before source code, so an edit to application code doesn't invalidate the (slow) dependency install layer.
- One
RUNfor a related set of commands; clean up (rm -rf /var/lib/apt/lists/*, etc.) in the same layer, or the deleted files still bloat the image (they only leave the final layer, not the intermediate one that created them). - Prefer
COPYoverADDunless you specifically needADD's tar-extraction or remote-URL behavior. - Run as a non-root
USERwhere possible; the base image's default is oftenroot. - Add a
.dockerignore(.git,node_modules, build output) so the build context — and the layer cache — isn't invalidated by files the image doesn't need.
Debugging a build¶
docker build --progress=plain --no-cache . # full, uncached build log
docker run --rm -it <intermediate-layer-id> sh # shell into a layer that failed
hadolint (this repo's Dockerfile linter) catches most of the above automatically —
run it locally before pushing: