Building on Ephemeral Runners
Building on Ephemeral Runners
CI machines are throwaway, so the local builder cache is gone every run. Use BuildKit through buildx and store layers in a shared backend:
docker buildx build --cache-from type=gha --cache-to type=gha,mode=max --tag app:${GITHUB_SHA::8} --file Dockerfile .
mode=max exports every intermediate layer, not just the final stage — without it, npm ci layers from earlier stages never survive between runs. Build the image even when you will not push it: a build step on pull requests catches broken Dockerfiles before merge.
Tagging and Release Builds
Tagging and Release Builds
Derive tags from git so every image maps to a commit:
SHA=$(git rev-parse --short HEAD)
docker buildx build --tag registry.example.com/app:$SHA --tag registry.example.com/app:latest --push .
latest is convenience for humans; automation should pin the SHA tag or digest. For multi-arch releases, add --platform linux/amd64,linux/arm64 with QEMU registered — the result is a manifest list, and one tag serves both architectures. Attach provenance with --provenance=true so consumers can verify what was built and from which source.
Pitfalls and Interview Angles
Pitfalls and Interview Angles
Cache misses after the first COPY. Adding COPY . . early invalidates every later layer on each commit — order the Dockerfile so dependency install runs before source copy.
Secrets in build args. --build-arg TOKEN=... is recorded in image history and visible to anyone who pulls it; use BuildKit --mount=type=secret instead.
Built is not tested. Run unit tests in the pipeline before the build, then smoke-test the built image (start the container, hit /healthz) — the image is the artifact that ships.
Interview: explain the buildx cache backends you would choose (gha for GitHub, registry for portability), where mode=max matters, and how you would prove a rebuild reused cache.
Quiz
1. Why use --cache-to type=gha,mode=max in GitHub Actions?
2. Where should the dependency install step sit relative to COPY . .?
3. Why not pass secrets as --build-arg?
Flashcards
Question
Ephemeral runner cache
Click to reveal answer
Answer
Local builder cache is lost per job — export/import via type=gha or type=registry.
Question
cache-to mode=max
Click to reveal answer
Answer
Exports every intermediate layer, not only final-stage layers.
Question
Layer ordering rule
Click to reveal answer
Answer
Least frequently changing instructions first: package manifests → install → source copy.
Question
--mount=type=secret
Click to reveal answer
Answer
BuildKit secret exposed only to the RUN step that mounts it; absent from layers and history.
Revision Notes
Key Takeaways
- 1. CI builds need a remote cache backend or they rebuild everything each run
- 2. Order the Dockerfile by change frequency, not by reading convenience
- 3. Tag from git SHA and verify the built image, not just the repo
Interview Tips
- • Walk your Dockerfile line by line and defend each layer boundary against cache invalidation
- • Explain the difference between image cache backends and where secrets must live
Cheat Sheet
buildx + --cache-from/to type=gha (mode=max); COPY package* then npm ci then COPY .; SHA tags; --platform for manifest lists; secrets via type=secret mounts.
Continue Learning
Before You Start
- No prerequisites listed.