Lesson 2 · Containers & Docker

Images, layers & Dockerfiles

The read-only template a container runs from — and why layer order decides your build speed.

Your win: read a Dockerfile, explain what an image layer is and how the build cache uses it, and describe the multi-stage build this repo actually ships.

Image vs container (nail this first)

An image is a read-only template: a stack of filesystem layers plus metadata (which process to run, env vars, exposed ports). A container is a running instance of that image, with a thin writable layer on top.1 One image → many containers. You build an image once and run it many times — exactly why all ~25 services share one new-backend:locally image.

Layers: the key idea

An image is built up in layers, and each layer is an immutable set of filesystem changes.2 Roughly, each instruction in a Dockerfile creates one layer:

┌─────────────────────────────┐ ← COPY ./build/server /server (your code — changes often) ├─────────────────────────────┤ ← RUN apt-get install ... (dependencies) ├─────────────────────────────┤ ← FROM debian:12.11 (base image) └─────────────────────────────┘ stacked, read-only; a container adds a thin writable layer on top

Layers are cached and shared. Build the image again and Docker reuses every layer whose inputs haven't changed. But there's a catch that runs the whole show:

The rule that governs build speed When a layer changes, every layer below it must be rebuilt too — the cache is invalidated downstream.2 So order instructions from least-frequently-changing (base, dependencies) at the top to most-frequently-changing (your source code) at the bottom. Put COPY source too early and every code edit re-installs all your dependencies.

The Dockerfile

A Dockerfile is the recipe. The instructions you'll see 90% of the time:

InstructionDoes
FROMstart from a base image (the first layer)
RUNexecute a command at build time → a new layer
COPYcopy host files into the image
ENVset an environment variable
ENTRYPOINTthe process that runs when the container starts
CMDdefault arguments (easily overridden)

Build with docker build -t name:tag .; the result lands in a registry (this repo uses asia.gcr.io/...) tagged name:tag.

Multi-stage builds — what this repo does

A single Dockerfile can have several FROM stages, where a later stage copies artifacts out of an earlier one with COPY --from=. You build with a fat toolchain but ship only a lean runtime.3

Anchor — local/Dockerfile.development Read it top to bottom and you'll see the pattern exactly: Two things worth clocking: (1) one binary, many services — the image bakes in a single build/server; which service runs is chosen by command: at run time, not at build time. (2) The base is debian:12.11, not distroless — because dev/CI need bash, curl, netcat, cron (line 44) for debugging and tests.
Bonus: the no-op stage Line 28 defines a stage with no /server and ENTRYPOINT ["modd"]. It exists so the stack can deploy an empty placeholder fast, then hot-swap the freshly built binary in — the local reload trick you'll meet in Lesson 3 and Part 2.
Read this next

Docker — “Understanding the image layers” + multi-stage builds

The layer model, the immutability/cache rules, and how multi-stage builds keep runtime images small. Short and authoritative.

docs.docker.com — Understanding the image layers
docs.docker.com — Multi-stage builds

Check yourself (from memory)

Q1. An image differs from a container in that an image is…

Image = read-only stacked layers + metadata; a container is a running instance with a thin writable layer on top.

Q2. You edit source copied high up in a Dockerfile. Result?

A changed layer invalidates the cache for everything below — why code goes near the bottom and deps near the top.

Q3. A multi-stage build exists mainly to…

Build with the full toolchain, then COPY --from= only the artifacts into a small final image.
Explain image layers + the cache rule, and what a multi-stage build buys you.
recall, then click to reveal
An IMAGE is a read-only stack of immutable LAYERS (+ metadata); roughly one Dockerfile instruction = one layer. Layers are CACHED and SHARED — a rebuild reuses unchanged layers. CACHE RULE: changing a layer invalidates every layer BELOW it, so order least-changing (base, deps) → most-changing (your code). MULTI-STAGE: multiple FROM stages; a final stage COPY --from=s artifacts out of build stages, so you build with a fat toolchain but ship a lean runtime. Repo: local/Dockerfile.development — download-* stages feed the developer stage, which does COPY ./build/server /server (one binary, many services) on a debian:12.11 base.
Want to walk the actual Dockerfile.development stage graph, or see how .dockerignore and layer order cut build time? Ask me.

1. Docker — What is an image?

2. Docker — Understanding the image layers.

3. Docker — Multi-stage builds.