Back to Maven & Gradle
Q3Question 3 of 4

Maven lifecycle phases, `mvn install` vs `mvn deploy`, and `distributionManagement`

Key Concepts
  • The Maven default lifecycle is a fixed ordered chain: validate, compile, test, package, verify, install, deploy -- invoking any phase runs all earlier ones.
  • mvn install builds the artifact and places it into your local .m2 repository so only projects on the same machine can use it.
  • mvn deploy does everything install does and then uploads the artifact to a remote shared repository (Nexus or Artifactory).
  • distributionManagement in the POM configures the release and snapshot repository URLs that the deploy phase publishes to.
  • Snapshot versions end in -SNAPSHOT and can be re-published repeatedly; release versions are immutable once published.
  • Credentials for the remote repository are stored in settings.xml, never hard-coded in the POM.
  • In a disciplined setup, only the CI server runs mvn deploy so only reviewed, tested artifacts reach the shared repository.

Definition

The lifecycle runs validate through deploy in order; install publishes to your local .m2, deploy publishes to the shared remote repo configured by distributionManagement.
  • Maven default lifecycle -- a fixed ordered sequence of phases where invoking any phase executes all phases before it.
  • validate -- checks that the project is correct and all necessary information is available.
  • compile -- compiles main source code into target/classes.
  • test -- runs unit tests; if they fail, the build stops.
  • package -- bundles compiled code into a distributable format (JAR, WAR).
  • verify -- runs integration tests and quality checks.
  • install -- copies the artifact into the local .m2 repository on your machine.
  • deploy -- uploads the artifact to a remote shared repository (Nexus, Artifactory) configured by distributionManagement.

Think of baking a cake for National Bank of India branches. The lifecycle is the fixed recipe: check ingredients (validate), mix (compile), taste-test (test), box it (package), final quality inspection (verify), then distribute. mvn install is putting the finished cake in your own kitchen fridge so only your household can eat it -- local and private. mvn deploy is sending the cake to the central bakery warehouse so every branch in the country can collect it -- shared and organizational. The distributionManagement section is the warehouse address and delivery pass.

Understanding the Concepts

What are the lifecycle phases in order?

The Maven default lifecycle is a fixed, ordered chain. The golden rule is that invoking a phase runs that phase and all phases before it. Here are the main phases in order: validate, compile, test, package, verify, install, deploy.

BASH
# Running package also runs validate, compile, and test
mvn clean package
TEXT
[INFO] --- maven-clean-plugin:3.3.2:clean ---
[INFO] --- maven-resources-plugin:3.3.1:resources ---
[INFO] --- maven-compiler-plugin:3.11.0:compile ---
[INFO] Compiling 8 source files to target/classes
[INFO] --- maven-resources-plugin:3.3.1:testResources ---
[INFO] --- maven-compiler-plugin:3.11.0:testCompile ---
[INFO] --- maven-surefire-plugin:3.2.2:test ---
[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0
[INFO] --- maven-jar-plugin:3.3.0:jar ---
[INFO] Building jar: target/banking-common-1.1.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS

What does each phase do?

Validate checks that the POM is well-formed and required properties exist. Compile compiles main source code into target/classes. Test runs unit tests with Surefire and stops the build if any test fails. Package bundles the compiled code into a JAR or WAR in target. Verify runs integration tests and quality checks. Install copies the packaged artifact into the local .m2 repository. Deploy uploads it to a remote shared repository.

JAVA
package com.bank.common;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Shared utility for interest calculation across all NBI services.
 * This is the library that gets installed/deployed.
 */
public class InterestCalculator {

    public static BigDecimal simpleInterest(
            BigDecimal principal, BigDecimal annualRate, int months) {
        return principal
            .multiply(annualRate)
            .multiply(BigDecimal.valueOf(months))
            .divide(BigDecimal.valueOf(1200), 2, RoundingMode.HALF_UP);
    }
}
BASH
# Compile only
mvn compile

# Compile + test (build stops if tests fail)
mvn test

# Compile + test + package
mvn package

# Compile + test + package + verify (integration tests + quality gate)
mvn verify

What is the difference between mvn install and mvn deploy?

mvn install is local and private. It copies the built artifact into your machine's .m2 repository, so only projects on that same machine can resolve it as a dependency. This is perfect during local development when you build a shared library and want your other local project to use it immediately.

BASH
# Install to local .m2
mvn clean install
TEXT
[INFO] --- maven-install-plugin:3.1.1:install ---
[INFO] Installing target/banking-common-1.1.0-SNAPSHOT.jar
[INFO]   to /Users/rajesh/.m2/repository/com/bank/common/banking-common/1.1.0-SNAPSHOT/banking-common-1.1.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS

mvn deploy is remote and shared. After doing everything install does, it uploads the artifact to a remote repository manager like Nexus or Artifactory. Now every developer and every CI pipeline in the organization can depend on that version.

BASH
# Deploy to shared repository (runs all phases including install first)
mvn clean deploy
TEXT
[INFO] --- maven-deploy-plugin:3.1.1:deploy ---
[INFO] Uploading banking-common-1.1.0-SNAPSHOT.jar to
[INFO]   https://nexus.nbi.com/repository/maven-snapshots/
[INFO] BUILD SUCCESS

What is distributionManagement?

The distributionManagement section in the POM configures where the deploy phase publishes artifacts. It defines a repository for releases and a snapshotRepository for snapshots. The id links to credentials stored in settings.xml, never in the POM.

XML
<!-- pom.xml --syn-kw">>
syn-kw"><projectsyn-kw">>
  syn-kw"><groupIdsyn-kw">>com.bank.commonsyn-kw"></groupIdsyn-kw">>
  syn-kw"><artifactIdsyn-kw">>banking-commonsyn-kw"></artifactIdsyn-kw">>
  syn-kw"><versionsyn-kw">>1.1.0-SNAPSHOTsyn-kw"></versionsyn-kw">>

  syn-kw"><distributionManagementsyn-kw">>
    syn-kw"><repositorysyn-kw">>
      syn-kw"><idsyn-kw">>nbi-releasessyn-kw"></idsyn-kw">>
      syn-kw"><urlsyn-kw">>https://nexus.nbi.com/repository/maven-releases/syn-kw"></urlsyn-kw">>
    syn-kw"></repositorysyn-kw">>
    syn-kw"><snapshotRepositorysyn-kw">>
      syn-kw"><idsyn-kw">>nbi-snapshotssyn-kw"></idsyn-kw">>
      syn-kw"><urlsyn-kw">>https://nexus.nbi.com/repository/maven-snapshots/syn-kw"></urlsyn-kw">>
    syn-kw"></snapshotRepositorysyn-kw">>
  syn-kw"></distributionManagementsyn-kw">>
syn-kw"></projectsyn-kw">>
XML
<!-- settings.xml (in ~/.m2/) -- credentials stored here, not in POM --syn-kw">>
syn-kw"><settingssyn-kw">>
  syn-kw"><serverssyn-kw">>
    syn-kw"><serversyn-kw">>
      syn-kw"><idsyn-kw">>nbi-releasessyn-kw"></idsyn-kw">>
      syn-kw"><usernamesyn-kw">>ci-deployersyn-kw"></usernamesyn-kw">>
      syn-kw"><passwordsyn-kw">>${env.NEXUS_PASSWORD}syn-kw"></passwordsyn-kw">>
    syn-kw"></serversyn-kw">>
    syn-kw"><serversyn-kw">>
      syn-kw"><idsyn-kw">>nbi-snapshotssyn-kw"></idsyn-kw">>
      syn-kw"><usernamesyn-kw">>ci-deployersyn-kw"></usernamesyn-kw">>
      syn-kw"><passwordsyn-kw">>${env.NEXUS_PASSWORD}syn-kw"></passwordsyn-kw">>
    syn-kw"></serversyn-kw">>
  syn-kw"></serverssyn-kw">>
syn-kw"></settingssyn-kw">>

What is the difference between snapshot and release versions?

A version ending in -SNAPSHOT, like 1.1.0-SNAPSHOT, is a work-in-progress that can be re-published repeatedly. Maven appends a timestamp to each snapshot upload. A plain version like 1.0.0 is an immutable release that should never change once published. Treating released versions as immutable is critical for audits.

XML
<!-- During development: snapshot --syn-kw">>
syn-kw"><versionsyn-kw">>1.1.0-SNAPSHOTsyn-kw"></versionsyn-kw">>

<!-- After release: immutable --syn-kw">>
syn-kw"><versionsyn-kw">>1.1.0syn-kw"></versionsyn-kw">>
BASH
# Snapshot can be re-deployed many times
mvn deploy
# Uploads: banking-common-1.1.0-20240115.103045-1.jar

mvn deploy
# Uploads: banking-common-1.1.0-20240115.143022-2.jar

# Release is deployed exactly once
# Attempting to re-deploy 1.1.0 to a release repo should fail

How does mvn deploy fit in a CI pipeline?

In a disciplined setup, developers rarely run deploy from their laptops. The CI server runs mvn deploy after tests pass on the main branch, so only reviewed, tested artifacts reach the shared repository.

YAML
# .github/workflows/deploy.yml
name: Deploy Library
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
      - run: mvn clean deploy -s .mvn/settings.xml
        env:
          NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}

What are common mistakes?

The most common mistakes are hard-coding credentials in the POM, re-deploying a release version to overwrite it, running deploy from a laptop against the shared repository without review, and confusing install with deploy.

XML
<!-- WRONG -- credentials in POM (visible to everyone who reads the POM) --syn-kw">>
syn-kw"><distributionManagementsyn-kw">>
  syn-kw"><repositorysyn-kw">>
    syn-kw"><idsyn-kw">>releasessyn-kw"></idsyn-kw">>
    syn-kw"><urlsyn-kw">>https://nexus.nbi.com/repository/maven-releases/syn-kw"></urlsyn-kw">>
    syn-kw"><usernamesyn-kw">>adminsyn-kw"></usernamesyn-kw">>
    syn-kw"><passwordsyn-kw">>secret123syn-kw"></passwordsyn-kw">>  <!-- NEVER do this --syn-kw">>
  syn-kw"></repositorysyn-kw">>
syn-kw"></distributionManagementsyn-kw">>

<!-- CORRECT -- credentials in settings.xml, referenced by id --syn-kw">>
syn-kw"><distributionManagementsyn-kw">>
  syn-kw"><repositorysyn-kw">>
    syn-kw"><idsyn-kw">>nbi-releasessyn-kw"></idsyn-kw">>
    syn-kw"><urlsyn-kw">>https://nexus.nbi.com/repository/maven-releases/syn-kw"></urlsyn-kw">>
  syn-kw"></repositorysyn-kw">>
syn-kw"></distributionManagementsyn-kw">>

What should a senior engineer know?

A senior engineer knows that release immutability is a governance and audit requirement. If version 1.0.0 of a payments library could be silently overwritten, a security audit could never trust that the artifact in production matches what was reviewed. They know the Maven release plugin automates the strip-SNAPSHOT, tag, deploy, bump-to-next-SNAPSHOT workflow, and that the enforcer plugin can prevent snapshot dependencies in release builds.

Visual Overview

DIAGRAM
  Maven Lifecycle Phases (ordered)
  ────────────────────────────────

  ┌──────────┐  ┌─────────┐  ┌──────┐  ┌─────────┐  ┌────────┐  ┌─────────┐  ┌────────┐
  │ validate  │─▶│ compile  │─▶│ test  │─▶│ package  │─▶│ verify  │─▶│ install  │─▶│ deploy  │
  └──────────┘  └─────────┘  └──────┘  └─────────┘  └────────┘  └─────────┘  └────────┘
       POM ok?    src->class   JUnit     JAR/WAR     integ tests   local .m2    remote Nexus

  mvn install vs mvn deploy:
  ┌───────────────────────────────────────────────────────┐
  │                                                       │
  │  mvn install                                          │
  │  ┌────────┐     ┌───────────────────────┐             │
  │  │  build  │────▶│ ~/.m2/repository/     │  LOCAL      │
  │  │  JAR    │     │ banking-common-1.1.0  │  only this  │
  │  └────────┘     └───────────────────────┘  machine    │
  │                                                       │
  │  mvn deploy                                           │
  │  ┌────────┐     ┌───────────────────────┐             │
  │  │  build  │────▶│ ~/.m2/repository/     │  LOCAL      │
  │  │  JAR    │     └───────────────────────┘             │
  │  │         │     ┌───────────────────────┐             │
  │  │         │────▶│ Nexus / Artifactory    │  SHARED     │
  │  │         │     │ everyone can download  │  everyone   │
  │  └────────┘     └───────────────────────┘             │
  │                                                       │
  └───────────────────────────────────────────────────────┘

  Snapshot vs Release:
  ┌────────────────────────────┐   ┌────────────────────────────┐
  │ 1.1.0-SNAPSHOT              │   │ 1.1.0 (release)             │
  │ Can be re-published         │   │ Immutable once published    │
  │ Timestamped uploads         │   │ Overwrite should be blocked │
  │ Goes to snapshotRepository  │   │ Goes to repository          │
  └────────────────────────────┘   └────────────────────────────┘

Key Takeaway

The Maven default lifecycle is a fixed ordered sequence of phases -- validate, compile, test, package, verify, install, deploy -- and the key rule is that invoking any phase runs all phases before it. So when you run deploy, Maven quietly runs everything from validate through install first, giving you a single command that does the complete build.

The difference between install and deploy is scope and audience. mvn install copies the built artifact into your local .m2 repository so only projects on the same machine can use it, which is perfect for local development. mvn deploy does everything install does and then uploads the artifact to a shared repository like Nexus or Artifactory, so every developer and CI pipeline in the organization can depend on that version.

The distributionManagement section tells deploy where to publish, with separate URLs for release and snapshot repositories. Snapshot versions can be re-published repeatedly for work in progress, while release versions are immutable once published. Credentials are stored in settings.xml, never in the POM.

Remember one sentence: install publishes to your local .m2 for private use, deploy publishes to the shared Nexus or Artifactory for the whole organization, and release versions are immutable because a bank's audit must trust that what runs in production matches what was reviewed.

---

Share
Use ← → arrow keys to navigate