Checkpoint Lab — Proxy Caching, Negative Cache, Remote Storage, Routing Rules, Repository Health, and Failure Behavior
Execute a controlled hit/miss/negative-cache/routing/upstream-failure experiment, predict state transitions, collect an evidence packet, apply least-destructive remediation, and clean up only the disposable repository state.
Checkpoint objectives
- Build a disposable public-read proxy topology and record exact starting state.
- Predict cache/blob/metadata/negative-cache/routing changes before triggering them.
- Execute hit, miss, negative-cache, routing-rule and upstream-blocked phases one variable at a time.
- Collect request, component/asset, checksum, configuration and failure evidence without secrets.
- Apply least-destructive remediation and remove only the lab repositories and rules.
Checkpoint scope. Use a disposable Nexus Community instance and synthetic repository/rule names. The current reference baseline is the 3.95.2 verified image line, but your evidence packet must record the actual running version. The upstream is public Maven Central and is never modified. Outage simulation uses Nexus Blocked state, not DNS/firewall sabotage. Do not run this checkpoint against a shared organization proxy.
1. Scenario and required prediction
You are validating whether a proposed proxy policy can keep already-used dependencies available during an upstream incident, suppress repeated misses, and stop an internal namespace from leaking to a public repository.
Before touching Nexus, write predictions for at least these four transitions:
| Phase | Prediction to write first |
|---|---|
| Positive miss → fill | Which repository gains asset/blob metadata after first JUnit request? |
| Repeat read | Will the returned bytes change? Is a remote check required under the chosen age? |
| Negative miss | What state exists even though no positive asset is stored? |
| Blocked outage | Which already-cached request still works and which previously uncached request cannot be retrieved? |
| Routing rule | Which namespace is prevented from reaching the public remote? |
2. Preflight and evidence workspace
export NX_URL="http://127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch13-checkpoint"
rm -rf "$LAB"
mkdir -p "$LAB"/{evidence,downloads}
chmod 700 "$LAB"
curl -fsS "$NX_URL/service/rest/v1/status" | tee "$LAB/evidence/status.txt"
read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
export NX_AUTH_FILE="$LAB/nexus.netrc"
umask 077
printf 'machine 127.0.0.1 login %s password %s
' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/repositories" > "$LAB/evidence/repositories-before.json"
Windows/PowerShell: use a protected temporary
directory and curl.exe. PowerShell's
Get-FileHash -Algorithm SHA256 is the equivalent
checksum command. Keep credentials out of command arguments and
remove the temporary secret file at the end.
3. Create the two-repository topology
Create through Settings → Repository → Repositories:
| Repository | Recipe | Configuration |
|---|---|---|
academy-ch13cp-central |
maven2 (proxy) |
Remote https://repo1.maven.org/maven2/;
Release; Strict; component age 60; metadata age 5; negative
cache enabled TTL 5; Auto Blocking enabled.
|
academy-ch13cp-public |
maven2 (group) | Single member: academy-ch13cp-central. |
Record screenshots or a text runbook of those exact fields. Do not
reuse maven-central because the checkpoint needs empty
initial cache state.
4. Phase A — positive miss and cache fill
export GROUP_URL="$NX_URL/repository/academy-ch13cp-public"
export JUNIT_PATH="junit/junit/4.13.2/junit-4.13.2.pom"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/assets?repository=academy-ch13cp-central" > "$LAB/evidence/assets-phase-a-before.json"
curl -sS --netrc-file "$NX_AUTH_FILE" -o "$LAB/downloads/junit-a.pom" -w 'phase-a http=%{http_code} total=%{time_total} size=%{size_download}
' "$GROUP_URL/$JUNIT_PATH" | tee "$LAB/evidence/phase-a-request.txt"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/assets?repository=academy-ch13cp-central" > "$LAB/evidence/assets-phase-a-after.json"
grep -F 'junit-4.13.2.pom' "$LAB/evidence/assets-phase-a-after.json"
sha256sum "$LAB/downloads/junit-a.pom" > "$LAB/evidence/junit-a.sha256"
Expected causal result: the proxy gains the JUnit asset/component metadata and blob-backed content after the first successful request.
5. Phase B — repeat read and byte identity
curl -sS --netrc-file "$NX_AUTH_FILE" -o "$LAB/downloads/junit-b.pom" -w 'phase-b http=%{http_code} total=%{time_total} size=%{size_download}
' "$GROUP_URL/$JUNIT_PATH" | tee "$LAB/evidence/phase-b-request.txt"
sha256sum "$LAB/downloads/junit-b.pom" > "$LAB/evidence/junit-b.sha256"
cmp -s "$LAB/downloads/junit-a.pom" "$LAB/downloads/junit-b.pom" && echo 'same bytes: PASS' | tee "$LAB/evidence/phase-b-identity.txt"
Do not claim that checksum equality proves trusted provenance. It proves the two observed downloads are byte-identical. Source trust, signatures, provenance and vulnerability policy are separate controls.
6. Phase C — negative cache
export MISS_PATH="dev/academy/ch13/nonexistent/never-publish/0.0.0/never-publish-0.0.0.pom"
for n in 1 2; do
curl -sS --netrc-file "$NX_AUTH_FILE" -o "$LAB/evidence/negative-$n-body.txt" -w "negative-$n http=%{http_code} total=%{time_total}
" "$GROUP_URL/$MISS_PATH" | tee "$LAB/evidence/negative-$n-metrics.txt"
done
Verify the path is absent from the Assets API. Record the configured five-minute negative TTL. If temporary request logging is enabled on this disposable instance, add only the relevant redacted lines to the evidence packet; otherwise treat timing as supportive, not definitive, evidence of the negative-cache hit.
7. Phase D — routing policy experiment
Create a BLOCK routing rule
academy-ch13cp-no-internal-public with matcher
.*dev/academy/internal/.*. In the built-in tester,
confirm a path under that namespace is blocked and
junit/junit/4.13.2/... is allowed. Assign the rule to
academy-ch13cp-central.
Request a synthetic internal path through the group and capture the response. The expected invariant is that Nexus does not forward that matching path to Maven Central. Then request cached JUnit again to prove the targeted routing rule did not destroy unrelated cached content.
8. Phase E — safe upstream-failure injection
Edit academy-ch13cp-central and check
Blocked. Capture that configuration state. Then
run:
# Cached content.
curl -sS --netrc-file "$NX_AUTH_FILE" -o "$LAB/downloads/junit-blocked.pom" -w 'blocked-cached http=%{http_code} total=%{time_total}
' "$GROUP_URL/$JUNIT_PATH" | tee "$LAB/evidence/blocked-cached.txt"
# Known Central release intentionally not requested earlier.
export NEW_PATH="commons-codec/commons-codec/1.17.0/commons-codec-1.17.0.pom"
curl -sS --netrc-file "$NX_AUTH_FILE" -o "$LAB/evidence/blocked-new-body.txt" -w 'blocked-new http=%{http_code} total=%{time_total}
' "$GROUP_URL/$NEW_PATH" | tee "$LAB/evidence/blocked-new.txt"
Record the actual HTTP result from the uncached request rather than forcing a specific status-code expectation. The important observation is that Nexus serves cached JUnit locally but does not retrieve the new Commons Codec POM while manually blocked.
9. Least-destructive remediation
- Clear Blocked on the disposable proxy.
-
Repeat only
$NEW_PATHand verify it can now populate the proxy. - Do not invalidate all caches merely because the remote was blocked.
-
If you want to prove the negative-cache control separately, use
Invalidate Cache on this disposable proxy and repeat only
$MISS_PATH; it should still be not-found because the upstream truly lacks it. - Keep the routing rule in place until the namespace-governance evidence is complete.
10. Optional Community RHC observation
If outbound access to Sonatype RHC services is permitted in the lab environment, enable Repository Health Check on the disposable Maven proxy and record only the summary state. It may take time to populate and is not required for cache correctness. Do not represent the Pro detailed report as Community functionality.
11. Required evidence packet
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/repositories" > "$LAB/evidence/repositories-after.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/assets?repository=academy-ch13cp-central" > "$LAB/evidence/assets-final.json"
python - <<'PYI'
from pathlib import Path
import os, json
lab=Path(os.environ['LAB'])
summary={
'proxy':'academy-ch13cp-central',
'group':'academy-ch13cp-public',
'remote':'https://repo1.maven.org/maven2/',
'component_age_minutes':60,
'metadata_age_minutes':5,
'negative_cache_ttl_minutes':5,
'routing_rule':'academy-ch13cp-no-internal-public',
'outage_simulation':'Nexus manual Blocked flag only',
'direct_blob_or_database_edits':False,
'credentials_in_evidence':False,
}
(lab/'evidence/checkpoint-summary.json').write_text(json.dumps(summary,indent=2)+'
')
PYI
find "$LAB/evidence" -maxdepth 1 -type f -printf '%f
' 2>/dev/null | sort > "$LAB/evidence/files.txt" || true
cat "$LAB/evidence/files.txt"
Review every evidence file before sharing it. The netrc file is intentionally outside the evidence directory and must never be included in a support bundle.
12. Verification checklist
- Actual Nexus version/edition and repository list were captured before changes.
- First successful JUnit request created observable proxy asset state.
- Second JUnit response is byte-identical to the first.
- Negative request produced no positive asset and the TTL was recorded.
- Routing rule was tested before assignment and only targets the intended namespace.
- Manual Blocked mode preserved cached JUnit while preventing retrieval of a previously uncached known artifact.
- Unblocking restored remote retrieval without deleting caches.
- No credential appears in command arguments, evidence JSON, screenshots or URLs.
- No database row, blob file, DNS entry, firewall rule or public upstream was modified.
13. Cleanup and rollback
- Clear Blocked if still set.
-
Unassign
academy-ch13cp-no-internal-publicfrom the proxy. - Delete the group
academy-ch13cp-public. - Delete the proxy
academy-ch13cp-central. - Delete the now-unassigned routing rule.
- Confirm all three names are absent from Nexus before removing local lab state.
rm -f "$NX_AUTH_FILE"
unset NX_AUTH_FILE NX_USER GROUP_URL JUNIT_PATH MISS_PATH NEW_PATH
# Archive reviewed evidence elsewhere if desired, then remove the disposable workspace.
rm -rf "$LAB"
Deleting the repository through supported Nexus operations is the cleanup boundary. Never remove its blob-store directory directly.
14. Knowledge check
During Blocked mode, JUnit still downloads but a never-before-requested package does not. What does this prove?
It proves Nexus can serve local cached content while outbound requests are disabled. It does not prove the upstream is healthy.
Why is the fake negative-cache coordinate intentionally never published?
It makes the not-found observation deterministic and prevents the lab from depending on changing public upstream content.
A routing rule blocks the internal namespace. What should you change if that matcher is wrong?
Change or unassign that routing rule only; do not clear blobs, rebuild indexes, or restart the server.
Why does the evidence packet record cache ages explicitly?
Without the configured TTL/age values, later reviewers cannot interpret whether a remote recheck should have occurred.
What is the production lesson from the outage phase?
Cached dependency availability is valuable, but only for content Nexus already has; cache policy and dependency warmness are part of resilience planning.
15. Production operating-model addition and bridge to Chapter 14
This chapter adds an explicit proxy runbook to the artifact platform: freshness SLOs, negative-cache policy, egress routing, upstream dependency evidence, auto-blocking behavior, scoped remediation and outage drills. Operators can now explain why a dependency was or was not fetched instead of treating the proxy as an opaque mirror.
Chapter 14 moves from remote egress control to who may see which repository paths: content selectors, repository targets, fine-grained privileges and path-level authorization.
Official references and version notes
- Sonatype: Repository Types.
- Sonatype: Configurable Repository Fields — proxy cache ages, negative cache, blocked and auto-blocking fields.
- Sonatype: Repository Actions — Invalidate Cache, Rebuild Index and HealthCheck actions.
- Sonatype: Routing Rules.
- Sonatype: HTTP Request and Proxy Settings.
- Sonatype: Repository Health Check and Self-Hosted Feature Matrix.
- Sonatype: Securing Nexus Repository — current private-network/SSRF controls for remote URLs.
- Sonatype: Nexus Repository API Reference.
- Sonatype verified Nexus Docker image tags.
Version-sensitive statements were rechecked on 2026-08-26.
Sonatype's verified container registry exposes Nexus Repository
3.95.2 as the current latest image line, while the
archive-download documentation may lag at 3.94.1. This chapter
therefore records the
actual running server version as authoritative
evidence and uses 3.95.2 only as the concrete current reference
baseline. The mandatory work requires Community-compatible proxy
repositories, routing rules, cache controls and the Repository
Health Check summary only; no Pro-only detailed RHC report, staging,
HA, user tokens, export/import or Repository Firewall capability is
required.
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.