ssprkd.io
Containers & Docker
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.