Chapter 05Lesson 05190–240 min

Checkpoint Lab — Blob Stores, Storage Layout, Database Choices, Capacity Planning, and Data Separation

Model capacity, place disposable repositories on two blob stores, generate measurable content, prove checksum identity and recovery dependencies, then remove only the lab state through supported Nexus operations.

Checkpoint labCapacity modelMulti-store placementRecovery matrixSafe cleanup

Learning objectives

  • Build an independent two-blob-store lab without relying on state left by earlier lessons.
  • Model one year of growth and choose capacity from an explicit utilization/headroom target.
  • Predict repository, database, blob-store, checksum, and authorization effects before publishing.
  • Generate measurable synthetic content, preserve exact release bytes, and collect an evidence packet.
  • Prove the database/blob recovery dependency and clean up the entire checkpoint through supported Nexus operations.

Current lab baseline (reviewed 2026-08-26): Nexus Repository Community Edition 3.94.1-06, Java 21, loopback-only HTTP, a dedicated non-root/non-administrator process identity, and embedded H2 only for the disposable local instance. Sonatype currently recommends external PostgreSQL for production deployments. The lab never treats a blob store as a database backup or manipulates $data-dir/blobs or database files directly.

Independent checkpoint: use a disposable single-node Community instance on 127.0.0.1. This lab creates only academy-ch05-hot-store, academy-ch05-release-store, academy-ch05-builds, and academy-ch05-releases. If any name already exists, stop and choose a fresh disposable instance rather than deleting unknown state.

1. Record exact assumptions

Dimension Checkpoint assumption
Nexus Self-hosted Community Edition 3.94.1-06; re-check current GA/download before use.
Runtime Java 21; dedicated non-root/non-administrator Nexus process account.
Network Loopback HTTP only; no public exposure.
Database Embedded H2 for this disposable lab only; PostgreSQL is the production-preferred direction.
Blob backend Two file blob stores with relative disposable paths. No cloud account required.
Repository format Raw hosted for deterministic, protocol-simple byte measurement.
Credentials Disposable admin only for local configuration; stored in a temporary permission-restricted credential file.
Safety No direct database/blob-file edits, no real disk exhaustion, no production backup/migration.

2. Preflight and collision check

# POSIX/Bash. Use only against the disposable loopback instance.
LAB="$HOME/nexus-ch05-checkpoint"
NX_URL="http://127.0.0.1:8081"
mkdir -p "$LAB/evidence" "$LAB/payload" "$LAB/move"
umask 077
read -rsp "Disposable Nexus admin password: " NX_PASS; printf "\n"
printf 'machine 127.0.0.1 login admin password %s\n' "$NX_PASS" > "$LAB/nexus.netrc"
unset NX_PASS
NETRC="$LAB/nexus.netrc"

# Never print, commit, or reuse this temporary credential file.
curl -fsS "$NX_URL/service/rest/v1/status/writable"   | tee "$LAB/evidence/01-writable.txt"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/blobstores"   | tee "$LAB/evidence/02-blobstores-before.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/repositories"   | tee "$LAB/evidence/03-repositories-before.json"

python3 - <<'PY'
import json, pathlib, sys
root = pathlib.Path.home()/"nexus-ch05-checkpoint/evidence"
blobs = json.loads((root/"02-blobstores-before.json").read_text())
repos = json.loads((root/"03-repositories-before.json").read_text())
wanted_b = {"academy-ch05-hot-store","academy-ch05-release-store"}
wanted_r = {"academy-ch05-builds","academy-ch05-releases"}
present_b = {x.get("name") for x in blobs} & wanted_b
present_r = {x.get("name") for x in repos} & wanted_r
if present_b or present_r:
    print("Collision:", sorted(present_b | present_r))
    sys.exit(2)
print("No checkpoint name collisions.")
PY

df -h "$HOME" | tee "$LAB/evidence/04-disk-before.txt"

Windows PowerShell: use the same loopback URLs with curl.exe. Keep the disposable credential in a user-only temporary credential mechanism rather than embedding a password in command history. Use Get-FileHash -Algorithm SHA256 for hashes and Get-Volume/Get-PSDrive for free-space evidence. Do not run Nexus as Administrator merely to bypass a permissions problem.

If the collision script exits non-zero, stop. Do not delete the matching object unless you independently proved that it belongs to this exact checkpoint run.

3. Predict state changes before acting

Write your predictions into $LAB/evidence/05-predictions.txt before you create anything:

  1. Creating a blob store changes Nexus configuration/database state but does not create repository assets.
  2. Creating a repository mapped to a blob store changes repository configuration but does not add payload bytes.
  3. Uploading three files to academy-ch05-builds increases blob count/used size in academy-ch05-hot-store, not the release store.
  4. Publishing the exact retrieved release candidate to academy-ch05-releases adds bytes to the release store while preserving SHA-256 identity.

The checkpoint passes only if you later compare these predictions with independent API and checksum evidence.

4. Capacity model before storage creation

Assume the organization produces 18 GiB/month of build/release bytes and removes 8 GiB/month after supported retention. Releases retained separately add another 6 GiB/month. For twelve months:

\[\text{hot net growth}=(18-8)\times12=120\text{ GiB}\]

\[\text{release growth}=6\times12=72\text{ GiB}\]

Total projected retained bytes are 192 GiB. Add a 10% allowance for blob properties/allocation/estimation uncertainty: 211.2 GiB. Reserve another 100 GiB for operating growth, temporary work, cleanup/recovery and forecast error. If the target is at most 70% utilization:

\[(211.2+100)/0.70\approx444.6\text{ GiB}\]

This is a planning exercise, not a universal Nexus sizing formula. Real environments measure actual growth, package formats, cleanup lag, filesystem allocation, database/data-directory growth, and recovery staging.

5. Create two stores and two hosted repositories

Through Settings → Repository → Blob Stores, create file stores:

  • academy-ch05-hot-store with relative path academy-ch05-hot-store
  • academy-ch05-release-store with relative path academy-ch05-release-store

Then create Raw hosted repositories:

  • academy-ch05-builds → academy-ch05-hot-store, write policy Allow
  • academy-ch05-releases → academy-ch05-release-store, write policy Allow once
Checkpoint storage topology
flowchart TD
  CI[Synthetic producer] -->|PUT build assets| R1[academy-ch05-builds]
  R1 --> B1[(academy-ch05-hot-store)]
  CI -->|exact accepted release bytes| R2[academy-ch05-releases]
  R2 --> B2[(academy-ch05-release-store)]
  R1 --> DB[(Nexus database / repository metadata)]
  R2 --> DB
  B1 -. recovery dependency .-> DB
  B2 -. recovery dependency .-> DB
  DB -. recovery dependency .-> B1
  DB -. recovery dependency .-> B2

The diagram is deliberately asymmetric: repository configuration/metadata is relational database state; asset bytes live in the mapped blob stores. A recovery point must preserve their relationship.

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/blobstores"   | tee "$LAB/evidence/06-blobstores-created.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/repositorySettings"   | tee "$LAB/evidence/07-repository-settings-created.json"

6. Generate and publish measurable build content

Create three harmless files totaling 12 MiB. Record hashes before Nexus accepts them.

python3 - <<'PY'
from pathlib import Path
import hashlib
root = Path.home()/"nexus-ch05-checkpoint/payload"
root.mkdir(parents=True, exist_ok=True)
for mib, byte in [(3,b"D"), (5,b"E"), (4,b"R")]:
    p = root/f"checkpoint-{mib}MiB.bin"
    p.write_bytes(byte * (mib*1024*1024))
    print(p.name, p.stat().st_size, hashlib.sha256(p.read_bytes()).hexdigest())
PY

sha256sum "$LAB"/payload/*.bin | tee "$LAB/evidence/08-local-sha256.txt"

for f in "$LAB"/payload/*.bin; do
  name="$(basename "$f")"
  curl --fail-with-body --silent --show-error --netrc-file "$NETRC"     --upload-file "$f"     "$NX_URL/repository/academy-ch05-builds/builds/$name"
done

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/blobstores"   | tee "$LAB/evidence/09-blobstores-after-builds.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/assets?repository=academy-ch05-builds"   | tee "$LAB/evidence/10-build-assets.json"

Compare evidence 06 and 09. The hot store should show new blobs/used bytes; the release store should not contain the build assets merely because it exists.

7. Preserve exact release bytes

Treat the 4 MiB object as the accepted release candidate. Retrieve it from Nexus, hash it, then upload those exact bytes to the release repository. Do not rebuild or regenerate the file for the release stage.

SRC="$NX_URL/repository/academy-ch05-builds/builds/checkpoint-4MiB.bin"
DST="$NX_URL/repository/academy-ch05-releases/releases/checkpoint-4MiB.bin"

curl --fail-with-body --silent --show-error "$SRC"   -o "$LAB/move/accepted-release.bin"

sha256sum "$LAB/payload/checkpoint-4MiB.bin" "$LAB/move/accepted-release.bin"   | tee "$LAB/evidence/11-release-source-sha256.txt"
cmp "$LAB/payload/checkpoint-4MiB.bin" "$LAB/move/accepted-release.bin"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   --upload-file "$LAB/move/accepted-release.bin" "$DST"

curl --fail-with-body --silent --show-error "$DST"   -o "$LAB/move/retrieved-release.bin"

sha256sum "$LAB/move/accepted-release.bin" "$LAB/move/retrieved-release.bin"   | tee "$LAB/evidence/12-release-destination-sha256.txt"
cmp "$LAB/move/accepted-release.bin" "$LAB/move/retrieved-release.bin"

This models exact-artifact promotion in Community using ordinary hosted repository operations. It does not claim to be Nexus Pro staging or the Pro-only Change Repository Blob Store task.

8. Verify placement, headroom, and predictions independently

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/blobstores"   | tee "$LAB/evidence/13-blobstores-final.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/assets?repository=academy-ch05-releases"   | tee "$LAB/evidence/14-release-assets.json"

for b in academy-ch05-hot-store academy-ch05-release-store; do
  curl --fail-with-body --silent --show-error --netrc-file "$NETRC"     "$NX_URL/service/rest/v1/blobstores/$b/quota-status"     | tee "$LAB/evidence/15-$b-quota.json"
done

df -h "$HOME" | tee "$LAB/evidence/16-disk-after.txt"

Now compare each prediction from step 3 with the API outputs. A correct mental model explains why a store changed, not merely that numbers differ.

9. Recovery dependency matrix

State Checkpoint evidence Needed for real recovery? Why
Repository mappings/configuration 07-repository-settings-created.json Yes Defines which repositories and blob stores Nexus expects.
Relational database Recorded H2 mode for lab / PostgreSQL in production design Yes Contains Nexus repository/component/configuration state.
Hot blob content 09/13-blobstores*.json plus asset evidence Depends on RPO/retention Contains build assets; some may be reconstructable but do not assume.
Release blob content Release asset + SHA-256 evidence Yes for retained authoritative releases Contains exact accepted release bytes.
Application directory Pinned release/version evidence Reinstallable software, not sufficient backup Needed to run Nexus but does not replace persistent state.
Client cache Not part of checkpoint backup No A consumer cache is not an authoritative repository recovery source.

For this checkpoint, you are not performing a real backup. The acceptance requirement is to identify the pieces a future Chapter 25 backup must coordinate and to reject “we copied the blob folder” as sufficient proof.

10. Evidence packet

Your checkpoint packet should contain:

  • Nexus version/runtime/database-mode note and loopback/edition assumptions.
  • Before/after blob-store and repository lists.
  • Repository-to-blob mappings.
  • Capacity model and chosen headroom target.
  • Local payload hashes and asset listings.
  • Source/destination release SHA-256 proof.
  • Final blob counts/used sizes, quota status, and OS free-space evidence.
  • The prediction-vs-observation review and recovery dependency matrix.

Do not include the temporary credential file in the evidence packet.

11. Cleanup and rollback

Cleanup order proves you understand dependencies. Through Nexus UI/API, delete the two disposable repositories first. Then confirm no repository uses the two checkpoint blob stores and delete those stores through Nexus. Do not remove their backing directories manually.

# After deleting the two repositories and then the two blob stores in Nexus:
curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/repositories"   | tee "$LAB/evidence/17-repositories-after-cleanup.json"

curl --fail-with-body --silent --show-error --netrc-file "$NETRC"   "$NX_URL/service/rest/v1/blobstores"   | tee "$LAB/evidence/18-blobstores-after-cleanup.json"

rm -f "$NETRC"
unset NETRC

# Remove only the local client/evidence workspace after saving the evidence you need.
# rm -rf "$LAB"   # optional; this is NOT a Nexus data/blob path.

If Nexus refuses to delete a blob store because it is still in use, treat that as useful dependency evidence. Find the remaining repository reference; do not force-delete filesystem content.

Knowledge check

Why must repositories be deleted before their blob stores in checkpoint cleanup?

What proves the release promotion preserved artifact identity?

If the release blob store survives but the database is lost, is the checkpoint service recoverable as-is?

Why is the calculated 444.6 GiB target not a universal Nexus requirement?

A soft quota is below current used size. Should Nexus block the next upload?

What production topic follows naturally from this chapter?

12. What Chapter 05 adds to the operating model

The artifact platform now has an explicit storage contract: application software is separate from persistent state; database and blob stores are distinct but recovery-coupled; repository mappings are configuration evidence; capacity has headroom and emergency thresholds; IO/permissions/file handles are integrity dependencies; and cleanup/migration occurs through supported Nexus mechanisms.

13. Summary

You can now predict where a Nexus change lands, measure which storage layer grows, distinguish alerting from enforcement, preserve exact bytes across repository stages, and explain why a backup must coordinate relational and blob state. Chapter 06 can therefore focus on Maven protocol semantics without re-teaching storage fundamentals.

Next chapter

Maven 2 repositories, snapshots, releases, metadata, checksums, and client configuration

Apply the repository and storage model to Maven coordinates, repository policies, metadata, publication, resolution, and verification.

Official references and version notes

Version-sensitive statements were rechecked against Sonatype primary documentation on 2026-08-26. The current Download, versions-status, and 2026 release-notes pages list 3.94.1 as the newest GA/downloadable self-hosted line. Mandatory labs therefore pin Nexus Repository Community Edition 3.94.1-06 on loopback with Java 21 and embedded H2 only as a disposable learning database. Current system requirements recommend external PostgreSQL for supported production-scale deployments and require at least 4 GB of free disk at all times. Learners should re-check the live pages before executing the lab because Nexus support matrices evolve.

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.