Chapter 08Lesson 05205–265 min

Checkpoint Lab — Docker and OCI Repositories, Connectors, Registry Paths, Authentication, Layers, and Image Metadata

Push and pull a tiny image through a disposable Nexus OCI topology, capture tag and digest identities, retag without rebuilding, verify layer reuse and authentication behavior, prove a digest-pinned pull, and clean up only disposable state.

Checkpoint labExact-image identityLayer reuseDigest pinningSafe cleanup

Checkpoint objectives

  • Build a disposable native OCI hosted/proxy/group topology on loopback and document its exact version/edition/routing assumptions.
  • Push one tiny synthetic image, record its manifest digest, and consume it through the group from isolated client state.
  • Retag the exact local image without rebuilding and prove the manifest digest/layers are reused.
  • Exercise one authentication/routing failure and repair it from preserved evidence.
  • Produce an evidence packet and remove repository/client state through supported controls only.

Checkpoint safety boundary. Use only a disposable self-hosted Nexus Community instance and synthetic learner-example image names. The lab never touches a production registry, public namespace you do not own, Nexus database rows, or blob-store files. Plain HTTP is accepted only on loopback with a command-scoped Podman TLS override; production guidance is HTTPS with trusted certificates.

1. Pin and record assumptions

Item Checkpoint assumption
Nexus Self-hosted Community Edition 3.95.0; current downloadable release as checked 2026-08-26.
Runtime/database Java 21 on Nexus; small disposable local instance may use H2. Production database/storage design remains Chapter 05 guidance.
Repository format Native OCI hosted/proxy/group, available since 3.94.0 in Community and Pro.
Routing Path-based routing on 127.0.0.1:8081.
Client Current Podman preferred for command-scoped --tls-verify=false on loopback HTTP; Docker equivalent requires trusted HTTPS or explicit daemon insecure-registry configuration.
Identity Disposable least-privilege Nexus user; OCI Bearer Token Realm active; isolated auth file.
Public fixture Docker Hub library/alpine:3.20 only for harmless proxy-read proof.

2. Preflight and evidence directory

export NX_URL="http://127.0.0.1:8081"
export NX_REGISTRY="127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch08-checkpoint"
rm -rf "$LAB"
mkdir -p "$LAB/evidence" "$LAB/build" "$LAB/auth"
chmod 700 "$LAB" "$LAB/auth"

curl -fsS "$NX_URL/service/rest/v1/status" | tee "$LAB/evidence/status.txt"
podman version | tee "$LAB/evidence/podman-version.txt"
printf 'Record Nexus UI version/edition screenshot separately.
'

3. Predict before changing state

Write predictions before creating or pushing anything. The checkpoint is not complete if the learner merely records what happened after the fact.

Action Prediction to write first
Push 1.0.0 to hosted A hosted image/component record plus manifest/config/layer blobs will appear; no upstream registry is mutated.
Pull internal image through group Group will route to hosted first; group is a read view, not a second publication.
Push candidate for the same local image No rebuild; same manifest digest expected; layer/config blobs should already exist.
Pull library/alpine:3.20 through group Hosted miss, proxy fetch/cache from Docker Hub; proxy state grows.
Remove client auth and attempt authenticated internal pull Token acquisition/read should fail; Nexus content remains unchanged.

4. Create the disposable topology

In Nexus UI create:

  • academy-ch08cp-hosted — oci (hosted), default blob store, Disable redeploy, path-based routing.
  • academy-ch08cp-proxy — oci (proxy), remote https://registry-1.docker.io, default blob store, path-based routing.
  • academy-ch08cp-group — oci (group), members hosted first then proxy, default blob store, path-based routing.

Enable the OCI Bearer Token Realm if it was not already active. Create a disposable user that can add/read the hosted repository and read the group/proxy. Preserve a screenshot or redacted settings note showing repository type/format/member order.

5. Login with isolated client state

export REGISTRY_AUTH_FILE="$LAB/auth/auth.json"
read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
printf '%s' "$NX_PASS" | podman login   --authfile "$REGISTRY_AUTH_FILE"   --tls-verify=false   --username "$NX_USER"   --password-stdin   "$NX_REGISTRY"   | tee "$LAB/evidence/login.txt"
unset NX_PASS
chmod 600 "$REGISTRY_AUTH_FILE"

6. Build the tiny image once

cd "$LAB/build"
printf 'checkpoint-image-v1
' > marker.txt
cat > Dockerfile <<'DOCKERFILE'
FROM scratch
COPY marker.txt /marker.txt
LABEL org.opencontainers.image.title="academy-ch08-checkpoint"
LABEL org.opencontainers.image.version="1.0.0"
DOCKERFILE

podman build --format oci   -t localhost/learner-example/ch08-checkpoint:1.0.0 .   | tee "$LAB/evidence/build.txt"
podman image inspect localhost/learner-example/ch08-checkpoint:1.0.0   > "$LAB/evidence/local-image.json"

Do not build again later when creating candidate. Retagging the already-built local image is the experiment.

7. Push 1.0.0 and record exact identity

HOSTED_V1="$NX_REGISTRY/academy-ch08cp-hosted/learner-example/ch08-checkpoint:1.0.0"
podman tag localhost/learner-example/ch08-checkpoint:1.0.0 "$HOSTED_V1"
podman push   --authfile "$REGISTRY_AUTH_FILE"   --tls-verify=false   --digestfile "$LAB/evidence/v1-digest.txt"   "$HOSTED_V1"   | tee "$LAB/evidence/v1-push.txt"

printf 'v1 manifest digest: '
cat "$LAB/evidence/v1-digest.txt"

Capture Nexus Browse/Search evidence showing the hosted repository owns the image content. Record the tag and digest separately.

8. Consume through group and then by digest

GROUP_V1="$NX_REGISTRY/academy-ch08cp-group/learner-example/ch08-checkpoint:1.0.0"
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$GROUP_V1"   | tee "$LAB/evidence/group-tag-pull.txt"

DIGEST="$(cat "$LAB/evidence/v1-digest.txt")"
GROUP_DIGEST="$NX_REGISTRY/academy-ch08cp-group/learner-example/ch08-checkpoint@$DIGEST"
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$GROUP_DIGEST"   | tee "$LAB/evidence/group-digest-pull.txt"

The digest-pinned request is the exact-image proof. It should resolve even though the human-friendly tag is not part of the reference.

9. Retag without rebuilding and verify reuse

HOSTED_CANDIDATE="$NX_REGISTRY/academy-ch08cp-hosted/learner-example/ch08-checkpoint:candidate"
podman tag "$HOSTED_V1" "$HOSTED_CANDIDATE"
podman push   --authfile "$REGISTRY_AUTH_FILE"   --tls-verify=false   --digestfile "$LAB/evidence/candidate-digest.txt"   "$HOSTED_CANDIDATE"   | tee "$LAB/evidence/candidate-push.txt"

V1_DIGEST="$(cat "$LAB/evidence/v1-digest.txt")"
CANDIDATE_DIGEST="$(cat "$LAB/evidence/candidate-digest.txt")"
test "$V1_DIGEST" = "$CANDIDATE_DIGEST"
printf 'same manifest digest: %s
' "$V1_DIGEST"   | tee "$LAB/evidence/retag-identity.txt"

Inspect the push output. Wording differs by client/version, but existing blobs should be recognized rather than re-uploaded as new byte content. The digest equality is the stronger identity proof.

10. Exercise the proxy path

PUBLIC_REF="$NX_REGISTRY/academy-ch08cp-group/library/alpine:3.20"
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$PUBLIC_REF"   | tee "$LAB/evidence/public-pull.txt"

Verify the public image appears in the proxy member, not hosted. This proves group member routing: hosted is checked first, then the proxy retrieves the upstream miss.

11. Intentionally break authentication, preserve the cause, then repair

Move the auth file out of the way and try an internal pull that your instance requires authentication for. Preserve the error. Do not weaken Nexus permissions or enable anonymous access just to make the error disappear.

mv "$REGISTRY_AUTH_FILE" "$REGISTRY_AUTH_FILE.saved"
set +e
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$GROUP_V1"   > "$LAB/evidence/unauthenticated-pull.txt" 2>&1
RC=$?
set -e
printf 'expected failure rc=%s
' "$RC" | tee -a "$LAB/evidence/unauthenticated-pull.txt"

mv "$REGISTRY_AUTH_FILE.saved" "$REGISTRY_AUTH_FILE"
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$GROUP_V1"   | tee "$LAB/evidence/repaired-pull.txt"

If your disposable repository is intentionally configured for anonymous read, use a different controlled failure instead: remove the user's hosted add privilege and attempt a new synthetic tag push, then restore it. The principle is to create one known authorization failure without changing production state.

12. Required evidence packet

  • Nexus version/edition and container client version.
  • Repository topology: format/type/routing mode/member order and proxy remote.
  • Realm state and a redacted privilege matrix for the lab user.
  • Single build output and local image inspection.
  • Hosted 1.0.0 push output plus v1-digest.txt.
  • Group tag pull and digest-pinned pull outputs.
  • Candidate push output and equality proof showing the same manifest digest.
  • Nexus Browse/Search or safe API evidence identifying hosted versus proxy ownership.
  • Public proxy pull evidence.
  • Original intentional auth/routing error plus repaired request.
  • Cleanup checklist proving no direct blob/database deletion.

13. Cleanup and rollback

First remove the three disposable repositories through Nexus UI/API in dependency-safe order: group, proxy, hosted. Remove the disposable user. Restore the previous realm list if you enabled OCI Bearer Token Realm only for this checkpoint. Do not manually delete Nexus blob files; repository deletion/cleanup/reclamation are distinct supported operations.

# Local images only; these commands do not delete Nexus repository content.
podman image rm   "$NX_REGISTRY/academy-ch08cp-group/library/alpine:3.20"   "$NX_REGISTRY/academy-ch08cp-group/learner-example/ch08-checkpoint:1.0.0"   "$NX_REGISTRY/academy-ch08cp-hosted/learner-example/ch08-checkpoint:candidate"   "$NX_REGISTRY/academy-ch08cp-hosted/learner-example/ch08-checkpoint:1.0.0"   localhost/learner-example/ch08-checkpoint:1.0.0   2>/dev/null || true

rm -f "$REGISTRY_AUTH_FILE"
unset REGISTRY_AUTH_FILE NX_USER
printf 'Preserve evidence externally if required, then remove %s
' "$LAB"

14. Verification checklist

  • Exactly one image build occurred.
  • 1.0.0 was pushed only to hosted and read through group.
  • The manifest digest was captured at publication and used for a digest-pinned pull.
  • candidate was created by retagging, not rebuilding, and its digest matched 1.0.0.
  • The public image populated the proxy path, not hosted.
  • One controlled authentication/routing failure was preserved and repaired without broadening security.
  • No real credential appears in the evidence packet, shell command arguments, Dockerfile, or repository.
  • No Nexus database row or blob-store file was directly edited/deleted.

Knowledge check

Why is “I pushed candidate without rebuilding” not enough by itself?

What does the digest-pinned pull prove?

Why does the proxy public pull belong in a separate evidence section from the internal hosted push?

If removing the auth file does not make a pull fail, what should you conclude?

Why can a repository delete succeed without immediately recovering all filesystem bytes?

What new production invariant does Chapter 08 add?

15. What Chapter 08 adds to the operating model

You can now reason about a container registry as a content-addressed graph rather than a pile of tags. A production design can separate authoritative hosted content from proxied upstreams, expose a deliberate group route, authenticate through the correct bearer-token realm, pin deployments to manifest digests, interpret layer reuse correctly, and troubleshoot path/TLS/auth/cache failures without weakening controls or editing storage internals.

Chapter 09 moves to Python package indexes. PyPI and Conda have different metadata/index/client semantics, so the next chapter will reuse the same evidence-first discipline without pretending Python packages behave like container manifests and layers.

Next chapter

PyPI, Conda, and Python index behavior

Carry forward repository topology, client isolation, exact artifact evidence, and safe proxy diagnostics into Python package index protocols and metadata.

Official references and version notes

Version-sensitive statements were rechecked against Sonatype, Docker, and OCI primary documentation on 2026-08-26. The mandatory lab assumes self-hosted Nexus Repository Community Edition 3.95.0, Java 21 on the Nexus side, native OCI repositories introduced in Nexus 3.94.0, and a current Docker-compatible client. Record docker version or podman version locally; client behavior evolves independently of Nexus. Re-check the live release and format documentation before executing the lab.

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.