Deploy, Publish, and Distribution Management
Learn Maven In Action - Part 031
Deploy, publish, and distribution management for production-grade Maven builds: install vs deploy, distributionManagement, credentials, staging, snapshot/release routing, multi-module deploy, repository promotion, and incident playbooks.
Part 031 — Deploy, Publish, and Distribution Management
Maven deployment is not “uploading a JAR”.
In a serious engineering organization, Maven deployment is the point where a local build becomes a shared artifact that other projects, teams, runtime images, and release pipelines may consume.
That means deployment is a boundary with consequences:
- dependency consumers may resolve your artifact;
- CI jobs may cache it;
- repository managers may index it;
- runtime images may embed it;
- scanners may generate vulnerability reports from it;
- auditors may ask where it came from;
- rollback procedures may depend on its immutability.
This part teaches Maven deploy as an artifact publication protocol.
We will separate:
package— produce artifact locally insidetarget/;install— put artifact into the local repository;deploy— publish artifact to a remote repository;- repository manager staging — hold and validate candidate releases;
- promotion — move validated artifacts to a trusted release repository.
By the end, you should be able to design a Maven publication flow that is reproducible, auditable, CI-safe, and safe for multi-module enterprise builds.
1. The Core Mental Model
Maven deployment is a controlled transition:
source code -> local build output -> local repository -> remote repository -> consumed dependency
Each arrow changes the trust level.
| Boundary | Maven command/concept | What changes |
|---|---|---|
| Source to build output | mvn package | Project source becomes files under target/. |
| Build output to local artifact | mvn install | Artifact becomes resolvable by other local builds. |
| Local artifact to shared artifact | mvn deploy | Artifact becomes resolvable by other machines. |
| Shared artifact to release artifact | repository staging/promotion | Artifact becomes an approved organization dependency. |
The mistake is treating all four as equivalent.
They are not.
package proves Maven can assemble the artifact.
install proves the artifact can be resolved locally using Maven repository layout.
deploy publishes the artifact to a remote repository for other developers and projects.
A release/promotion process proves the artifact is acceptable for wider consumption.
2. Maven Lifecycle Placement
Deployment belongs to Maven's deploy phase.
A simplified lifecycle path:
validate
-> compile
-> test
-> package
-> verify
-> install
-> deploy
deploy happens after install.
That is intentional. Maven should not publish an artifact before it has passed the local build, test, package, and verification flow.
In production, you rarely want developers to run mvn deploy casually from laptops.
You usually want:
Developer laptop:
mvn verify
CI validation branch:
mvn verify
CI release/publish job:
mvn deploy
Why?
Because deploy changes shared state.
A local mvn verify is mostly private. A remote mvn deploy affects the organization.
3. install vs deploy
mvn install
install copies the built artifact and its POM into the local Maven repository.
Usually:
~/.m2/repository/<groupId path>/<artifactId>/<version>/
Example:
~/.m2/repository/com/acme/payment/payment-core/1.4.0/payment-core-1.4.0.jar
~/.m2/repository/com/acme/payment/payment-core/1.4.0/payment-core-1.4.0.pom
Use install when:
- developing multiple projects locally;
- testing a library consumer before publishing;
- debugging dependency resolution locally;
- working on a library and app split across repositories.
Do not use install as release.
install is private machine state.
It is not auditable across the organization.
mvn deploy
deploy uploads the artifact and POM to a configured remote repository.
Use deploy when:
- publishing snapshots to an internal snapshot repository;
- publishing release candidates to a staging repository;
- publishing internal libraries for other teams;
- publishing open-source artifacts to a public repository workflow;
- promoting validated artifacts into a release repository.
The Maven Deploy Plugin is primarily used in the deploy phase to add artifacts to a remote repository for sharing with other developers and projects.
4. What Gets Deployed?
A Maven project usually deploys more than a JAR.
A deploy may include:
- main artifact:
.jar,.war,.ear,.pom, etc.; - project POM;
- sources JAR;
- javadoc JAR;
- test JAR if attached;
- shaded artifact if configured;
- checksums generated by repository tooling;
- signatures if signing is enabled;
- metadata updates.
Example release artifact set:
payment-api-1.4.0.jar
payment-api-1.4.0.pom
payment-api-1.4.0-sources.jar
payment-api-1.4.0-javadoc.jar
payment-api-1.4.0.jar.asc
payment-api-1.4.0.pom.asc
payment-api-1.4.0-sources.jar.asc
payment-api-1.4.0-javadoc.jar.asc
For internal libraries, sources and javadocs are still useful.
They improve IDE navigation, debugging, and consumer understanding.
A top-tier Maven pipeline treats metadata as part of the artifact contract, not decoration.
5. distributionManagement
The normal Maven way to tell mvn deploy where to publish is distributionManagement.
Minimal example:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme.payment</groupId>
<artifactId>payment-api</artifactId>
<version>1.4.0</version>
<distributionManagement>
<repository>
<id>acme-releases</id>
<url>https://repo.acme.internal/repository/maven-releases</url>
</repository>
<snapshotRepository>
<id>acme-snapshots</id>
<url>https://repo.acme.internal/repository/maven-snapshots</url>
</snapshotRepository>
</distributionManagement>
</project>
The important pieces:
| Element | Meaning |
|---|---|
repository | Target repository for non-SNAPSHOT releases. |
snapshotRepository | Target repository for *-SNAPSHOT versions. |
id | Logical server identifier. Must match credentials in settings.xml. |
url | Remote repository URL. |
This split matters.
A release repository should be stable and immutable.
A snapshot repository is mutable by design.
If your organization publishes snapshots and releases into the same repository, you lose an important safety boundary.
6. The id Contract: POM Meets settings.xml
This is one of the most common Maven deploy failure points.
The POM contains repository identity:
<distributionManagement>
<repository>
<id>acme-releases</id>
<url>https://repo.acme.internal/repository/maven-releases</url>
</repository>
</distributionManagement>
The user's or CI's settings.xml contains credentials for that same id:
<settings>
<servers>
<server>
<id>acme-releases</id>
<username>${env.MAVEN_REPO_USERNAME}</username>
<password>${env.MAVEN_REPO_PASSWORD}</password>
</server>
</servers>
</settings>
The id is the join key.
If id differs by one character, Maven will not use the credentials you expect.
This is why enterprise teams standardize repository IDs.
Good:
company-releases
company-snapshots
company-staging
company-thirdparty
Bad:
repo
nexus
nexus-release
release-repo
internal-release
maven-release
companyReleaseRepo
When every project invents its own IDs, CI credential management becomes fragile.
7. Where Should distributionManagement Live?
There are three common patterns.
Pattern A — In every project POM
<distributionManagement>
...
</distributionManagement>
Works, but scales poorly.
Problems:
- duplicated repository URLs;
- inconsistent repository IDs;
- hard migration when repository manager URL changes;
- risk of publishing to wrong repository.
Use only for small standalone projects.
Pattern B — In parent POM
<parent>
<groupId>com.acme.build</groupId>
<artifactId>acme-parent</artifactId>
<version>2026.7.0</version>
</parent>
Parent contains:
<distributionManagement>
<repository>
<id>acme-releases</id>
<url>https://repo.acme.internal/repository/maven-releases</url>
</repository>
<snapshotRepository>
<id>acme-snapshots</id>
<url>https://repo.acme.internal/repository/maven-snapshots</url>
</snapshotRepository>
</distributionManagement>
This is the common enterprise pattern.
Pros:
- centralized publishing policy;
- consistent repository IDs;
- easier migration;
- fewer copy-paste errors.
Cons:
- parent POM change affects many projects;
- not suitable if different artifact families need different publishing flows;
- open-source publication may need a separate parent.
Pattern C — Use alternate deployment repository in CI
Maven Deploy Plugin supports alternate deployment repository parameters such as altDeploymentRepository.
Example:
mvn deploy \
-DaltDeploymentRepository=acme-staging::https://repo.acme.internal/repository/maven-staging
This is useful when:
- CI decides target repository dynamically;
- repository target depends on branch/tag;
- you need emergency rerouting;
- you want POMs not to contain deployment URLs;
- large-scale centralized deployments are controlled through CI/settings.
But do not use this casually.
If deployment target is invisible in the POM, you must make it very visible in CI logs and release documentation.
8. Snapshot vs Release Deployment
Maven version suffix decides which repository target is used.
1.4.0-SNAPSHOT -> snapshotRepository
1.4.0 -> repository
Snapshots are mutable.
Release versions must be immutable.
A snapshot artifact in the remote repository usually does not stay as plain 1.4.0-SNAPSHOT.jar. Repository metadata may resolve it to timestamped snapshot builds.
Conceptually:
payment-api-1.4.0-SNAPSHOT.jar
may map to deployed timestamped artifacts like:
payment-api-1.4.0-20260703.081512-3.jar
That lets Maven distinguish different deployed builds under one logical snapshot version.
But the consumer still declares:
<dependency>
<groupId>com.acme.payment</groupId>
<artifactId>payment-api</artifactId>
<version>1.4.0-SNAPSHOT</version>
</dependency>
This is powerful and dangerous.
A downstream build may resolve a different snapshot today than yesterday.
Use snapshots for integration velocity, not release certainty.
9. Snapshot Policy
A senior Maven setup defines snapshot policy explicitly.
Questions:
- Which branches can deploy snapshots?
- Are snapshots retained forever?
- Can snapshots enter production images?
- Can downstream release builds consume snapshots?
- Are snapshots scanned?
- Are snapshots replicated to disaster recovery repository?
- Are snapshots allowed across team boundaries?
Suggested policy:
| Consumer | Snapshot allowed? | Reason |
|---|---|---|
| Developer local build | Yes | Fast integration. |
| Feature branch CI | Yes | Integration testing. |
| Main branch CI | Maybe | Depends on organization discipline. |
| Release candidate build | No | Release must be deterministic. |
| Production image | No | Mutable dependency not acceptable. |
| Regulated/audited deployment | No | Artifact must be traceable and immutable. |
Enforce no-snapshot release builds with Maven Enforcer.
Example:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>release-no-snapshots</id>
<phase>validate</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireReleaseDeps>
<message>Release builds must not depend on SNAPSHOT artifacts.</message>
</requireReleaseDeps>
</rules>
</configuration>
</execution>
</executions>
</plugin>
The exact rule set may vary, but the invariant is stable:
A release build must not depend on mutable artifacts.
10. Repository Layout and Metadata
Maven remote repositories use a structured layout.
For coordinates:
groupId: com.acme.payment
artifactId: payment-api
version: 1.4.0
packaging: jar
Path:
com/acme/payment/payment-api/1.4.0/payment-api-1.4.0.jar
com/acme/payment/payment-api/1.4.0/payment-api-1.4.0.pom
Repository metadata also exists:
com/acme/payment/payment-api/maven-metadata.xml
com/acme/payment/payment-api/1.4.0-SNAPSHOT/maven-metadata.xml
Metadata supports version discovery, snapshot resolution, and repository indexing.
Operationally, corrupted metadata can break resolution even when the artifact file exists.
Deployment failures are not always JAR upload failures.
Sometimes the artifact upload succeeds but metadata update fails.
That is why repository manager health and transactional behavior matter.
11. The Deploy Plugin
Maven uses the Maven Deploy Plugin for deploy operations.
The main goals are:
| Goal | Use |
|---|---|
deploy:deploy | Deploy artifacts produced by the current Maven project. |
deploy:deploy-file | Deploy an external artifact not built by the current Maven project. |
Normally you do not call deploy:deploy directly.
You run:
mvn deploy
Maven reaches the deploy lifecycle phase and invokes the bound deploy plugin goal.
Pin the plugin version
Production parent POM:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
Properties:
<properties>
<maven-deploy-plugin.version>3.1.4</maven-deploy-plugin.version>
</properties>
Do not rely on implicit plugin versions in enterprise builds.
The deploy phase is too important to let Maven choose hidden defaults.
12. deployAtEnd for Multi-Module Builds
Multi-module deploy has a painful failure mode.
Suppose a reactor has 20 modules.
If Maven deploys each module immediately after its build and module 19 fails, modules 1–18 may already be deployed.
Now the remote repository contains a partial release.
That is dangerous.
deployAtEnd delays deployment until the end of a successful reactor build.
Example:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<deployAtEnd>true</deployAtEnd>
</configuration>
</plugin>
Mental model:
Use deployAtEnd for release-like multi-module deployments.
It reduces partial publication risk.
It does not replace repository staging.
If the network fails during final deploy, you can still get partial upload. A repository manager staging workflow is stronger.
13. Retry Behavior and Network Failure
Publishing is network I/O.
Network I/O fails.
The Deploy Plugin has retry-related configuration such as retryFailedDeploymentCount.
Example:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<retryFailedDeploymentCount>3</retryFailedDeploymentCount>
</configuration>
</plugin>
This can help with transient network issues.
But retries are not a release strategy.
You still need:
- repository manager availability;
- staging repository rollback/drop;
- idempotent CI behavior;
- immutable release repository;
- clear human procedure for partial uploads.
For snapshots, retry is usually acceptable.
For releases, prefer staging and promotion rather than direct deploy into final release repository.
14. Deploying External Artifacts with deploy:deploy-file
Sometimes you need to add a third-party artifact to an internal repository.
Example cases:
- vendor JAR not available in Maven Central;
- internal binary from a legacy build;
- generated artifact from a non-Maven build;
- artifact built by another tool but consumed by Maven projects.
Example:
mvn deploy:deploy-file \
-DgroupId=com.vendor.legacy \
-DartifactId=legacy-driver \
-Dversion=2.8.1 \
-Dpackaging=jar \
-Dfile=legacy-driver-2.8.1.jar \
-DrepositoryId=acme-thirdparty \
-Durl=https://repo.acme.internal/repository/thirdparty-releases
Use this carefully.
The moment you deploy an external artifact, your organization becomes responsible for its metadata quality.
Minimum metadata checklist:
- correct
groupId; - correct
artifactId; - correct version;
- license recorded somewhere;
- source/vendor provenance known;
- checksum recorded;
- vulnerability scanning possible;
- no accidental repackaging under misleading coordinates.
Bad pattern:
com.acme:some-jar:1.0
Good pattern:
com.vendor.product:vendor-client:2.8.1-acme1
If your organization modifies the binary, use a suffix or internal namespace to make that clear.
15. Repository Manager Publication Topology
A repository manager usually has multiple repository roles.
Common repository types:
| Repository | Type | Purpose |
|---|---|---|
maven-snapshots | hosted | Mutable internal development artifacts. |
maven-staging | hosted/staging | Release candidates waiting for validation. |
maven-releases | hosted | Immutable approved internal releases. |
maven-central-proxy | proxy | Cached external dependencies. |
maven-thirdparty | hosted | Approved vendor/manual artifacts. |
maven-public | group/virtual | Single URL for consumers. |
Do not let every build talk directly to Maven Central.
Use a repository manager as supply-chain boundary.
16. Direct Deploy vs Staged Release
Direct deploy
mvn deploy -> maven-releases
Simple.
Risky.
If the release repository is immutable, a bad release cannot be overwritten. That is good for audit, but bad if direct deploy published a broken artifact.
Staged release
mvn deploy -> staging repository -> validation -> promotion -> releases
Better.
It supports:
- artifact inspection;
- checksum/signature verification;
- SBOM attachment;
- vulnerability scanning;
- license checks;
- consumer smoke tests;
- manual approval if needed;
- atomic-ish promotion semantics.
Recommended enterprise flow:
The critical invariant:
A failed release candidate should be droppable without contaminating the final release repository.
17. Deployment Credentials
Do not put credentials in pom.xml.
Use settings.xml, CI secrets, or repository manager token mechanisms.
Bad:
<distributionManagement>
<repository>
<id>acme-releases</id>
<url>https://user:password@repo.acme.internal/repository/maven-releases</url>
</repository>
</distributionManagement>
Good:
<distributionManagement>
<repository>
<id>acme-releases</id>
<url>https://repo.acme.internal/repository/maven-releases</url>
</repository>
</distributionManagement>
CI settings.xml generated at runtime:
<settings>
<servers>
<server>
<id>acme-releases</id>
<username>${env.MAVEN_REPO_USERNAME}</username>
<password>${env.MAVEN_REPO_TOKEN}</password>
</server>
</servers>
</settings>
Also consider:
- use short-lived tokens where possible;
- separate snapshot deploy credentials from release deploy credentials;
- prevent developer accounts from deploying releases directly;
- use service accounts with least privilege;
- rotate tokens;
- prevent secrets from appearing in logs;
- scan repository for leaked credentials.
18. CI-Safe settings.xml
Example generated CI settings file:
<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">
<mirrors>
<mirror>
<id>acme-public</id>
<mirrorOf>*</mirrorOf>
<url>https://repo.acme.internal/repository/maven-public</url>
</mirror>
</mirrors>
<servers>
<server>
<id>acme-snapshots</id>
<username>${env.MAVEN_REPO_USERNAME}</username>
<password>${env.MAVEN_REPO_TOKEN}</password>
</server>
<server>
<id>acme-releases</id>
<username>${env.MAVEN_REPO_USERNAME}</username>
<password>${env.MAVEN_REPO_TOKEN}</password>
</server>
<server>
<id>acme-staging</id>
<username>${env.MAVEN_REPO_USERNAME}</username>
<password>${env.MAVEN_REPO_TOKEN}</password>
</server>
</servers>
</settings>
CI command:
mvn --batch-mode \
--settings .ci/maven-settings.xml \
--no-transfer-progress \
clean deploy
Use --batch-mode in CI.
Use --no-transfer-progress to reduce noisy logs.
Keep settings generation explicit and visible.
19. The install-file vs deploy-file Difference
For external artifacts:
| Command | Target | Scope |
|---|---|---|
install:install-file | local repository | Only this machine/runner. |
deploy:deploy-file | remote repository | Shared by consumers. |
Example local test:
mvn install:install-file \
-DgroupId=com.vendor.legacy \
-DartifactId=legacy-driver \
-Dversion=2.8.1 \
-Dpackaging=jar \
-Dfile=legacy-driver-2.8.1.jar
Then deploy only after metadata review:
mvn deploy:deploy-file \
-DgroupId=com.vendor.legacy \
-DartifactId=legacy-driver \
-Dversion=2.8.1 \
-Dpackaging=jar \
-Dfile=legacy-driver-2.8.1.jar \
-DrepositoryId=acme-thirdparty \
-Durl=https://repo.acme.internal/repository/thirdparty-releases
Do not skip local install testing.
If Maven cannot consume the artifact locally, publishing it remotely only spreads the problem.
20. POM Quality for Published Artifacts
A deployed artifact's POM is consumed by downstream builds.
Bad POM metadata can break consumers.
Important fields:
<groupId>com.acme.payment</groupId>
<artifactId>payment-api</artifactId>
<version>1.4.0</version>
<packaging>jar</packaging>
<name>Acme Payment API</name>
<description>Stable API contracts for the Acme payment domain.</description>
<url>https://git.acme.internal/payment/payment-api</url>
For open-source/public artifacts, also include:
- license;
- developers;
- SCM;
- issue management;
- organization;
- distribution management required by publishing flow.
For internal enterprise artifacts, still include at least:
- clear name;
- description;
- SCM link;
- owner/team metadata where supported;
- accurate dependencies;
- no accidental test dependencies in compile scope;
- no private repositories leaking into consumer POM unless intended.
Consumer POM hygiene matters.
When you publish a library, your POM becomes part of other teams' build graph.
21. Avoid Repositories in Published Library POMs
A library POM that declares random repositories can affect consumers.
Example smell:
<repositories>
<repository>
<id>temporary-vendor-repo</id>
<url>https://vendor.example.com/temp/maven</url>
</repository>
</repositories>
Why risky?
- consumers inherit resolution behavior;
- builds may reach unapproved external endpoints;
- supply-chain boundary is weakened;
- repository outage can break consumers;
- internal repository policy becomes bypassable.
Enterprise rule:
Library POMs should generally not declare arbitrary external repositories.
Resolve all external dependencies through the organization's repository manager.
Use mirrors in settings.xml to route traffic.
22. Publishing Parent POMs and BOMs
Parent POMs and BOMs are artifacts too.
They usually have packaging pom.
Parent POM:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme.build</groupId>
<artifactId>acme-service-parent</artifactId>
<version>2026.7.0</version>
<packaging>pom</packaging>
</project>
BOM:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme.platform</groupId>
<artifactId>acme-platform-bom</artifactId>
<version>2026.7.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
...
</dependencies>
</dependencyManagement>
</project>
Publishing order matters.
If a service release depends on parent 2026.7.0 and BOM 2026.7.0, those must be available before the service can build cleanly in a fresh environment.
Recommended platform release order:
1. build parent POM
2. publish parent POM to staging/releases
3. build platform BOM
4. publish platform BOM
5. build service/library projects using parent + BOM
If parent and BOM live in the same multi-module reactor, ordering is handled by the reactor only if dependencies/parent relationships are expressed correctly.
23. Publishing Multi-Module Projects
Example enterprise reactor:
payment-platform/
pom.xml # aggregator
payment-parent/ # packaging pom
payment-bom/ # packaging pom
payment-api/ # jar
payment-domain/ # jar
payment-app/ # jar or war
Publication strategy:
| Module | Publish? | Reason |
|---|---|---|
| aggregator root | Usually no | Often just local build orchestration. |
| parent POM | Yes | Needed by child builds if used externally. |
| BOM | Yes | Needed for version alignment. |
| API module | Yes | Consumed by other services. |
| domain module | Maybe | Publish only if cross-project reuse is intended. |
| app module | Maybe | Publish if deployment pipeline consumes artifact from Maven repository. |
Do not publish every module by default.
Publish artifacts that represent a stable reuse or deployment boundary.
For modules that should not deploy:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
Use sparingly and document why.
24. Artifact Promotion vs Artifact Rebuild
A common release engineering mistake:
CI build #1: validate artifact
CI build #2: rebuild same version and deploy artifact
This breaks provenance.
The deployed artifact is not the exact artifact that was tested.
Better:
CI build #1:
build artifact
test artifact
deploy to staging
scan staging artifact
promote same artifact
Or:
CI build #1:
build artifact
test artifact
store CI artifact
CI release job:
publish exact stored artifact
But Maven deploy normally publishes from project build outputs. If you rebuild during release, you need strong reproducible-build discipline.
Best invariant:
The artifact promoted to release is the artifact that passed the gates.
This is easier with repository staging than with ad-hoc rebuilds.
25. Deployment Command Patterns
Snapshot branch deployment
mvn --batch-mode --no-transfer-progress clean deploy
Conditions:
- version ends in
-SNAPSHOT; - target is snapshot repository;
- branch is allowed to publish snapshots;
- CI credentials have snapshot deploy permission only.
Release candidate staging
mvn --batch-mode --no-transfer-progress clean deploy \
-DskipTests=false \
-Dgpg.skip=false
Conditions:
- version is release version, not snapshot;
- target is staging repository;
- CI has staging deploy permission;
- release gates run after deploy.
Emergency no-deploy verification
mvn --batch-mode --no-transfer-progress clean verify
Use when investigating failures without changing remote repositories.
Deploy one module and required dependencies
mvn --batch-mode clean deploy -pl payment-api -am
Be careful.
Partial deployment from a multi-module project may publish artifacts without the full release context.
Use only for snapshot or explicitly scoped internal builds.
26. Permissions Model
A serious Maven repository has role-separated permissions.
| Role | Allowed |
|---|---|
| Developer | Read group repo, deploy snapshots maybe. |
| CI snapshot publisher | Deploy snapshots. |
| CI staging publisher | Deploy to staging. |
| Release manager/service | Promote staging to releases. |
| Repository admin | Manage repositories and cleanup policy. |
| Security/scanner | Read artifacts and metadata. |
Avoid one all-powerful Maven credential.
Bad:
MAVEN_USERNAME=admin
MAVEN_PASSWORD=...
Good:
snapshot-publisher token -> maven-snapshots deploy only
release-publisher token -> maven-staging deploy only
promoter token -> staging promote/drop only
reader token -> read group repository only
This reduces blast radius.
27. Cleanup and Retention Policy
Snapshots grow quickly.
Define retention:
| Artifact type | Retention suggestion |
|---|---|
| Feature branch snapshots | Short retention, e.g. days/weeks. |
| Main branch snapshots | Longer retention, e.g. weeks/months. |
| Release candidates | Retain if linked to release audit; otherwise cleanup failed candidates. |
| Releases | Immutable and long-lived. |
| Third-party approved artifacts | Long-lived with provenance metadata. |
Do not rely on manual cleanup.
Repository managers should enforce retention policy.
For Maven, metadata cleanup matters too.
If snapshots are deleted but metadata points to missing files, consumers may fail oddly.
28. Deployment Failure Modes
401 Unauthorized
Likely causes:
server.idmismatch;- missing CI secret;
- expired token;
- wrong repository URL;
- repository requires token instead of password;
- mirror/server config confusion.
Diagnostic path:
mvn help:effective-settings
mvn -X deploy
Do not print secrets in logs.
Check whether Maven is using the expected settings file.
403 Forbidden
Likely causes:
- credentials valid but insufficient permission;
- trying to deploy release into snapshot repository;
- trying to overwrite immutable release;
- path or namespace not allowed;
- repository manager policy blocks artifact.
409 Conflict
Likely causes:
- release version already exists;
- immutable repository rejects overwrite;
- staging repository already closed;
- concurrent release job used same version.
Correct response:
- do not delete and overwrite release casually;
- bump version or investigate why version was reused;
- preserve audit trail.
Metadata failure
Symptoms:
- artifact file exists but consumers cannot resolve latest;
- snapshot resolution fails;
- repository metadata inconsistent.
Actions:
- check repository manager logs;
- rebuild metadata if supported;
- avoid manual filesystem edits;
- rerun deploy only if idempotent and policy allows.
Partial multi-module deployment
Symptoms:
- some modules exist in remote repository;
- later modules missing;
- consumers fail depending on graph path.
Mitigation:
- use
deployAtEnd; - use staging repository;
- drop failed staging repository;
- avoid direct release repository deploy.
29. Release Deployment Checklist
Before deploy:
- version is non-SNAPSHOT;
- no SNAPSHOT dependencies;
- parent POM resolvable from clean environment;
- BOM resolvable from clean environment;
- plugin versions pinned;
- toolchain controlled;
- tests pass with
mvn verify; - artifact content inspected;
- generated sources deterministic;
- secrets not in artifact;
distributionManagementtarget correct;- CI
settings.xmluses correct server IDs; - release notes/changelog generated if required.
During deploy:
- run with
--batch-mode; - use CI, not laptop;
- use release credential with least privilege;
- publish to staging first if possible;
- preserve logs;
- capture artifact coordinates;
- capture commit SHA and build number.
After deploy:
- verify artifact is resolvable from clean local repo;
- run consumer smoke test;
- scan artifact/SBOM;
- close/promote staging repository;
- tag source if not already tagged;
- announce coordinates;
- monitor downstream failures.
30. Clean-Room Deployment Verification
After publishing, test as a consumer.
rm -rf /tmp/maven-clean-room
mkdir -p /tmp/maven-clean-room
mvn --batch-mode \
-Dmaven.repo.local=/tmp/maven-clean-room \
--settings .ci/maven-settings.xml \
dependency:get \
-Dartifact=com.acme.payment:payment-api:1.4.0
Then test a small consumer project:
<dependency>
<groupId>com.acme.payment</groupId>
<artifactId>payment-api</artifactId>
<version>1.4.0</version>
</dependency>
Run:
mvn -Dmaven.repo.local=/tmp/maven-consumer-repo clean verify
This catches:
- missing transitive dependency;
- broken POM;
- repository visibility issue;
- missing parent POM;
- wrong scope;
- artifact deployed to wrong repository;
- credential/mirror issue.
31. Enterprise Distribution Management Blueprint
A strong internal parent POM may include:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.acme.build</groupId>
<artifactId>acme-service-parent</artifactId>
<version>2026.7.0</version>
<packaging>pom</packaging>
<distributionManagement>
<repository>
<id>acme-releases</id>
<name>Acme Maven Releases</name>
<url>https://repo.acme.internal/repository/maven-releases</url>
</repository>
<snapshotRepository>
<id>acme-snapshots</id>
<name>Acme Maven Snapshots</name>
<url>https://repo.acme.internal/repository/maven-snapshots</url>
</snapshotRepository>
</distributionManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<deployAtEnd>true</deployAtEnd>
<retryFailedDeploymentCount>3</retryFailedDeploymentCount>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
This gives child projects a consistent publishing default.
For staging-based release, CI may override:
mvn deploy \
-DaltDeploymentRepository=acme-staging::https://repo.acme.internal/repository/maven-staging
But that override must be controlled and visible.
32. Anti-Patterns
Anti-pattern: publishing from developer laptop
Problem:
- non-reproducible local environment;
- unmanaged credentials;
- unrecorded provenance;
- possible uncommitted changes;
- inconsistent JDK/Maven version.
Fix:
- publish only from CI;
- use clean checkout;
- record commit SHA;
- use controlled toolchain;
- use service credentials.
Anti-pattern: overwriting releases
Problem:
- consumers cannot trust coordinates;
- audit trail broken;
- rollback ambiguous;
- caches may contain different bytes for same version.
Fix:
- immutable release repository;
- version bump for every change;
- repository manager blocks redeploy.
Anti-pattern: publishing everything
Problem:
- internal implementation modules become accidental API;
- downstream coupling increases;
- refactoring becomes hard;
- repository clutter grows.
Fix:
- define publication boundary;
- deploy only stable modules;
- skip deploy for private modules.
Anti-pattern: POM contains credentials
Problem:
- credential leak;
- rotated secrets require source change;
- forks inherit credentials;
- logs may expose secrets.
Fix:
- use
settings.xml+ CI secrets; - use repository tokens;
- never include credentials in URLs.
Anti-pattern: release from snapshots
Problem:
- release can change without version change;
- build cannot be reproduced;
- downstream incident root cause becomes unclear.
Fix:
- enforce no snapshot dependencies for release;
- use release BOM;
- use immutable repository.
33. Senior-Level Diagnostic Commands
Show effective deploy config
mvn help:effective-pom
Search for:
distributionManagement
maven-deploy-plugin
Show effective settings
mvn help:effective-settings
Search for:
servers
mirrors
profiles
Be careful not to expose secrets.
Debug deploy
mvn -X --batch-mode deploy
Use only in secure logs because debug output may reveal sensitive context.
Verify remote resolution
mvn dependency:get -Dartifact=com.acme.payment:payment-api:1.4.0
Use clean local repository
mvn -Dmaven.repo.local=/tmp/m2-clean clean verify
This proves the build does not depend on polluted local cache.
34. Decision Matrix
| Situation | Recommended action |
|---|---|
| Need to test library locally | mvn install, consume from local repo. |
| Need to share unstable integration artifact | Deploy -SNAPSHOT to snapshot repo. |
| Need to release internal stable library | Deploy to staging, scan, promote to releases. |
| Need to publish public OSS artifact | Follow public repository requirements: POM metadata, signing, sources, javadocs. |
| Need to add vendor JAR | Use controlled deploy:deploy-file into third-party repository. |
| Need to avoid partial multi-module publish | Use deployAtEnd plus staging. |
| Need branch-specific target repo | Use CI-controlled altDeploymentRepository. |
| Need reproducible audit trail | Publish from CI, capture commit SHA, artifact checksum, build logs. |
35. Practical Implementation Exercise
Create a three-module project:
acme-payment/
pom.xml
payment-parent/
payment-bom/
payment-api/
Tasks:
- Configure parent POM with
distributionManagement. - Configure Deploy Plugin version in
pluginManagement. - Enable
deployAtEnd. - Generate CI
settings.xmlfrom environment variables. - Run clean-room
mvn verify. - Run
mvn deployto a local test repository manager. - Consume the deployed artifact from a separate sample project.
- Break server ID intentionally and observe 401.
- Try redeploying the same release version into immutable repo and observe conflict.
- Add
-SNAPSHOTversion and observe snapshot repository routing.
The exercise is not complete until you can explain every remote request Maven made.
36. Final Mental Model
Deployment is publication.
Publication creates shared dependency state.
Shared dependency state must be governed.
The production-grade Maven deployment model is:
The invariant:
Do not publish artifacts you cannot explain, reproduce, trace, and revoke from promotion.
References
- Apache Maven Deploy Plugin — Introduction and usage: https://maven.apache.org/plugins/maven-deploy-plugin/
- Apache Maven Deploy Plugin —
deploy:deploy: https://maven.apache.org/plugins/maven-deploy-plugin/deploy-mojo.html - Apache Maven Deploy Plugin —
deploy:deploy-file: https://maven.apache.org/plugins/maven-deploy-plugin/deploy-file-mojo.html - Apache Maven POM Reference —
distributionManagement: https://maven.apache.org/pom.html - Apache Maven Settings Reference — servers, mirrors, repositories: https://maven.apache.org/settings.html
- Apache Maven Guide to Large Scale Centralized Deployments: https://maven.apache.org/guides/mini/guide-large-scale-centralized-deployments.html
You just completed lesson 31 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.