W3docs

Java Maven Build Lifecycle

Maven's default lifecycle phases — validate, compile, test, package, verify, install, deploy.

Java Maven Build Lifecycle

A lifecycle is Maven's central idea: building a project is not an arbitrary pile of tasks but an ordered sequence of well-known phases. When you type mvn package, you are not naming one action — you are asking Maven to run every phase up to and including package, in order. Understanding that sequence, and how plugin goals attach to it, is the difference between cargo-culting commands and actually knowing what your build does.

Three lifecycles, and the one you use most

Maven ships three built-in lifecycles. They are independent — invoking a phase of one does not trigger another.

LifecyclePurposeKey phases
cleanRemove build outputpre-clean, clean, post-clean
defaultBuild and deploy the projectvalidatecompiletestpackageinstalldeploy
siteGenerate project documentationpre-site, site, site-deploy

The default lifecycle is where the real work happens. That mvn clean install you see everywhere is simply two lifecycles in one command: the clean phase from the clean lifecycle, then the install phase from the default lifecycle.

The phases of the default lifecycle

The default lifecycle has 23 phases, but seven carry the weight of almost every build. They always run in this order:

PhaseWhat it does
validateCheck the project is correct and all needed info is available
compileCompile the project's main source code
testRun unit tests with a suitable framework (does not require packaging)
packageBundle compiled code into a distributable format, e.g. a JAR
verifyRun checks on integration-test results to confirm quality
installCopy the package into the local repository (~/.m2) for other local projects
deployUpload the package to a remote repository for sharing

The rule that governs everything: running a phase runs that phase and all phases before it. So mvn package quietly runs validate, compile, and test first. There is no way to "skip ahead" — you can only stop earlier.

mvn validate     # just the sanity checks
mvn compile      # validate -> compile
mvn test         # validate -> compile -> test
mvn package      # ... -> test -> package (produces target/app-1.0.jar)
mvn install      # ... -> package -> verify -> install (now in ~/.m2)
mvn deploy       # the full pipeline, ending with an upload

Phases are empty until plugins bind goals to them

A phase is just a name and a position in the sequence — it does no work by itself. The work is done by plugin goals that are bound to phases. A goal is written plugin:goal, e.g. compiler:compile. For a project whose <packaging> is jar, Maven supplies a sensible default set of bindings:

PhaseDefault bound goal
compilemaven-compiler-plugin:compile
testmaven-surefire-plugin:test
packagemaven-jar-plugin:jar
installmaven-install-plugin:install
deploymaven-deploy-plugin:deploy

You can add your own bindings in pom.xml. Here the exec plugin is wired to the verify phase so it runs automatically near the end of the build:

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.1.0</version>
      <executions>
        <execution>
          <id>smoke-test</id>
          <phase>verify</phase>
          <goals><goal>java</goal></goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

You can also invoke a goal directly, outside any phase — mvn compiler:compile runs only that goal, skipping the lifecycle entirely. That is occasionally useful, but day to day you call phases and let the bindings fire.

A worked example: simulating the lifecycle

Maven itself is not installed on this runner, so we model the lifecycle in plain Java to make its rules concrete. The program lists the default phases, records the plugin:goal bound to each, then "runs" mvn install — executing every phase from validate up to install, and proving that deploy and clean are not pulled in.

java— editable, runs on the server

What to take from the run:

  • The output starts at validate and stops at install — six phases for one command. That is the cumulative rule in action: mvn install is shorthand for running the whole prefix of the sequence up to install, never just the single named phase.
  • Each executed phase printed the plugin:goal bound to it (compile -> compiler:compile, package -> jar:jar, and so on). Phases are slots; the goals are what actually compile, test, and bundle. validate printed (no goal bound), showing a phase can run yet do nothing.
  • deploy skipped : true confirms phases after the one you request never run. To publish to a remote repository you must explicitly ask for mvn deploy; a plain install deliberately stays local.
  • clean ran : false proves clean belongs to a separate lifecycle. Invoking install does not delete target/, which is exactly why mvn clean install spells out both — one phase from each lifecycle.
  • The phases ran in the fixed order validate, compile, test, package, verify, install and the program ended with BUILD SUCCESS. The ordering is not negotiable; Maven's convention-over-configuration promise rests on this guaranteed, repeatable sequence.

Common commands in practice

A handful of invocations cover almost all daily work:

mvn clean              # delete target/
mvn test               # build and run unit tests
mvn package -DskipTests  # build the JAR without running tests
mvn clean install      # fresh build, install to local ~/.m2
mvn clean verify       # CI's favourite: build + unit + integration checks

-DskipTests compiles tests but does not run them; -Dmaven.test.skip=true skips compiling them too. Reach for the first when tests are slow but should still build, the second only when you truly want them out of the way.

Practice

Practice

In a standard Maven JAR project, what does running 'mvn package' do?