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.
| Lifecycle | Purpose | Key phases |
|---|---|---|
clean | Remove build output | pre-clean, clean, post-clean |
default | Build and deploy the project | validate … compile … test … package … install … deploy |
site | Generate project documentation | pre-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:
| Phase | What it does |
|---|---|
validate | Check the project is correct and all needed info is available |
compile | Compile the project's main source code |
test | Run unit tests with a suitable framework (does not require packaging) |
package | Bundle compiled code into a distributable format, e.g. a JAR |
verify | Run checks on integration-test results to confirm quality |
install | Copy the package into the local repository (~/.m2) for other local projects |
deploy | Upload 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 uploadPhases 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:
| Phase | Default bound goal |
|---|---|
compile | maven-compiler-plugin:compile |
test | maven-surefire-plugin:test |
package | maven-jar-plugin:jar |
install | maven-install-plugin:install |
deploy | maven-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.
What to take from the run:
- The output starts at
validateand stops atinstall— six phases for one command. That is the cumulative rule in action:mvn installis shorthand for running the whole prefix of the sequence up toinstall, never just the single named phase. - Each executed phase printed the
plugin:goalbound to it (compile -> compiler:compile,package -> jar:jar, and so on). Phases are slots; the goals are what actually compile, test, and bundle.validateprinted(no goal bound), showing a phase can run yet do nothing. deploy skipped : trueconfirms phases after the one you request never run. To publish to a remote repository you must explicitly ask formvn deploy; a plaininstalldeliberately stays local.clean ran : falseprovescleanbelongs to a separate lifecycle. Invokinginstalldoes not deletetarget/, which is exactly whymvn clean installspells out both — one phase from each lifecycle.- The phases ran in the fixed order
validate, compile, test, package, verify, installand the program ended withBUILD 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
In a standard Maven JAR project, what does running 'mvn package' do?