Chapter 06Lesson 02165–210 min

Maven 2 Repositories, Snapshots, Releases, Metadata, Checksums, and Maven Client Configuration: Guided Hands-On Workflow and Core Operations

Build a disposable Maven/Nexus topology, configure an isolated settings.xml and local repository, resolve through a group, deploy synthetic snapshots and releases only to hosted repositories, and inspect metadata and checksums.

Hosted / proxy / groupMaven Deploy PluginIsolated clientChecksumsEvidence

Learning objectives

  • Create disposable Maven release, snapshot, Central proxy, and group repositories with policies that match their roles.
  • Use a dedicated settings.xml and local Maven repository so the exercise cannot be confused with a developer's normal cache/configuration.
  • Resolve a public dependency through the Nexus group and distinguish first proxy fetch from later cached use.
  • Deploy synthetic snapshot and release components with the Maven Deploy Plugin only to hosted repositories.
  • Verify Maven metadata, component/assets, and checksums after each mutation.

Disposable only. Use a local Nexus 3.95.2 Community instance on loopback. Create a dedicated lab user rather than reusing a production identity. The tutorial never asks for a real company namespace, public endpoint, or administrator password in a command line.

1. Preflight: prove versions and isolate the Maven client

Start with evidence, then create a lab directory. Maven's default local cache at ~/.m2/repository can hide whether Nexus served a request, so this chapter gives Maven a separate local repository. That isolation is as important as the disposable Nexus repositories.

set -eu
NX_URL=http://127.0.0.1:8081
LAB="${TMPDIR:-/tmp}/academy-nexus-ch06"
rm -rf "$LAB"
mkdir -p "$LAB/m2" "$LAB/project" "$LAB/evidence"
mvn --version | tee "$LAB/evidence/00-maven-version.txt"
curl -fsS "$NX_URL/service/rest/v1/status" | tee "$LAB/evidence/01-nexus-status.txt"
printf 'Lab directory: %s
' "$LAB"

2. Create four repositories with one responsibility each

In Settings → Repository → Repositories, create the following lab-only Maven repositories. Using the UI here makes the policy fields visible to beginners; later chapters automate repository creation via REST.

Name Recipe / important settings Why
academy-ch06-releases maven2 hosted · Version policy Release · Layout Strict · Deployment policy Disable redeploy Stable internal publication target.
academy-ch06-snapshots maven2 hosted · Version policy Snapshot · Layout Strict · Deployment policy Allow redeploy Repeated development publications and metadata updates.
academy-ch06-central maven2 proxy · Release · Strict · https://repo1.maven.org/maven2/ Controlled/cacheable route to Central.
academy-ch06-public maven2 group · members releases, snapshots, central in that order One read endpoint for Maven.

Record the repository list after creation. The configuration lives in Nexus relational state; proxied or uploaded repository file bytes live in the selected blob store.

curl -fsS "$NX_URL/service/rest/v1/repositories" > "$LAB/evidence/02-repositories-after.json"
grep -E 'academy-ch06-(releases|snapshots|central|public)' "$LAB/evidence/02-repositories-after.json" || true

3. Create a disposable publisher identity

Create a local Nexus user named academy-ch06-publisher and grant only the privileges needed to read/browse the group and add/edit content in the two hosted lab repositories. Do not use the built-in administrator as a Maven CI pattern. The exact privilege construction is covered deeply in Chapter 15; here the point is to keep the credential scope smaller than instance administration.

Load the disposable credentials into environment variables interactively:

read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
export NX_USER NX_PASS

Environment variables are used only to keep this lab from embedding a password in HTML, shell history, or project files. For real CI, use the CI platform's secret store and Maven's supported credential protection; clear these variables during cleanup.

4. Build an isolated settings.xml

The mirror sends repository reads to the Nexus group. The profile enables both releases and snapshots on the logical central repository so the mirror can serve both from the group. The two server IDs match the IDs used later by the Deploy Plugin.

cat > "$LAB/settings.xml" <<'XML'
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <localRepository>${env.MAVEN_LAB_REPO}</localRepository>
  <servers>
    <server><id>academy-ch06-releases</id><username>${env.NX_USER}</username><password>${env.NX_PASS}</password></server>
    <server><id>academy-ch06-snapshots</id><username>${env.NX_USER}</username><password>${env.NX_PASS}</password></server>
  </servers>
  <mirrors>
    <mirror><id>academy-ch06-public</id><mirrorOf>*</mirrorOf><url>http://127.0.0.1:8081/repository/academy-ch06-public/</url></mirror>
  </mirrors>
  <profiles>
    <profile><id>academy-ch06</id><repositories><repository><id>central</id><url>http://central.invalid/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories><pluginRepositories><pluginRepository><id>central</id><url>http://central.invalid/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></pluginRepository></pluginRepositories></profile>
  </profiles>
  <activeProfiles><activeProfile>academy-ch06</activeProfile></activeProfiles>
</settings>
XML
export MAVEN_LAB_REPO="$LAB/m2"

5. Resolve a harmless public dependency through the group

Use Apache Commons Lang only as a read test. A fresh local cache means the first request must leave Maven, hit the group, enter the Central proxy if no hosted member owns the coordinate, reach Central, then populate Nexus proxy state and the isolated Maven cache. A second resolution may be served without the same upstream work.

mvn -s "$LAB/settings.xml" -U   org.apache.maven.plugins:maven-dependency-plugin:3.9.0:get   -Dartifact=org.apache.commons:commons-lang3:3.20.0   | tee "$LAB/evidence/03-first-resolution.txt"

mvn -s "$LAB/settings.xml"   org.apache.maven.plugins:maven-dependency-plugin:3.9.0:get   -Dartifact=org.apache.commons:commons-lang3:3.20.0   | tee "$LAB/evidence/04-second-resolution.txt"

Verify in Nexus Browse/Search that the component now exists in academy-ch06-central. That observation separates Nexus proxy cache from Maven's local cache: both may now hold copies, but they are different state stores with different owners and eviction rules.

6. Create deterministic synthetic Maven files

We use tiny ZIP-based JARs rather than source code so the lesson focuses on repository semantics, not Java compilation.

mkdir -p "$LAB/project/payload"
printf 'chapter=06
artifact=ch06-demo
content=v1
' > "$LAB/project/payload/info.txt"
(cd "$LAB/project/payload" && jar --create --file ../ch06-demo.jar info.txt)

cat > "$LAB/project/release-pom.xml" <<'XML'
<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><groupId>com.example.academy</groupId><artifactId>ch06-demo</artifactId><version>1.0.0</version><packaging>jar</packaging></project>
XML
cat > "$LAB/project/snapshot-pom.xml" <<'XML'
<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><groupId>com.example.academy</groupId><artifactId>ch06-demo</artifactId><version>1.1.0-SNAPSHOT</version><packaging>jar</packaging></project>
XML
sha256sum "$LAB/project/ch06-demo.jar" | tee "$LAB/evidence/05-source-sha256.txt"

Windows PowerShell: create the same files under a temporary folder, use the JDK jar.exe, and record SHA-256 with Get-FileHash -Algorithm SHA256. If jar is unavailable, use a ZIP utility and name the archive .jar; the fixture contains no executable code.

7. Deploy snapshot, then release, to hosted targets only

repositoryId selects the matching <server> entry. The URL selects the hosted Nexus target. The client uploads POM/JAR data; Nexus validates repository format/version/deployment policies, writes relational metadata, stores assets in the blob store, and updates Maven metadata where required.

mvn -s "$LAB/settings.xml" org.apache.maven.plugins:maven-deploy-plugin:3.1.4:deploy-file   -DrepositoryId=academy-ch06-snapshots   -Durl="$NX_URL/repository/academy-ch06-snapshots/"   -Dfile="$LAB/project/ch06-demo.jar"   -DpomFile="$LAB/project/snapshot-pom.xml"   | tee "$LAB/evidence/06-snapshot-deploy.txt"

mvn -s "$LAB/settings.xml" org.apache.maven.plugins:maven-deploy-plugin:3.1.4:deploy-file   -DrepositoryId=academy-ch06-releases   -Durl="$NX_URL/repository/academy-ch06-releases/"   -Dfile="$LAB/project/ch06-demo.jar"   -DpomFile="$LAB/project/release-pom.xml"   | tee "$LAB/evidence/07-release-deploy.txt"

8. Verify metadata, bytes, and group visibility independently

curl -fsS "$NX_URL/repository/academy-ch06-public/com/example/academy/ch06-demo/1.0.0/ch06-demo-1.0.0.jar"   -o "$LAB/evidence/retrieved-release.jar"
sha256sum "$LAB/project/ch06-demo.jar" "$LAB/evidence/retrieved-release.jar"

curl -fsS "$NX_URL/repository/academy-ch06-snapshots/com/example/academy/ch06-demo/1.1.0-SNAPSHOT/maven-metadata.xml"   | tee "$LAB/evidence/08-snapshot-metadata.xml"

curl -fsS "$NX_URL/service/rest/v1/search?repository=academy-ch06-releases&group=com.example.academy&name=ch06-demo"   | tee "$LAB/evidence/09-release-search.json"

The two SHA-256 values should match. Snapshot metadata should contain timestamp/build information that connects 1.1.0-SNAPSHOT to a unique stored snapshot. Search should return the release component and assets.

9. Challenge: choose the control, do not copy a sequence

A developer asks to publish 1.2.0-SNAPSHOT but proposes sending it to academy-ch06-public because that is the URL in settings.xml. Decide which endpoint must receive the deployment and explain three states that would change if it succeeds.

Expected reasoning: deploy to academy-ch06-snapshots. The hosted repository's Maven metadata/database records change, snapshot assets are written to its blob store, and the group can subsequently expose them to readers. The group is the read aggregation boundary, not the ordinary authoritative publish target for this lab.

Knowledge check

Why did the lab use a separate Maven local repository?

Which Nexus repository should receive 1.1.0-SNAPSHOT?

What should change after the first Central resolution but not necessarily after the second?

Why is matching the source and retrieved SHA-256 useful?

Where should a CI publisher password live in a real pipeline?

10. Summary

You created a four-repository Maven topology, isolated the client, observed proxy behavior, deployed snapshot/release components to hosted targets, and verified metadata plus exact bytes. The next lesson evaluates the policy choices behind that design.

Next lesson

Choose policies deliberately

Decide mirror scope, release immutability, snapshot behavior, namespace routing and integrity controls from observable consequences.

Official references and version notes

Version-sensitive statements were rechecked against Sonatype and Apache Maven primary documentation on 2026-08-26. The mandatory lab pins Nexus Repository Community Edition 3.95.2 and Apache Maven 3.9.16. Nexus 3.95.2 was released 2026-08-21; Maven 3.9.16 is the current recommended Maven 3 release. Nexus 3.87+ requires Java 21 when using an external JVM and official Nexus packages include a bundled Java 21 runtime. Re-check live support/download pages before executing these labs.

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.