Project challenges / verified progress
Beacon: package it so anyone can run it

The engineering notebook

Build the first image

How does a Dockerfile become a runnable image?

Loading statusStage 2 of 9

  • Workspace not ready
  • Agent not ready
Focus25:00
A small focus ritual

0 focus sessions completed. Every fourth session offers a longer break. Start each phase when you are ready.

Study time never unlocks verified lesson progress.

Loading...

Loading verified progress...

Loading GitHub account...
Phase 1 — From process to image

Step 01 of 06

Learn the concept

The first Beacon image will be embarrassingly large: roughly 900MB for a service binary under 20MB. That is not failure. It is the visible cost of copying a compiler, package cache, module cache, source tree, and runtime into one filesystem because you have not separated build-time from run-time yet.

TEXT TO RUNNING PROCESS01Contextfiles sent to builder.dockerignore02Dockerfileordered instructionslayer plan03Imageread-only layerstagged04Containerprocess plus configruns CMD
A Dockerfile is not a shell script. It is a recipe for layered filesystem snapshots. The container appears only after the image exists and the runtime starts the configured command inside the image root filesystem.
Step 01

The ideas this is made of

The build context is a tarball with consequences

When you run docker build ., the dot is packaged and sent to the builder. If .git, screenshots, databases, or node_modules are inside and not ignored, they are eligible for COPY and can slow or leak the build. .dockerignore is not decoration. It is the first supply-chain boundary in the course.

Layers cache instructions, not intentions

Docker reuses a layer when the instruction and the files it reads are unchanged. Put COPY . . before go mod download and every source edit invalidates dependency download. Copy go.mod and go.sum first, download modules, then copy source, and the cache behaves like you meant it to. Computers remain literal-minded pests.

CMD is the default process, not an install step

RUN executes during image build and commits the filesystem result. CMD records what starts when a container is run. For Beacon, RUN go build creates /out/beacond; CMD ["/app/beacond"] starts the service. If you put server startup in RUN, the build hangs or bakes in nothing useful.

A large first image is a measurement

The naive golang:1.22 image includes Debian, the Go compiler, toolchain caches, and build tools. That is why image size lands near 900MB. Do not hand-wave it away. Measure it with docker image ls, then stage three has a concrete number to beat.

A cache-friendly tiny Dockerfile
FROM alpine:3.20
WORKDIR /app
COPY message.txt .
CMD ["cat", "/app/message.txt"]

Only one file is copied and the command is JSON-array form, so no shell is needed to start it. Beacon uses the same shape, then adds a Go build step.

Image and container, separated

ThingWhat it isBeacon example

Image

Immutable layers and metadata

beacon:naive

Container

A running process from an image

One beacond process

Layer

One filesystem change

RUN go build output

Context

Files sent to builder

Repo minus .dockerignore

What these are called on the job

  • Tag — A mutable name for an image reference, such as beacon:naive. It is convenient, not a guarantee.

  • Layer cache — Previously built instruction results reused when inputs have not changed.

  • Build context — The directory contents made available to COPY and ADD during the build.