Lesson 1 of 7
Images & layers
See how a union filesystem stacks read-only layers into an image, so you finally get why the build cache works and how a container's writable layer differs.
① Watch
Speed
📖 Read this walkthrough — every command, and why
Images & Layers — a Dockerfile builds a stack of read-only layers
Mental model: an image is a stack of read-only layers. Each Dockerfile instruction (ADD, RUN, CMD...) adds exactly one layer on top of the one before it. Docker merges the stack into a single union filesystem the container sees as one tree, but on disk each layer is separate and shareable — ten images off the same base share that one base layer instead of copying it. An image is named two ways: a tag (a mutable pointer you can move) and a digest (an immutable sha256 hash of the actual bytes).
1 - Build the image
docker build -t sprkd-demo:v1 .
#7 writing image sha256:e843b45e9c47a46321f951fbaab7290cf1cab6dd1f7a45692d0e1faf6e08d8cc done
#7 naming to docker.io/library/sprkd-demo:v1 done
#7 DONE 0.0s
Docker reads the Dockerfile top to bottom, runs each instruction as its own step, then writes the
final image (its content sha256) and names it. The short image id here is e843b45e9c47 (the first 12
hex chars of that sha256).
2 - Inspect the layers (each instruction = a layer)
docker history sprkd-demo:v1
IMAGE CREATED CREATED BY SIZE COMMENT
e843b45e9c47 1 second ago CMD ["cat" "/a.txt" "/b.txt"] 0B buildkit.dockerfile.v0
<missing> 1 second ago RUN /bin/sh -c echo "layer two" > /b.txt ... 10B buildkit.dockerfile.v0
<missing> 1 second ago RUN /bin/sh -c echo "layer one" > /a.txt ... 10B buildkit.dockerfile.v0
<missing> 2 months ago CMD ["/bin/sh"] 0B buildkit.dockerfile.v0
<missing> 2 months ago ADD alpine-minirootfs-3.20.10-aarch64.tar... 8.82MB buildkit.dockerfile.v0
Read bottom to top — that is your build replayed. The alpine ADD (base root filesystem, 8.82MB) is
at the bottom; two RUN layers each write a file (10B each); the CMD on top carries the run command
and adds 0B (metadata only). Only the top layer has a real IMAGE id — the lower layers show <missing>
because their content ids come from the base image and aren't tracked separately here.
3 - List images
docker images
# sprkd-demo:v1 -> id=e843b45e9c47 size=8.82MB
The size (8.82MB) is dominated by the base alpine layer; the two RUN layers add only 10B each and the
CMD adds nothing, so the whole image is essentially the base plus a few bytes.
4 - Tag vs digest
The build produced BOTH a name and a content hash:
tag: sprkd-demo:v1 mutable pointer -> can be moved to point at other bytes later
digest: sha256:e843b45e... immutable hash -> change any byte and the hash changes
The tag is a friendly label. The digest is computed from the bytes themselves, so it always names the
exact same image content.
Flag / part notes
docker build -t name:tag . -t sets name:tag; the final "." is the build context (dir with the Dockerfile).
docker history <image> shows the layer stack, newest first; CREATED BY = the instruction that made it.
SIZE column per-layer bytes added; 0B means metadata-only (CMD, ENV, LABEL...).
<missing> in IMAGE normal for base/intermediate layers — not an error.
Gotchas
- Tags are not immutable. The SAME tag (sprkd-demo:v1) can be repushed later to point at completely
different bytes, so "it worked last month" can break with no visible change. Pin by digest
(name@sha256:...) when you need the exact image you tested.
- Layer order matters for size and cache: every instruction stacks on the ones below it. Read
docker history bottom to top to reason about what each step actually added.
- 0B layers are real layers, just metadata — CMD still counts as a layer even though it adds nothing.
Verify: docker history sprkd-demo:v1
Confirm 5 rows: alpine ADD (8.82MB) at the bottom, two 10B RUN layers, and CMD (0B) on top —
the image id e843b45e9c47 matches the sha256 printed by docker build.