Cleanup Policies, Retention, Component Age, Last-Downloaded Rules, Preview, and Storage Reclamation: Guided Hands-On Workflow and Core Operations
Practice the complete retention workflow on disposable data: inventory first, model age and usage safely, build a narrow policy, preview it, execute logical cleanup, verify the surviving artifacts, and reclaim blob space only after evidence review.
Learning objectives
- Create a disposable Raw hosted repository and synthetic retention dataset without touching global package-manager configuration.
- Separate a deterministic live deletion exercise from an age/usage fixture that does not falsify Nexus internal timestamps.
- Preview and execute a narrowly scoped cleanup policy using current supported UI behavior.
- Prove logical deletion independently from physical blob-store reclamation.
- Capture a reproducible evidence packet and remove only resources created by the lab.
1. Scenario and safety envelope
You operate a small build platform with fast-moving development artifacts. The team wants to learn retention mechanics today, but component-age and last-downloaded criteria are measured in days. Changing the server clock, database rows, or blob metadata to make artifacts “old” would teach an unsafe practice. This lab therefore has two synchronized tracks:
- Live Nexus track: a disposable Raw hosted repository uses an Asset Name Matcher to make candidate selection deterministic and immediate. You preview, execute, verify soft deletion, then compact only the dedicated lab blob store.
- Retention-model track: a local CSV fixture contains controlled published/last-downloaded dates. A tiny Python evaluator demonstrates exactly how age and usage interact before you later apply real day-based policies to naturally aged content.
Isolation rule. Use only a disposable local Nexus
instance, a dedicated lab repository, a dedicated lab blob store
if your edition/UI makes that practical, synthetic paths under
learner-example/, and fake credentials. Never run
this exercise on an employer repository, shared blob store,
production database, or valuable backup.
Version baseline (26 August 2026). Sonatype's current 3.95.x release notes list Nexus Repository 3.95.2 (released 21 August 2026) as the newest patch in that line. The 3.95 release expanded cleanup-policy administration and retain-last-N coverage. Current Nexus Repository 3.95.x system requirements use Java 21; official bundles include the recommended runtime. Record the exact version, edition, database, and blob-store type of your lab before applying any cleanup rule because cleanup behavior and available criteria are version- and format-sensitive.
2. Preflight: prove what instance you are about to change
Record the exact version, edition, database type, blob-store name, repository list, and task state. For the mandatory path, Community Edition is sufficient for normal cleanup policies; do not depend on the Pro-only cleanup-policy REST management API or Retain Select Versions.
| Check | Expected lab condition | Stop if |
|---|---|---|
| Nexus | Disposable self-hosted 3.95.x; examples validated against 3.95.2 docs | Production/shared instance |
| Runtime | Supported Java 21 runtime | Unsupported runtime or unknown version |
| Database | Local H2 is acceptable for this small single-node lab | You are about to manipulate DB files directly |
| Repository |
New cleanup-lab-raw hosted Raw repository
|
Name already exists with unknown content |
| Blob store |
Prefer dedicated cleanup-lab-blobs file blob
store
|
It contains unrelated repositories |
| Auth | Temporary admin only for setup; least-privilege reader for verification | Real token/password would be copied into lesson commands |
# POSIX/Bash — read-only preflight
BASE='http://127.0.0.1:8081'
curl -fsS "$BASE/service/rest/v1/status"
# Inspect Settings → Repository → Repositories, Blob Stores,
# Cleanup Policies, and System → Tasks in the UI as well.
3. Create the disposable repository boundary
In Settings → Repository → Blob Stores, create a file blob store
named cleanup-lab-blobs if your lab does not already
have a safe disposable store. Then create a Raw hosted repository
named cleanup-lab-raw on that blob store. Keep
deployment policy appropriate for a lab and do not enable anonymous
write access.
Using a dedicated blob store makes the final reclamation observation understandable: compaction affects only disposable bytes. If you must use a shared lab blob store, stop before compaction and treat physical reclamation as a simulation because compacting a shared store crosses the lab's isolation boundary.
4. Populate synthetic “keep” and “expire” paths
Raw is useful here because the cleanup criterion can match asset paths without introducing Maven/npm/Docker metadata. Upload four tiny files through the supported repository HTTP endpoint. Use a credential from an environment variable rather than embedding it in shell history.
# POSIX/Bash — disposable lab only
BASE='http://127.0.0.1:8081'
REPO='cleanup-lab-raw'
: "${NEXUS_USER:?set a temporary lab user}"
: "${NEXUS_PASS:?set a temporary lab password}"
mkdir -p cleanup-lab-payloads
printf 'release 1\n' > cleanup-lab-payloads/release-1.txt
printf 'release 2\n' > cleanup-lab-payloads/release-2.txt
printf 'ci build 101\n' > cleanup-lab-payloads/build-101.txt
printf 'ci build 102\n' > cleanup-lab-payloads/build-102.txt
curl -fsS -u "$NEXUS_USER:$NEXUS_PASS" --upload-file cleanup-lab-payloads/release-1.txt "$BASE/repository/$REPO/learner-example/releases/release-1.txt"
curl -fsS -u "$NEXUS_USER:$NEXUS_PASS" --upload-file cleanup-lab-payloads/release-2.txt "$BASE/repository/$REPO/learner-example/releases/release-2.txt"
curl -fsS -u "$NEXUS_USER:$NEXUS_PASS" --upload-file cleanup-lab-payloads/build-101.txt "$BASE/repository/$REPO/learner-example/ci-expire/build-101.txt"
curl -fsS -u "$NEXUS_USER:$NEXUS_PASS" --upload-file cleanup-lab-payloads/build-102.txt "$BASE/repository/$REPO/learner-example/ci-expire/build-102.txt"
Verify all four paths with HEAD or GET requests and Browse. Capture checksums locally before cleanup. The repository should be authoritative for these synthetic bytes; no proxy/upstream state participates.
sha256sum cleanup-lab-payloads/*.txt
for p in learner-example/releases/release-1.txt learner-example/releases/release-2.txt learner-example/ci-expire/build-101.txt learner-example/ci-expire/build-102.txt; do
curl -fsSI -u "$NEXUS_USER:$NEXUS_PASS" "$BASE/repository/$REPO/$p" | head -n 1
done
5. Build controlled age/usage evidence without falsifying Nexus
Create a local fixture that represents the lifecycle SLA you actually want: remove development artifacts only when they are at least 30 days old and have not been downloaded for 14 days; never use this fixture as proof that the live repository has those timestamps.
path,published_days_ago,last_downloaded_days_ago,class
learner-example/ci/build-097.txt,45,31,development
learner-example/ci/build-098.txt,40,2,development
learner-example/ci/build-099.txt,12,12,development
learner-example/releases/release-1.txt,180,120,release
learner-example/releases/release-2.txt,20,3,release
# evaluate_retention.py — local simulation, not a Nexus API
import csv
AGE_DAYS = 30
USAGE_DAYS = 14
with open("retention-fixture.csv", newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
for row in rows:
old = int(row["published_days_ago"]) >= AGE_DAYS
idle = int(row["last_downloaded_days_ago"]) >= USAGE_DAYS
development = row["class"] == "development"
candidate = old and idle and development
print(f"{row['path']}: {'DELETE-CANDIDATE' if candidate else 'KEEP'}")
The expected candidate is build-097.txt only. Build 098
is old but recently used; build 099 is idle-looking but too new;
releases are protected by lifecycle class. Nexus policies may
express release protection differently by format, but the AND
reasoning is the same.
6. Create a deterministic live cleanup policy
In Settings → Repository → Cleanup Policies, create
cleanup-lab-ci-expire for Raw. Use the
Asset Name Matcher supported for Raw and match only
the disposable subtree. For the H2/PostgreSQL baseline used here,
current Sonatype examples represent the asset name with a leading
slash, so use an anchored Java-regex pattern:
^/learner-example/ci-expire/.*$
Do not add age or usage to the live policy for this immediate exercise because the four files were just uploaded and should not be made artificially old. In 3.95, current UI documentation allows repositories to be associated from the policy configuration; if your exact patch/interface presents association on the repository instead, attach the same policy from the repository's Cleanup section. Record which UI path your version uses.
Prediction 1. Preview should select the two
ci-expire assets and exclude both
releases assets. Prediction 2. After
logical cleanup, the deleted paths should return not-found while
physical capacity may remain nearly unchanged until compaction.
7. Preview before execution
Open the policy's preview for cleanup-lab-raw. Confirm
that every selected path is under
learner-example/ci-expire/ and that both release paths
are absent. If the preview contains anything unexpected, do not
execute—fix the policy first.
Capture the policy name, repository, regex, preview timestamp, and candidate count. Remember that current documentation calls preview point-in-time evidence. Its job is to show that your intent and Nexus's interpretation agree before mutation.
8. Execute supported cleanup and verify logical deletion
Current Nexus maintains a system cleanup service task for repositories with associated policies. On a disposable lab you may run that task manually from System → Tasks after confirming the scope, or wait for its schedule. Record the task name, start/end status, and result. Do not create an ad-hoc filesystem deletion job.
# Verification after the cleanup service has completed
for p in learner-example/ci-expire/build-101.txt learner-example/ci-expire/build-102.txt; do
code=$(curl -sS -o /dev/null -w '%{http_code}' -u "$NEXUS_USER:$NEXUS_PASS" "$BASE/repository/$REPO/$p")
printf '%s -> %s\n' "$p" "$code"
done
for p in learner-example/releases/release-1.txt learner-example/releases/release-2.txt; do
code=$(curl -sS -o /dev/null -w '%{http_code}' -u "$NEXUS_USER:$NEXUS_PASS" "$BASE/repository/$REPO/$p")
printf '%s -> %s\n' "$p" "$code"
done
Expected: the two expired lab paths are unavailable; the two release paths remain available. Also verify Browse/search and task history. This proves repository semantics—not physical disk recovery.
9. Reclaim physical storage only after accepting the deletion set
If and only if cleanup-lab-blobs is dedicated to this
exercise, create or use an
Admin - Compact blob store task targeted at that blob
store. Current versions allow a
Blobs Older Than retention period so soft-deleted
blobs can remain recoverable for a controlled window before hard
deletion. For a training instance, use a value consistent with your
lab intent and understand that hard deletion is the point after
which repository-level recovery options become much more limited.
Destructive boundary. Compaction permanently removes eligible soft-deleted blob content. Do not run it on a shared or production blob store merely to make a storage graph move during a lesson. If the lab blob store is shared, stop here and document the expected operation instead.
Measure capacity at the volume/blob-store level before and after, not by deleting or rewriting internal blob files. A four-file lab may be too small for a visible filesystem change; correctness is established by task success and repository evidence, not by requiring a large number of bytes to disappear.
10. What changed at each step
| Step | Repository/database state | Blob state | Client observation |
|---|---|---|---|
| Upload | Raw assets/components registered | New blobs allocated | 200 on GET |
| Create/attach policy | Configuration changes only | No content change | All artifacts still readable |
| Preview | Read-only candidate evaluation | No deletion | All artifacts still readable |
| Cleanup service | Matched content logically removed/soft-deleted | Soft-deleted bytes can remain | Matched paths unavailable |
| Compaction | Deletion already accepted | Eligible soft-deleted blobs hard deleted | No change to which paths are already absent |
11. Challenge: choose the control, not the clicks
Your team says: “Delete build artifacts older than 30 days, but only if nobody downloaded them for 14 days; keep release artifacts regardless of age.” Write the policy logic you would want for a versioned format that supports release type. Explain why age alone is insufficient, why usage alone is insufficient, and whether a separate dev repository would make this policy safer. Do not execute a new rule until you can predict its candidate set from an inventory.
12. Lab cleanup and rollback
- Save the evidence packet first: screenshots/notes, fixture output, checksums, task result, and verification status.
- Delete the cleanup policy association from the disposable repository, then delete the policy if no other repository uses it.
-
Delete
cleanup-lab-rawthrough the Nexus UI/API supported for repository administration. - If the dedicated blob store is now unused, delete it through Nexus after verifying no repository references it.
-
Remove local payload/fixture files and unset
NEXUS_USER/NEXUS_PASS. - If compaction was intentionally skipped, record that physical reclamation was not performed.
Rollback for an accidentally selected component depends on whether hard deletion has occurred and on available supported recovery/backup evidence. This chapter does not normalize undelete as a substitute for safe preview; Chapter 25 will treat tested backup/restore as the recovery control.
13. Knowledge check
Why does the live lab use Asset Name Matcher instead of fabricating old timestamps?
Because changing server time, database rows, or blob metadata would bypass supported Nexus semantics. A deterministic path matcher proves the cleanup lifecycle safely, while the CSV fixture teaches age/usage logic.
What must be true before running compaction in this lab?
The blob store must be safely isolated to disposable content, the cleanup result must be accepted and verified, and the operator must understand that compaction is permanent reclamation.
Build 098 is 40 days old but downloaded 2 days ago. Does a 30-day-age AND 14-day-usage rule delete it?
No. It fails the usage criterion because it was recently downloaded.
What evidence proves the release paths were protected?
The preview excludes them and controlled post-cleanup GET/HEAD requests still succeed for those exact paths.
Why might disk free space barely change in this tiny lab?
The payload is very small, filesystem allocation is coarse, and logical deletion precedes physical reclamation. Task/repository evidence matters more than a dramatic capacity delta.
14. Summary and next step
You have now separated retention reasoning from destructive implementation: deterministic live cleanup proves policy → preview → logical deletion → compaction, while a fixture proves age/usage logic without corrupting Nexus metadata. Lesson 3 moves from one lab to production design choices: how long to retain releases, how to trust usage data, and how to schedule cleanup without harming reliability.
Official references and version notes
- Sonatype: Cleanup Policies — current criteria, preview, system cleanup tasks, soft deletion, compact-blob-store reclamation, and format matrix.
- Sonatype: Nexus Repository 3.95.x Release Notes — 3.95 cleanup enhancements and current known issues.
-
Sonatype: Tasks
— current task types including
Admin - Compact blob store. - Sonatype: Keeping Disk Usage Low — supported cleanup/reclamation guidance and the separation between deletion and freed disk space.
- Sonatype: Raw Repositories — Raw repository behavior used for the disposable payload.
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.