Chapter 13Lesson 02210–280 min

Proxy Caching, Negative Cache, Remote Storage, Routing Rules, Repository Health, and Failure Behavior: Guided Hands-On Workflow and Core Operations

Run a disposable Maven proxy experiment that observes cache fill, repeat reads, negative cache, scoped TTL changes, routing controls, and a safe upstream-unavailable simulation without modifying public infrastructure.

Maven proxy labCache evidenceTTLBlocked modeCommunity

Learning objectives

  • Create a disposable Maven proxy and group without changing a production repository.
  • Measure first fetch and repeat fetch while verifying component/asset state independently.
  • Observe a negative-cache candidate and change only disposable TTL settings.
  • Simulate an upstream outage safely with the proxy Blocked control and prove cached-versus-uncached behavior.
  • Test a routing rule before assignment and explain exactly which state each action changes.

Disposable-lab boundary. Use an already-running disposable Nexus Community instance on loopback/private networking. The examples use the current 3.95.2 image line as a reference but require you to record your actual running server version. Do not run this experiment against an organization's shared Maven proxy. The lab never alters Maven Central, DNS, firewalls or operating-system routes.

1. Create a local evidence and credential boundary

export NX_URL="http://127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch13"
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"   | tee "$LAB/evidence/repositories-before.json"

Windows note: use a temporary directory under $env:TEMP, protect any credential file with Windows ACLs, and use curl.exe if PowerShell aliases curl. Do not place a password in a command-line URL or a checked-in Maven settings file.

2. Create a purpose-built proxy and group

In Settings → Repository → Repositories create academy-ch13-central-proxy using maven2 (proxy) with these lab values:

Field Lab value Why
Remote storage https://repo1.maven.org/maven2/ Known public Maven Central endpoint; read-only upstream.
Version policy Release The coordinates used here are releases.
Layout policy Strict Keeps path semantics predictable.
Maximum Component Age 60 minutes Long enough to demonstrate a stable positive cache.
Maximum Metadata Age 5 minutes Makes metadata freshness deliberately different from component age.
Negative Cache Enabled, TTL 5 minutes Short disposable interval for observation; not a production recommendation.
Auto Blocking Enabled Preserves normal resilience behavior.
Blocked Unchecked Outbound access is initially allowed.

Create academy-ch13-public using maven2 (group) with the proxy as its only member. A group is used so the consumer endpoint resembles a production read path, while all remote behavior still belongs to the member proxy.

3. Prove the artifact is not already cached

The known artifact is the JUnit 4.13.2 POM. Query the proxy's assets before requesting it:

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch13-central-proxy"   > "$LAB/evidence/assets-before.json"

grep -F 'junit-4.13.2.pom' "$LAB/evidence/assets-before.json"   && echo 'STOP: artifact was already cached; recreate the disposable proxy'   || echo 'Expected: JUnit POM absent before first fetch'

If the artifact is already present, recreate the disposable proxy or choose another well-known release. A “first fetch” experiment is invalid if the object already exists in the proxy cache.

4. Fetch twice and compare byte identity plus timing

export JUNIT_PATH="junit/junit/4.13.2/junit-4.13.2.pom"
export GROUP_URL="$NX_URL/repository/academy-ch13-public"

curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/downloads/junit-first.pom"   -w 'first http=%{http_code} total=%{time_total} size=%{size_download}
'   "$GROUP_URL/$JUNIT_PATH"   | tee "$LAB/evidence/junit-first-metrics.txt"

curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/downloads/junit-second.pom"   -w 'second http=%{http_code} total=%{time_total} size=%{size_download}
'   "$GROUP_URL/$JUNIT_PATH"   | tee "$LAB/evidence/junit-second-metrics.txt"

sha256sum "$LAB/downloads/junit-first.pom" "$LAB/downloads/junit-second.pom"   | tee "$LAB/evidence/junit-sha256.txt"
cmp -s "$LAB/downloads/junit-first.pom" "$LAB/downloads/junit-second.pom"   && echo 'byte identity: PASS' | tee "$LAB/evidence/junit-byte-identity.txt"

Timing is only supporting evidence; a fast second request is not itself proof of a cache hit. Stronger evidence is the before/after repository state plus identical response bytes and the known cache policy.

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch13-central-proxy"   | tee "$LAB/evidence/assets-after-junit.json"

grep -F 'junit-4.13.2.pom' "$LAB/evidence/assets-after-junit.json"

5. Observe a negative-cache candidate without inventing an upstream package

export MISS_PATH="dev/academy/nonexistent/nexus-ch13-never-publish/0.0.0/nexus-ch13-never-publish-0.0.0.pom"

for n in 1 2; do
  curl -sS --netrc-file "$NX_AUTH_FILE"     -o "$LAB/evidence/miss-$n-body.txt"     -w "miss-$n http=%{http_code} total=%{time_total}
"     "$GROUP_URL/$MISS_PATH"     | tee "$LAB/evidence/miss-$n-metrics.txt"
done

Both responses should remain not-found. The second may be faster, but do not claim a negative-cache hit solely from latency. The causal evidence is: the path is absent from the proxy assets, the negative cache is enabled with a known TTL, and Nexus request/log evidence—if you enable temporary logging on the disposable instance—can show whether another remote attempt occurred.

6. Change one disposable variable: negative TTL or scoped invalidation

First record the current proxy fields. Then choose one of these experiments, not both at once:

  • Set Not Found Cache TTL from 5 minutes to 1 minute, save, wait for expiry, then repeat the missing path.
  • Use the repository's Invalidate Cache action, then repeat the missing path immediately.

Invalidate Cache expires component/metadata ages and purges the not-found cache. It does not delete the JUnit blob. Verify that JUnit remains present in the Assets API after invalidation.

7. Simulate upstream unavailability with Blocked

Edit only academy-ch13-central-proxy and check Blocked. This is the lab's outage injection: Nexus will not send outbound requests to Maven Central, but cached content remains eligible to serve.

# Cached JUnit should remain available.
curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/downloads/junit-while-blocked.pom"   -w 'cached-while-blocked http=%{http_code} total=%{time_total}
'   "$GROUP_URL/$JUNIT_PATH"   | tee "$LAB/evidence/blocked-cached.txt"

# This well-known artifact was not requested earlier in the lab.
export FRESH_PATH="org/hamcrest/hamcrest/2.2/hamcrest-2.2.pom"
curl -sS --netrc-file "$NX_AUTH_FILE"   -o "$LAB/evidence/blocked-uncached-body.txt"   -w 'uncached-while-blocked http=%{http_code} total=%{time_total}
'   "$GROUP_URL/$FRESH_PATH"   | tee "$LAB/evidence/blocked-uncached.txt"

Record the actual status returned by your pinned build rather than hard-coding a universal error code. The invariant is behavioral: cached JUnit remains available; Nexus does not retrieve the previously uncached Hamcrest POM while the proxy is manually blocked. Clear Blocked afterward and verify Hamcrest can then be requested successfully.

8. Build and test a routing rule before assigning it

Navigate to Settings → Repository → Routing Rules. Create a disposable BLOCK rule named academy-ch13-block-internal with a simple Java regular expression such as .*dev/academy/internal/.*. Use the built-in test panel with both a matching internal path and the JUnit path. Only after the tester behaves as predicted should you assign the rule to the disposable proxy.

The rule affects remote requests from that proxy. It does not delete existing cached content, change group order, or create a content-selector privilege.

9. Challenge: choose the right control

You need to stop public fallback for dev.academy.internal while still allowing all other Central dependencies. Which control belongs at the remote egress boundary: shorter cache age, group reordering, negative TTL, or a proxy routing rule? Write your answer and expected request evidence before applying anything.

10. Safe cleanup

Unassign and delete the disposable routing rule, delete academy-ch13-public, then delete academy-ch13-central-proxy through Nexus-supported UI/API operations. Confirm the repository names disappear from /service/rest/v1/repositories. Never remove cached Maven files directly from the blob store or database.

rm -f "$NX_AUTH_FILE"
unset NX_AUTH_FILE NX_USER JUNIT_PATH GROUP_URL MISS_PATH FRESH_PATH
# Keep the evidence directory if you are reviewing it; otherwise remove only this lab path.
# rm -rf "$LAB"

11. Knowledge check

Why is the first-vs-second curl timing not sufficient proof of caching?

What should happen to a cached component when you manually Block its proxy repository?

Why should a routing rule be tested before assignment?

Why is deleting proxy blob files an invalid refresh technique?

What does shortening negative TTL trade away?

12. Summary and next step

You have now observed positive cache fill, repeated reads, negative-cache behavior, scoped cache control, manual outage injection, and routing-rule testing. Lesson 3 converts those mechanics into design decisions for production proxy topology.

Official references and version notes

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.

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