Chapter 07Lesson 05~120 minutes

Checkpoint Lab — Dockerfile Fundamentals: FROM, RUN, COPY, ADD, WORKDIR, ENV, ARG, USER, CMD, and ENTRYPOINT

The checkpoint turns the chapter into an evidence packet: a tiny non-root image is built from a digest-resolved base, its declared inputs and runtime defaults are inspected, fake-secret checks are demonstrated safely, command semantics are verified, and only chapter-owned resources are cleaned up.

Checkpoint labDigest identityNon-rootEvidence packetCleanup

Learning objectives

  • Produce a chapter-owned Dockerfile project using only synthetic inputs and a digest-resolved base.
  • Predict which instructions change filesystem layers and which change image configuration, then verify the predictions.
  • Prove non-root runtime identity, working directory, environment, entrypoint, command, and declared file contents.
  • Create a compact evidence packet linking Dockerfile/source inputs to final local image identity and container behavior.
  • Perform exact cleanup without broad prune or deletion of unrelated Docker state.
Chapter 07 evidence baseline — verified 2026-09-21. Mandatory exercises use synthetic source, local disposable images/containers, exact chapter labels/names, and no real credentials. Docker Engine 29.8.1 is the current Engine 29 patch baseline at verification time, but each lab records the learner's actual Engine/CLI/Buildx/BuildKit state. Current Docker documentation recommends # syntax=docker/dockerfile:1 for most users who want the latest stable Dockerfile 1.x frontend, documents ARG/ENV as inappropriate secret channels, and supports docker build --check for Dockerfile build checks. The labs resolve the base tag to a repository digest before building and never require a registry push, paid service, privileged mode, Docker socket mount, or broad prune.

1. Checkpoint mission and acceptance criteria

Create one small local image whose Dockerfile expresses a clear build/runtime contract. The base must be recorded by digest, the final process must run non-root, no secret may be embedded, command semantics must be intentional, the final image configuration must be inspectable, and cleanup must target only chapter-owned objects.

Pass condition
  • Five core evidence classes exist: tool/context, source/base, build/check, image config/identity, runtime/cleanup.
  • The running process reports UID 65532 and working directory /app.
  • Default ENTRYPOINT/CMD behavior and one CMD override are both demonstrated.
  • No real secret, registry credential, Docker socket, privileged mode, or broad prune is used.

2. Create the evidence workspace

mkdir -p ch07-checkpoint/evidence
cd ch07-checkpoint

date -u +%Y-%m-%dT%H:%M:%SZ | tee evidence/00-started-at.txt
docker context show | tee evidence/01-context.txt
docker version | tee evidence/02-version.txt
docker info | tee evidence/03-info.txt
docker buildx version | tee evidence/04-buildx.txt

3. Resolve the base identity

BASE_TAG='busybox:1.37.0'
docker pull "$BASE_TAG"
BASE_IMAGE=$(docker image inspect "$BASE_TAG" --format '{{index .RepoDigests 0}}')
printf 'base_tag=%s\nbase_digest_ref=%s\n' "$BASE_TAG" "$BASE_IMAGE" | tee evidence/05-base.txt

If RepoDigests is unexpectedly empty, stop and investigate the local/registry state instead of substituting a guessed digest.

4. Create declared source and hash it

cat > app.sh <<'EOF'
#!/bin/sh
set -eu
printf 'name=%s mode=%s uid=%s cwd=%s\n' \
  "${1:-academy}" "${APP_MODE:-unset}" "$(id -u)" "$PWD"
printf 'content='; cat /app/content.txt
EOF
chmod 0755 app.sh
printf 'chapter-07-checkpoint\n' > content.txt
sha256sum app.sh content.txt | tee evidence/06-source-hashes.txt

5. Author the checkpoint Dockerfile

# syntax=docker/dockerfile:1

ARG BASE_IMAGE
FROM ${BASE_IMAGE}

ARG BUILD_CHANNEL=checkpoint
ENV APP_MODE=academy
WORKDIR /app

COPY --chmod=0555 app.sh /app/app.sh
COPY --chmod=0444 content.txt /app/content.txt
RUN printf 'build-channel=%s\n' "$BUILD_CHANNEL" > /app/build-channel.txt

USER 65532:65532
ENTRYPOINT ["/app/app.sh"]
CMD ["student"]
sha256sum Dockerfile | tee evidence/07-dockerfile-hash.txt

Before building, predict: COPY and RUN will change image filesystem content; ENV/WORKDIR/USER/ENTRYPOINT/CMD will be visible in image configuration; ARG BUILD_CHANNEL itself will not be a runtime environment variable, although the RUN instruction persists its value into a file.

6. Demonstrate the secret rule without a real secret

Create a separate, intentionally broken file using only a fake value. Do not merge it into the checkpoint Dockerfile.

cat > Dockerfile.secret-demo <<'EOF'
FROM busybox:1.37.0
ARG DEMO_API_TOKEN=not-a-secret
ENV DEMO_PASSWORD=not-a-secret
CMD ["true"]
EOF

docker build --check -f Dockerfile.secret-demo . 2>&1 | tee evidence/08-secret-check-demo.txt || true

The point is to capture the check behavior and then discard the unsafe pattern. Never replace the fake strings with real credentials.

7. Validate the real Dockerfile

docker build --check \
  --build-arg BASE_IMAGE="$BASE_IMAGE" \
  --build-arg BUILD_CHANNEL=checkpoint \
  . 2>&1 | tee evidence/09-build-check.txt

If the check reports a violation, preserve the file and output, repair the Dockerfile source, hash it again, and rerun. Do not suppress checks merely to produce a green result.

8. Build and capture the result

IMAGE='devops-academy-ch07-checkpoint:local'

docker build --progress=plain \
  --build-arg BASE_IMAGE="$BASE_IMAGE" \
  --build-arg BUILD_CHANNEL=checkpoint \
  --label devops-academy.lab=ch07-checkpoint \
  -t "$IMAGE" . 2>&1 | tee evidence/10-build.txt

docker image inspect "$IMAGE" --format 'ImageID={{.Id}} RepoTags={{json .RepoTags}} RepoDigests={{json .RepoDigests}}' \
  | tee evidence/11-image-identity.txt
docker history --no-trunc "$IMAGE" | tee evidence/12-history.txt

9. Verify configuration predictions

docker image inspect "$IMAGE" \
  --format 'User={{.Config.User}} Workdir={{.Config.WorkingDir}} Entrypoint={{json .Config.Entrypoint}} Cmd={{json .Config.Cmd}} Env={{json .Config.Env}}' \
  | tee evidence/13-config.txt

Verify that the values correspond to the source Dockerfile. Do not infer runtime health from this inspect result; it proves only the image's configuration.

10. Verify default runtime behavior and effective identity

DEFAULT='devops-academy-ch07-checkpoint-default'
docker run --name "$DEFAULT" --label devops-academy.lab=ch07-checkpoint "$IMAGE" \
  | tee evidence/14-default-run.txt

docker inspect "$DEFAULT" --format 'ID={{.Id}} Image={{.Image}} User={{.Config.User}} Path={{.Path}} Args={{json .Args}} Exit={{.State.ExitCode}}' \
  | tee evidence/15-default-inspect.txt

docker run --rm --entrypoint sh "$IMAGE" -c 'id; pwd; ls -ln /app; printf "BUILD_CHANNEL=%s\n" "${BUILD_CHANNEL-unset}"; cat /app/build-channel.txt' \
  | tee evidence/16-runtime-proof.txt

The first run should use CMD student. The second proves effective identity and that BUILD_CHANNEL is not automatically a runtime environment variable even though the build persisted its value into build-channel.txt.

11. Verify an intentional CMD override

docker run --rm --label devops-academy.lab=ch07-checkpoint "$IMAGE" reviewer \
  | tee evidence/17-cmd-override.txt

The output should show name=reviewer while ENTRYPOINT stays /app/app.sh. This is the behavior the Dockerfile contract intended.

12. Evidence packet review

Your evidence directory should independently answer:

  • Which context, Engine/CLI and Buildx handled the build?
  • Which human-readable base tag and immutable repository digest were used?
  • What were the Dockerfile and source hashes?
  • What did build checks report?
  • What was the final local image ID and did a registry digest exist?
  • What are Config.User, WorkingDir, Env, Entrypoint and Cmd?
  • What effective UID and working directory were observed at runtime?
  • Did CMD override behavior match the design?
  • Did the fake secret check remain isolated from the real image?

13. Exact cleanup and final verification

docker ps -a --filter label=devops-academy.lab=ch07-checkpoint --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}'
docker rm "$DEFAULT"
docker image rm "$IMAGE"
docker ps -a --filter label=devops-academy.lab=ch07-checkpoint
date -u +%Y-%m-%dT%H:%M:%SZ | tee evidence/18-finished-at.txt

Keep the evidence directory. Do not run broad image/container/system prune commands. The pulled BusyBox base can remain because it was not uniquely created by this chapter and may be shared by other labs.

14. Operational review and Chapter 08 handoff

Chapter 07 established the Dockerfile as a source-controlled contract between build inputs and runtime defaults. You can now separate filesystem-producing instructions from image configuration, use non-root runtime identity, distinguish ARG from ENV, reason about ENTRYPOINT/CMD, and prove base/output identities instead of relying on tags alone.

Chapter 08 narrows the next major uncertainty: which files and remote sources the build is allowed to see. You will measure and minimize build contexts, use .dockerignore, reason about remote and named contexts, and tie those inputs to deterministic evidence.

Next chapter

Next: Build Contexts, .dockerignore, Remote Contexts, Named Contexts, and Deterministic Build Inputs

Move from correct Dockerfile instructions to bounded, auditable build inputs.

Knowledge check

Which evidence proves the checkpoint base content, not merely its human-readable name?

Which checkpoint field proves the image's default runtime UID?

Why is the fake secret demo in a separate Dockerfile?

If RepoDigests is empty for the locally built final image, is the build invalid?

What is the next reproducibility boundary after the Dockerfile itself?

Official references and version notes

Version and compatibility note

Version-sensitive statements were rechecked against primary documentation on 2026-09-21. Docker Engine 29.8.1 is the current Engine 29 patch release at this checkpoint. Docker currently recommends # syntax=docker/dockerfile:1 for most users when an external stable Dockerfile frontend is desired; that reference follows the latest stable 1.x frontend rather than being an immutable artifact. Executable labs therefore record the learner's actual Engine/CLI/Buildx/BuildKit state and resolve the application base image to a repository digest before building.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.