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

The engineering notebook

Shrink it honestly

How do you remove the compiler without removing the program?

Loading statusStage 3 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

A production Beacon image does not need go test, git, shell history, or a compiler. It needs one Linux executable and CA certificates for outbound TLS checks. Multi-stage builds let you keep the heavy tools in one stage and copy only the runtime artifacts into another.

WHERE SIZE WENTmegabytesGo basecompiler and Debian~880Sourcerepo and module cache~30Binarybeacond only~14Runtimecerts and metadata~1
The big win is not compression. It is leaving the build stage behind. A static Go binary plus CA certificates can be smaller than the package index in a general-purpose Linux image.
Step 01

The ideas this is made of

Stages are named filesystems, not modes

FROM golang:1.22-bookworm AS build creates one filesystem with the compiler. FROM gcr.io/distroless/static-debian12:nonroot creates another. COPY --from=build /out/beacond /beacond copies one file across the boundary. Nothing else crosses unless you name it. That is the security and size story in one line.

Static linking removes the libc dependency

Go can produce a binary that does not need glibc or musl at runtime when cgo is disabled. CGO_ENABLED=0 go build is the common switch. If a dependency uses cgo, the build may fail or quietly need dynamic libraries. That is not a Docker problem; it is a linking fact you must know before choosing scratch.

Scratch is a dare, not a default

scratch contains nothing. No /bin/sh, no /etc/passwd, no CA bundle. Beacon checks HTTPS endpoints, so TLS verification needs trusted roots. Forget them and outbound checks fail with x509: certificate signed by unknown authority. The 0-byte base image is less charming at 03:00.

Distroless keeps runtime bytes, not debugging comfort

Distroless images contain the minimal runtime files Google maintains, not a shell or package manager. static-debian12:nonroot includes CA certificates and a numeric non-root user. It is larger than scratch and much smaller than Debian. You trade docker exec sh for a smaller attack surface and a runtime that still validates TLS.

Copying only the artifact
FROM golang:1.22-bookworm AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /out/hello ./cmd/hello

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/hello /hello
CMD ["/hello"]

The second stage cannot see /src, the module cache, or the compiler. It receives only /out/hello. Beacon uses the same move with /beacond.

Runtime base choices

BaseWhat you getMain cost

scratch

Nothing except copied files

Must add certs, passwd, debugging tools

alpine

Shell, musl, apk

More packages and musl edge cases

Distroless

Certs, users, minimal libs

No shell for debugging

Debian slim

Familiar OS tools

Much larger attack surface

What these are called on the job

  • Build stage — The image stage that has compilers and source code.

  • Runtime stage — The final image stage shipped and run in production.

  • Static binary — An executable that does not need shared runtime libraries from the image.