Docker and OCI Repositories, Connectors, Registry Paths, Authentication, Layers, and Image Metadata: Guided Hands-On Workflow and Core Operations
Build a disposable OCI hosted/proxy/group topology, authenticate with isolated client state, push a tiny synthetic image only to hosted storage, pull through a group, inspect manifests/digests, and observe proxy and layer reuse safely.
Learning objectives
- Create a disposable Community-compatible OCI hosted/proxy/group topology using current Nexus UI semantics.
- Keep container-client credentials in a disposable auth/config location and avoid secrets in command arguments.
- Build and push a tiny synthetic image only to hosted storage, then consume it through the group.
- Proxy a harmless public image and distinguish first-fetch proxy state from client-local cache.
- Capture manifest digest, image metadata, repository topology, and layer-reuse evidence.
Lab choice. The mandatory path uses
Podman when the Nexus endpoint is plain loopback
HTTP because Podman supports per-command
--tls-verify=false and an explicit auth file. Docker
Engine can perform the same registry operations, but plain HTTP
requires daemon-level
insecure-registries configuration; that is shown as an
optional, explicitly rolled-back alternative rather than silently
changing a learner's global Docker daemon.
1. Preflight and disposable names
Use only a disposable local Nexus instance. Do not run this workflow
against a production registry or organization namespace. Record the
exact Nexus and client versions before changes. The lab uses three
native OCI repositories: academy-ch08-hosted,
academy-ch08-proxy, and
academy-ch08-group. All names are lowercase because
current OCI repository names must be lowercase.
export NX_URL="http://127.0.0.1:8081"
export NX_REGISTRY="127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch08"
rm -rf "$LAB"
mkdir -p "$LAB/evidence" "$LAB/build" "$LAB/podman"
chmod 700 "$LAB" "$LAB/podman"
curl -fsS "$NX_URL/service/rest/v1/status" | tee "$LAB/evidence/status.txt"
podman version | tee "$LAB/evidence/podman-version.txt"
2. Enable the OCI bearer-token realm only if needed
For self-hosted native OCI repositories, Sonatype documents the OCI Bearer Token Realm under Administration/Settings → Security → Realms. Preserve the current active realm list before changing it. If the realm is already active, make no change. If you enable it only for this lab, record that fact so cleanup restores the previous realm state.
Security-sensitive: changing realms affects authentication behavior instance-wide. Do this only on the disposable lab instance. Do not disable unrelated realms to “make login work.”
3. Create the OCI repositories in current UI
In Settings → Repository → Repositories, create the following recipes. Using the UI here is deliberate: the exact REST schema for newly added format fields is version-sensitive and the instance Swagger is the authoritative API contract. Chapter 20 will automate repository provisioning after introducing idempotence and schema discovery.
| Repository | Recipe | Configuration |
|---|---|---|
academy-ch08-hosted |
oci (hosted) |
Blob store default; online;
Disable redeploy to enforce tag
immutability for the lab; enable path-based routing if the
UI exposes the routing choice.
|
academy-ch08-proxy |
oci (proxy) |
Remote storage https://registry-1.docker.io;
blob store default; path-based routing.
|
academy-ch08-group |
oci (group) |
Members in this exact order: hosted first, proxy second;
blob store default; path-based routing.
|
The hosted-first order ensures an internal collision wins over the public proxy. The group is a read aggregation endpoint. Pushes in the mandatory Community workflow go directly to hosted.
4. Verify topology before pushing
Use Nexus UI to confirm type/format/member order. If your lab account has browse permission for the Repositories API, capture it read-only. Do not put an admin password in a curl command. A permission-restricted netrc file is acceptable for this disposable local proof.
read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
NX_AUTH_FILE="$(mktemp)"
chmod 600 "$NX_AUTH_FILE"
printf 'machine 127.0.0.1 login %s password %s
' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS
curl --fail --silent --show-error --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/repositories" | tee "$LAB/evidence/repositories-before.json"
rm -f "$NX_AUTH_FILE"
unset NX_AUTH_FILE
5. Authenticate without creating a persistent global credential
Create a dedicated disposable Nexus user with only the minimum privileges needed to read the group/proxy and add/read the hosted lab repository. Use a real lab-only password typed interactively; do not copy it into this course, source control, screenshots, or evidence.
export REGISTRY_AUTH_FILE="$LAB/podman/auth.json"
read -r -p 'Disposable Nexus registry username: ' NX_USER
read -r -s -p 'Disposable Nexus registry password: ' NX_PASS; echo
printf '%s' "$NX_PASS" | podman login --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false --username "$NX_USER" --password-stdin "$NX_REGISTRY"
unset NX_PASS
chmod 600 "$REGISTRY_AUTH_FILE"
The password never appears in the process argument list. The auth file remains inside the disposable lab directory. The subsequent bearer token exchanged with Nexus is scoped by repository privileges.
6. Build a tiny synthetic image without pulling a base image
A FROM scratch image avoids introducing an external
base dependency into the hosted-push exercise. It contains one
harmless marker file and no executable runtime. This keeps the
evidence focused on registry mechanics.
FROM scratch
COPY marker.txt /marker.txt
LABEL org.opencontainers.image.title="academy-ch08"
LABEL org.opencontainers.image.version="1.0.0"
cd "$LAB/build"
printf 'DevOps Academy Nexus Chapter 08
' > marker.txt
cat > Dockerfile <<'DOCKERFILE'
FROM scratch
COPY marker.txt /marker.txt
LABEL org.opencontainers.image.title="academy-ch08"
LABEL org.opencontainers.image.version="1.0.0"
DOCKERFILE
podman build --format oci -t localhost/learner-example/ch08-app:1.0.0 . | tee "$LAB/evidence/build.txt"
podman image inspect localhost/learner-example/ch08-app:1.0.0 > "$LAB/evidence/local-image-before-push.json"
7. Push only to hosted and capture the registry digest
With path-based routing, the repository name is part of the image
reference. The hosted reference is
127.0.0.1:8081/academy-ch08-hosted/learner-example/ch08-app:1.0.0. The push output should show blob checks and uploads followed by a
manifest digest. Save that digest as release evidence.
HOSTED_REF="$NX_REGISTRY/academy-ch08-hosted/learner-example/ch08-app:1.0.0"
podman tag localhost/learner-example/ch08-app:1.0.0 "$HOSTED_REF"
podman push --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false --digestfile "$LAB/evidence/hosted-manifest-digest.txt" "$HOSTED_REF" | tee "$LAB/evidence/hosted-push.txt"
printf 'registry manifest digest: '
cat "$LAB/evidence/hosted-manifest-digest.txt"
Do not substitute the local image ID for the remote manifest digest. They are related content-addressed identifiers but represent different serialized objects.
8. Pull the internal image through the group
The group request should resolve to the hosted first member because that image path exists internally. Pull it using the same tag, then compare the resolved digest with the hosted push evidence.
GROUP_REF="$NX_REGISTRY/academy-ch08-group/learner-example/ch08-app:1.0.0"
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$GROUP_REF" | tee "$LAB/evidence/internal-group-pull.txt"
podman image inspect "$GROUP_REF" > "$LAB/evidence/internal-group-image.json"
In Nexus Browse/Search, confirm that the authoritative image assets are owned by the hosted repository. The group is a routing view, not a second publication target.
9. Proxy a harmless public image through the same group
Now request library/alpine:3.20 through the group. The
hosted member should miss; the proxy member retrieves from Docker
Hub and caches the content. This is a controlled public read, not a
publication.
PUBLIC_REF="$NX_REGISTRY/academy-ch08-group/library/alpine:3.20"
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$PUBLIC_REF" | tee "$LAB/evidence/public-first-pull.txt"
podman image inspect "$PUBLIC_REF" > "$LAB/evidence/public-image.json"
Inspect the proxy repository in Nexus after the pull. You should now see cached manifest/config/layer content associated with the public image. The upstream remains the authoritative public origin; Nexus holds cached copies.
10. Separate client layer cache from Nexus proxy cache
If you immediately pull again, the client may satisfy most content
from its own local store, which proves little about Nexus. Remove
only the local PUBLIC_REF image, then pull again. If
shared layers remain in the client because other images reference
them, some transfer may still be avoided locally. Therefore pair
client output with Nexus request logs/proxy repository evidence
rather than interpreting one “already exists” line as a complete
cache proof.
podman image rm "$PUBLIC_REF" || true
podman pull --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false "$PUBLIC_REF" | tee "$LAB/evidence/public-second-pull.txt"
11. Retag the same image and observe layer reuse
Create a second tag for the exact same local image and push it to hosted. Because the content graph is unchanged, the registry should not need to upload the same layer/config blobs again; only reference/manifest work should be needed. Client output wording varies, so prove reuse with digest comparison as well.
CANDIDATE_REF="$NX_REGISTRY/academy-ch08-hosted/learner-example/ch08-app:candidate"
podman tag "$HOSTED_REF" "$CANDIDATE_REF"
podman push --authfile "$REGISTRY_AUTH_FILE" --tls-verify=false --digestfile "$LAB/evidence/candidate-manifest-digest.txt" "$CANDIDATE_REF" | tee "$LAB/evidence/candidate-push.txt"
diff -u "$LAB/evidence/hosted-manifest-digest.txt" "$LAB/evidence/candidate-manifest-digest.txt"
For a pure retag of the same manifest, the manifest digest should match. If it does not, inspect the exact tool/media-type behavior rather than declaring the tags “the same image” from name alone.
12. Challenge: choose the correct endpoint
A developer wants to push
learner-example/ch08-app:1.1.0 and then let builds
consume both internal and public images. Which endpoint(s) should
they use in this Community lab?
The correct design is push to hosted, pull from group. The proxy is read-through upstream cache, not your write target. A group is the read aggregation endpoint; deployment to Docker/npm groups is a Pro capability, and this lab does not rely on it.
Knowledge check
Why use FROM scratch for the hosted-push exercise?
It creates a tiny synthetic image without needing to pull an external base, so the publication evidence is isolated from proxy/upstream behavior.
Why can the group serve the internal image without storing a second copy as a new publication?
The group routes reads to its members. The hosted repository owns the pushed image; the group provides a unified endpoint.
What proves exact identity better than “candidate points to the same thing”?
Compare the registry manifest digests. Matching digests prove the same manifest content; tag text alone is mutable metadata.
Why is a second pull not automatically proof that Nexus served from its proxy cache?
The container client also has a local content-addressed cache/store. Use local removal/fresh client state plus Nexus request/proxy evidence to isolate the layer.
Why is the lab login file disposable?
It contains reusable client credential material. Keep it out of the normal user profile and delete it after evidence capture.
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.