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 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), remotehttps://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.
-
candidatewas 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?
Because the exact registry result still needs proof. Compare the manifest digest for candidate with the previously captured 1.0.0 digest.
What does the digest-pinned pull prove?
That the group can serve the exact manifest identified by that digest, independent of a mutable tag name.
Why does the proxy public pull belong in a separate evidence section from the internal hosted push?
They exercise different repository responsibilities and state changes: hosted is your authoritative publication; proxy is cached upstream content.
If removing the auth file does not make a pull fail, what should you conclude?
The repository may permit anonymous read or the client may be using another credential source. Inspect the actual auth policy/client state rather than claiming authentication is broken.
Why can a repository delete succeed without immediately recovering all filesystem bytes?
Repository content deletion and blob reclamation/compaction can be separate lifecycle stages. Use current supported Nexus cleanup/reclamation semantics.
What new production invariant does Chapter 08 add?
Record and deploy exact container manifest digests through a governed Nexus route, with explicit repository ownership, authentication, routing, proxy, and layer/blob evidence.
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.
Official references and version notes
- Nexus Repository Download and 3.95.0 release notes — current downloadable self-hosted baseline for this chapter.
- Sonatype: Docker Registry — Docker registry paths, routing methods, API support, and self-hosted connector guidance.
- Sonatype: Docker Authentication — Docker Bearer Token Realm, login behavior, and anonymous-access prerequisites.
- Sonatype: OCI Repositories, Create an OCI Repository, and Configure OCI Repository.
- Sonatype: OCI CLI Usage — Docker/Podman/OCI-compatible client examples.
- Sonatype: Proxy Repository for Docker and reverse-proxy strategies.
- Docker: docker image pull — tag versus digest pulls and layer reuse.
- OCI Image Manifest Specification and OCI Image Configuration.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.