Maven: Getting Started

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Apache Maven 3.10.0 (released on 27 September 2026). Every example marked as run was executed on JDK 26 on 2026-10-16, on macOS, in a scratch directory, with Maven 3.10.0 unless the text names another version; output is shown as printed, with the long scratch path shortened to <scratch> (and the private local repository used in two runs to <repo>). Output that is shortened with a grep or by removing blank lines or Loaded ... prefixes lines says so. The page follows the official "Maven in 5 Minutes" and "Getting Started Guide", which it checks against what current Maven prints, and ends with the places where those guides are out of date. The running scenario of this section, tidepool, comes back at the end for the "more than one project" question; the ideas behind the commands (layout and coordinates) are on Philosophy, Coordinates & Directory Layout. If you are new to Java build and tooling in general, Java: Build & Tooling places Maven among the tools a Java developer meets.

Prerequisites

You need a JDK and Maven. The installation, including verifying the download, is on Installation; here it is enough to check that both work:

mvn --version
Apache Maven 3.10.0 (c43a36b8d67be7e0805a411bc0898af1a51f5472)
Maven home: <tools>/apache-maven-3.10.0
Java version: 26, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home
Default locale: es_ES, platform encoding: UTF-8, time zone: Europe/Madrid
OS name: "mac os x", version: "27.0.1", arch: "aarch64", family: "mac"

(<tools> stands for the directory the distribution was unpacked into.) The "Maven home" line shows which installation answered, and the "Java version" line which JDK it will run on, which is the first thing to check when a build behaves oddly. Maven 3.10.0 and 3.9.16 need Java 8 or later to run; Maven 4.0.0-rc-7 needs Java 17. The Java a project is compiled for is a separate choice, made in the POM (below). Behind a proxy or with a non-default local repository you also need some configuration; see Configuring Maven and settings.xml & Password Encryption.

Creating a project

Maven’s answer to "how do I start" is the archetype: a project template that is combined with some input to produce a working project. The archetype:generate goal (from the Maven Archetype plugin, version 3.4.1 in these runs) creates a project from one. The simplest template is maven-archetype-quickstart, whose latest release on Maven Central on 2026-10-16 is 1.5. The command of the official guide, run unchanged in an empty directory (the flags -B for batch mode and -ntp for no transfer progress were added to keep the log readable and are covered on the Running Maven page):

mvn -B -ntp archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app \
    -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false
[INFO] --- archetype:3.4.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: maven-archetype-quickstart:1.5
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.mycompany.app
[INFO] Parameter: artifactId, Value: my-app
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.mycompany.app
[INFO] Parameter: packageInPathFormat, Value: com/mycompany/app
[INFO] Parameter: junitVersion, Value: 5.11.0
[INFO] Parameter: javaCompilerVersion, Value: 17
[INFO] Project created from Archetype in dir: <scratch>/my-app
[INFO] BUILD SUCCESS

(Output filtered to the lines that carry information; the run also repeated a few Parameter lines.) Three things in that output are worth reading rather than skipping:

  • @ standalone-pom means the goal ran outside any project, against Maven’s built-in empty POM. archetype:generate creates a project; it does not need one.

  • The goal was called by its short prefix archetype, which Maven maps to the plugin org.apache.maven.plugins:maven-archetype-plugin and then resolves to its latest release (3.4.1 here). A repeatable build names the plugin version explicitly, as the generated POM does for the lifecycle plugins (below).

  • The parameters not given on the command line took their defaults: the version 1.0-SNAPSHOT, the Java package equal to the group id, JUnit 5.11.0 and Java 17 as the compiler release. They can all be set with -D options.

The same command also printed a few Don't override file warnings for directories that the archetype generated and that already existed in the target path (src/main/java/com/mycompany/app, the test equivalent and .mvn); they are noise on a first run. Maven 3.10.0 and 4.0.0-rc-7 (not 3.9.16) additionally printed a warning that Maven Central serves the path archetype-catalog.xml which the repository’s auto-discovered prefixes file denies and that "the prefixes file appears stale"; Maven allowed that one verified path and carried on. It comes from the auto-discovered repository prefixes that the same log reports loading (Loaded 24109 auto-discovered prefixes for remote repository central), and it is harmless here.

The first run downloads

The guide warns that the first run "may take a while" because Maven downloads plugins and their dependencies into the local repository, and that a flaky connection can make you repeat the command. This is measurable. The same generation followed by mvn package, each against an empty private local repository (-Dmaven.repo.local), on a fast connection (the time is the total Maven reported):

Run Downloads Total time

archetype:generate, empty local repository

149 files

4.4 s

mvn package of the generated project, same repository

152 files

6.1 s

the local repository afterwards

102 JARs

81 MB

Every later build of any project reuses what is in ~/.m2/repository, which is why the output below, run with a warm repository, shows no Downloading lines. A build that must not touch the network can be run offline (-o), and a team can put a repository manager in front of Central; both are covered on Repositories, Mirrors & Repository Managers.

The old goal archetype:create, still found in books, does not exist any more. On Maven 3.10.0 it fails with Could not find goal 'create' in plugin org.apache.maven.plugins:maven-archetype-plugin:3.4.1 among available goals crawl, create-from-project, generate, help, integration-test, jar, update-local-catalog. Archetypes, their catalogues and how to write one are in Archetypes.

What was generated

The command created a directory with the artifact id as its name. Its contents, from find . -type f | sort:

./.mvn/jvm.config
./.mvn/maven.config
./pom.xml
./src/main/java/com/mycompany/app/App.java
./src/test/java/com/mycompany/app/AppTest.java

That is the standard layout described on the philosophy page: sources under src/main/java, tests under src/test/java, the POM at the top. The two files under .mvn are new in the current quickstart archetype (the 5-minute guide’s tree, written for an older one, does not show them) and both are empty in this release (wc -c reports 0 bytes). They are the places for project-wide JVM options (jvm.config) and command-line arguments (maven.config) that Maven reads at start-up, and a .mvn directory also marks the root of the project for Maven 4; see Configuring Maven. Without a .mvn directory, Maven 4.0.0-rc-7 prints Unable to find the root directory. Create a .mvn directory in the root directory or add the root="true" attribute on the root project's model (seen in another run of this page’s experiments, on a POM with no .mvn), so keeping it is useful.

The two Java files are as small as the guide promises:

package com.mycompany.app;

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}
package com.mycompany.app;

import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

/**
 * Unit test for simple App.
 */
public class AppTest {

    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() {
        assertTrue(true);
    }
}

The POM

The pom.xml is the project descriptor and the core of a Maven project: Maven is project-centric, and the POM is the basic unit of work. This is the generated file; the pluginManagement section, which is long, is cut where marked ... and shown in full in the next section:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>my-app</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.release>17</maven.compiler.release>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.11.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Optionally: parameterized tests support -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      ...
    </pluginManagement>
  </build>
</project>

Element by element:

Element What it does here

project

the root of every POM; the namespace and xsi:schemaLocation let an editor validate the file against the 4.0.0 schema

modelVersion

the version of the POM model itself (4.0.0 for every Maven 2, 3 and 4 build POM that is published; Maven 4 adds a 4.1.0 model for build POMs)

groupId, artifactId, version

the coordinates of this project; the primary artifact will be my-app-1.0-SNAPSHOT.jar (the form artifactId-version.extension). -SNAPSHOT marks work in progress

name, url

display name and project website, used by the generated site; the FIXME comment is the archetype reminding you to replace the placeholder URL

properties

value placeholders available anywhere in the POM; here the source encoding and maven.compiler.release, which the compiler plugin reads as --release 17

dependencyManagement

pins versions without adding dependencies; here it imports the JUnit bill of materials (junit-bom, type pom, scope import) so that the JUnit artifacts below need no version

dependencies

what the project needs; both JUnit entries are test scope, so they are on the test class path and not in the artifact

build/pluginManagement

pins the version of every plugin the default lifecycle uses, so that the build does not depend on Maven’s built-in defaults

The POM has no packaging element, so it is jar (the default). The meaning of every element, inheritance and the effective POM are the subject of POM Fundamentals; the coordinates are explained on the philosophy page.

The plugin versions the archetype pinned

The part of the generated POM that the guides do not show is the one that decides how the build behaves. Its pluginManagement pins nine plugins, and each is older than what Maven 3.10.0 would pick on its own, which help:describe shows for the install phase of a jar project (the second column is read from the POM, the third from the lifecycle that Maven 3.10.0 printed):

Plugin Pinned by the archetype Default of Maven 3.10.0

maven-clean-plugin

3.4.0

3.5.0

maven-resources-plugin

3.3.1

3.5.0

maven-compiler-plugin

3.13.0

3.16.0

maven-surefire-plugin

3.3.0

3.6.0

maven-jar-plugin

3.4.2

3.5.1

maven-install-plugin

3.1.2

3.2.0

maven-deploy-plugin

3.1.2

3.2.0

The other two pinned plugins (maven-site-plugin 3.12.1 and maven-project-info-reports-plugin 3.6.1) are used only by the site and have no lifecycle default to compare with. Pinning is good practice, and the build below uses the pinned versions, but a project created from an archetype starts with versions that were current when the archetype was released, and keeping them current is part of maintaining the project (the versions plugin and the ways to see plugin updates are on Plugins & Their Configuration).

Compile, test, package

Change into the project directory (cd my-app) for everything that follows. The commands name a lifecycle phase.

mvn compile

mvn -B -ntp compile
[INFO] Scanning for projects...
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] --- resources:3.3.1:resources (default-resources) @ my-app ---
[INFO] skip non existing resourceDirectory <scratch>/my-app/src/main/resources
[INFO] --- compiler:3.13.0:compile (default-compile) @ my-app ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug release 17] to target/classes
[INFO] BUILD SUCCESS
[INFO] Total time:  1.500 s

(Blank [INFO] lines, the Loaded ... prefixes lines and the finish timestamp are left out here and below.) Read the lines rather than the verdict: each --- plugin:version:goal (execution-id) @ project --- line is a plugin goal running, in order. compile is a phase, and to reach it Maven ran the goals bound to every phase up to it that has one; for this project that is resources:resources (nothing to copy, since there is no src/main/resources) and compiler:compile. The compiler line shows the effect of the maven.compiler.release property: javac [debug release 17], JDK 26 compiling for Java 17. Classes went to target/classes without the POM saying so.

mvn test

mvn -B -ntp test
[INFO] --- resources:3.3.1:resources (default-resources) @ my-app ---
[INFO] skip non existing resourceDirectory <scratch>/my-app/src/main/resources
[INFO] --- compiler:3.13.0:compile (default-compile) @ my-app ---
[INFO] Nothing to compile - all classes are up to date.
[INFO] --- resources:3.3.1:testResources (default-testResources) @ my-app ---
[INFO] skip non existing resourceDirectory <scratch>/my-app/src/test/resources
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ my-app ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug release 17] to target/test-classes
[INFO] --- surefire:3.3.0:test (default-test) @ my-app ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO] Running com.mycompany.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.028 s -- in com.mycompany.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

(The T E S T S banner lines are left out.) Because test comes after compile, Maven ran compile again first, and the compiler plugin answered that all classes were up to date. Surefire found the JUnit 5 provider by itself ("auto detected") and ran AppTest: its default inclusion patterns are **/Test*.java, **/*Test.java, **/*Tests.java and **/*TestCase.java, and the default exclusion is **/*$* (nested classes), as help:describe of the plugin’s test goal reports. To compile the tests without running them, use the test-compile phase (run: it printed nothing under -q and exited with status 0). Testing is the subject of Testing with Surefire.

mvn package

mvn -B -ntp package
[INFO] --- jar:3.4.2:jar (default-jar) @ my-app ---
[INFO] Building jar: <scratch>/my-app/target/my-app-1.0-SNAPSHOT.jar
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

(Only the lines that differ from the test run are shown; the jar plugin’s goal is the new one.) The result is in target:

classes
generated-sources
generated-test-sources
maven-archiver
maven-status
my-app-1.0-SNAPSHOT.jar
surefire-reports
test-classes

Running the JAR

The 5-minute guide runs the result with java -cp, and it works as printed:

java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
Hello World!

It needs -cp and the class name because the JAR is a plain library: its manifest has no Main-Class entry. The manifest Maven wrote, and the contents of the JAR (unzip -l), are:

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.4.2
Build-Jdk-Spec: 26
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  02-01-1980 00:00   META-INF/
       81  02-01-1980 00:00   META-INF/MANIFEST.MF
        0  02-01-1980 00:00   com/
        0  02-01-1980 00:00   com/mycompany/
        0  02-01-1980 00:00   com/mycompany/app/
        0  02-01-1980 00:00   META-INF/maven/
        0  02-01-1980 00:00   META-INF/maven/com.mycompany.app/
        0  02-01-1980 00:00   META-INF/maven/com.mycompany.app/my-app/
      549  02-01-1980 00:00   com/mycompany/app/App.class
     3101  02-01-1980 00:00   META-INF/maven/com.mycompany.app/my-app/pom.xml
       65  02-01-1980 00:00   META-INF/maven/com.mycompany.app/my-app/pom.properties
---------                     -------
     3796                     11 files

Two details. The JAR carries the POM and a pom.properties file under META-INF/maven/<groupId>/<artifactId>, so every Maven artifact describes itself; the properties file (also in target/maven-archiver) holds groupId, artifactId and version, which an application can read with the standard Java API, a technique the Getting Started Guide shows. And every entry is dated 1980-02-01, because Maven 3.10.0 builds reproducibly by default (its super POM sets project.build.outputTimestamp); the same project built with Maven 3.9.16 dated its entries with the build time (10-16-2026 12:46) because it does not set that property. Making the JAR executable, with a Main-Class and its dependencies, is on Packaging JARs, WARs & Manifests and Uber JARs with Shade & Assembly.

Install into the local repository

mvn install copies the artifact into the local repository, so that other projects on the machine can depend on it. The default repository is ~/.m2/repository; to leave that untouched, this run used a private one with -Dmaven.repo.local:

mvn -B -ntp -Dmaven.repo.local=<repo> install
[INFO] --- install:3.1.2:install (default-install) @ my-app ---
[INFO] Installing <scratch>/my-app/pom.xml to <repo>/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.pom
[INFO] Installing <scratch>/my-app/target/my-app-1.0-SNAPSHOT.jar to <repo>/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS

(The compile, test and package output before it is the same as above.) The files the install left are the POM and the JAR under a path made of the group id (dots to slashes), the artifact id and the version, plus maven-metadata-local.xml files and a _remote.repositories file recording where the artifact came from:

com/mycompany/app/my-app/1.0-SNAPSHOT/_remote.repositories
com/mycompany/app/my-app/1.0-SNAPSHOT/maven-metadata-local.xml
com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.jar
com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.pom
com/mycompany/app/my-app/maven-metadata-local.xml

Another project can now depend on com.mycompany.app:my-app:1.0-SNAPSHOT and Maven will find it there. The repository layout, and what changes when the artifact is deployed to a remote repository instead, are on the philosophy page and in Install, Deploy & Distribution Management.

Phases, goals and the lifecycle

The first command of this page, archetype:generate, names a goal: a task of a plugin, written prefix:goal. The others, compile, test and package, are phases: steps in a build lifecycle, which is an ordered sequence of phases. Naming a phase runs every phase before it, in order, up to and including it. The guide lists those up to compile: validate, generate-sources, process-sources, generate-resources, process-resources and compile. What each phase does depends on which goals are bound to it, and that depends on the packaging. Maven can print the binding for the jar packaging of this very project:

mvn -B -ntp help:describe -Dcmd=install
[INFO] 'install' is a phase corresponding to this plugin:
org.apache.maven.plugins:maven-install-plugin:3.2.0:install

It is a part of the lifecycle for the POM packaging 'jar'. This lifecycle includes the following phases:
* validate: Not defined
* initialize: Not defined
* generate-sources: Not defined
* process-sources: Not defined
* generate-resources: Not defined
* process-resources: org.apache.maven.plugins:maven-resources-plugin:3.5.0:resources
* compile: org.apache.maven.plugins:maven-compiler-plugin:3.16.0:compile
* process-classes: Not defined
* generate-test-sources: Not defined
* process-test-sources: Not defined
* generate-test-resources: Not defined
* process-test-resources: org.apache.maven.plugins:maven-resources-plugin:3.5.0:testResources
* test-compile: org.apache.maven.plugins:maven-compiler-plugin:3.16.0:testCompile
* process-test-classes: Not defined
* test: org.apache.maven.plugins:maven-surefire-plugin:3.6.0:test
* prepare-package: Not defined
* package: org.apache.maven.plugins:maven-jar-plugin:3.5.1:jar
* pre-integration-test: Not defined
* integration-test: Not defined
* post-integration-test: Not defined
* verify: Not defined
* install: org.apache.maven.plugins:maven-install-plugin:3.2.0:install
* deploy: org.apache.maven.plugins:maven-deploy-plugin:3.2.0:deploy

(Run in my-app; the framing [INFO] lines are left out. It lists the Maven 3.10.0 defaults, not the versions the archetype pinned, which is why the versions differ from the build output above.) This is the whole build of a plain JAR project: eight goals bound to 23 phases, and the other phases are hooks that a plugin may bind to. Two more lifecycles exist next to the default one listed: clean, which deletes the target directory, and site, which generates documentation. Each phase and the choices behind them are explained on Build Lifecycle.

Phases and goals can be mixed on one command line, and run in the order written. The guide’s example:

mvn -B -ntp clean dependency:copy-dependencies package
[INFO] --- clean:3.4.0:clean (default-clean) @ my-app ---
[INFO] --- dependency:3.11.0:copy-dependencies (default-cli) @ my-app ---
[INFO] --- resources:3.3.1:resources (default-resources) @ my-app ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ my-app ---
[INFO] --- resources:3.3.1:testResources (default-testResources) @ my-app ---
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ my-app ---
[INFO] --- surefire:3.3.0:test (default-test) @ my-app ---
[INFO] --- jar:3.4.2:jar (default-jar) @ my-app ---
[INFO] BUILD SUCCESS

(Filtered to the goal lines.) It cleaned the project, ran the dependency plugin’s copy-dependencies goal (Maven resolved the prefix to version 3.11.0, because the POM does not manage that plugin), then ran the lifecycle up to package. The goal wrote the project’s dependencies to target/dependency:

apiguardian-api-1.1.2.jar
junit-jupiter-api-5.11.0.jar
junit-jupiter-params-5.11.0.jar
junit-platform-commons-1.11.0.jar
opentest4j-1.3.0.jar

Those are the two direct test dependencies and their transitive dependencies, resolved from the versions in junit-bom. Note the identifier in the output: (default-cli) is the execution id Maven gives to a goal invoked from the command line, and (default-<goal>) is the id of an execution bound by the packaging. Plugin executions and how to bind another goal to a phase are on Plugins & Their Configuration.

The site

A POM with a name, a URL and a few dependencies already carries enough information to generate a project web site:

mvn -B -ntp site
[INFO] --- site:3.12.1:site (default-site) @ my-app ---
[INFO] configuring report plugin org.apache.maven.plugins:maven-project-info-reports-plugin:3.6.1
[INFO] 15 reports detected for maven-project-info-reports-plugin:3.6.1: ci-management, dependencies, dependency-info, dependency-management, distribution-management, index, issue-management, licenses, mailing-lists, modules, plugin-management, plugins, scm, summary, team
[INFO] Rendering site with default locale English (en)
[INFO] Rendering content with org.apache.maven.skins:maven-default-skin:jar:1.3 skin.
[INFO] Generating "Dependencies" report         --- maven-project-info-reports-plugin:3.6.1:dependencies
[INFO] Generating "Maven Coordinates" report    --- maven-project-info-reports-plugin:3.6.1:dependency-info
[INFO] Generating "Dependency Management" report --- maven-project-info-reports-plugin:3.6.1:dependency-management
[INFO] Generating "About" report                --- maven-project-info-reports-plugin:3.6.1:index
[INFO] Generating "Plugin Management" report    --- maven-project-info-reports-plugin:3.6.1:plugin-management
[INFO] Generating "Plugins" report              --- maven-project-info-reports-plugin:3.6.1:plugins
[INFO] Generating "Summary" report              --- maven-project-info-reports-plugin:3.6.1:summary
[INFO] BUILD SUCCESS

(Filtered; the run took about five seconds.) Fifteen reports are available to the project-info plugin and seven were generated; the other eight (CI management, distribution management, issue management, licenses, mailing lists, modules, SCM and team) were not, and this POM declares no issue tracker, mailing list, SCM or license. The result is under target/site:

css
dependencies.html
dependency-info.html
dependency-management.html
images
index.html
plugin-management.html
plugins.html
project-info.html
summary.html

Customising the site, choosing skins and adding reports are on Site & Reporting. Maven 3.10 also introduced the property maven.site.lifecycle.disabled, covered with the other 3.10 changes in Versions & Release History. To start from a ready-made site skeleton, the guide suggests archetype:generate with maven-archetype-site (not run here).

The questions of the 30-minute guide

The Getting Started Guide is organised as a list of questions. The ones about compiling, testing, packaging, installing and the site are answered above. These are the rest, each checked against current Maven.

What is a SNAPSHOT version?

A version ending in -SNAPSHOT (the 1.0-SNAPSHOT of the generated POM) is the development version that comes before the release: the latest code along a development branch, with no guarantee that it is stable or unchanging. A release version (no suffix) never changes. Releasing turns 1.0-SNAPSHOT into 1.0 and moves development to 1.1-SNAPSHOT. The point that matters in practice is what Maven does with them: it looks for snapshot dependencies in the remote repositories again and downloads them again when they are newer than the local copy, and a snapshot that is deployed is stored with a timestamp, as the philosophy page shows by deploying my-app to a local file: repository (the file becomes my-app-1.0-20261016.104005-1.jar in the directory 1.0-SNAPSHOT, while a local install keeps the -SNAPSHOT name). The rules for resolving, ordering and ranges of versions are on Versions, Snapshots & Ranges.

How do I use plugins?

To customise a build you add or reconfigure a plugin in the build section of the POM. A plugin looks like a dependency (group id, artifact id, version) and is downloaded the same way. The configuration element passes parameters to every goal of the plugin, and the plugin’s documentation lists the parameters of each goal. The guide’s example configures the compiler for JDK 5 sources with <source>1.5</source> and <target>1.5</target> on plugin 3.3; that no longer works, because current JDKs reject such old values. The modern way to say which Java to compile for is the property the generated POM already has, maven.compiler.release, which was used in the compile run above (javac [debug release 17]). The compiler’s other options, and toolchains for building with a JDK different from the one running Maven, are on Compiling & Toolchains.

How do I add resources to my JAR?

Put them in src/main/resources: the files and directories there are copied to the root of the JAR with the same structure; test resources go in src/test/resources and onto the test class path. No POM change is needed. By default nothing is changed in the copy; to substitute build values into a resource, turn on filtering for the directory. The guide’s recipe, run on a copy of my-app with this src/main/resources/META-INF/application.properties:

application.name=${project.name}
application.version=${project.version}
java.version=${java.version}
command.line.prop=${command.line.prop}

and <filtering>true</filtering> on the resource directory (the resources element must name the directory explicitly once it is overridden):

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
  ...
</build>
mvn -B -ntp process-resources "-Dcommand.line.prop=hello again"
cat target/classes/META-INF/application.properties
[INFO] --- resources:3.3.1:resources (default-resources) @ my-app ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO] BUILD SUCCESS
application.name=my-app
application.version=1.0-SNAPSHOT
java.version=26
command.line.prop=hello again

Maven filled project.name and project.version from the POM, java.version from the JVM’s system properties and command.line.prop from the -D option. A property that is defined nowhere is left as it is: the same run without the -D option printed command.line.prop=${command.line.prop}. Filter files (src/main/filters), the escape rules and the pitfalls (binary files that get corrupted by filtering) are on Resources & Filtering.

How do I use external dependencies?

A dependency is declared in dependencies with at least a group id, an artifact id and a version (the version can come from dependencyManagement, as the JUnit ones do here) and a scope that says how it is used: compile (the default), test, runtime and others. Maven looks in the local repository first and downloads what is missing from the remote repositories, by default Maven Central. To find the coordinates of a library, look at its page on Maven Central; the directory of an artifact there holds a maven-metadata.xml listing its versions. The copy-dependencies run above shows the result for junit-jupiter-api: the artifact and its transitive dependencies. The guide’s example adds log4j:log4j:1.2.12. Do not copy it: Log4j 1.x reached end of life on 5 August 2015, and its own site lists unfixed vulnerabilities (CVE-2019-17571, CVE-2021-4104 and others) and tells users to upgrade to Log4j 2. The same section’s junit:junit:4.11 is also long superseded; a new project uses JUnit 5 (junit-jupiter) or later, as the archetype does. Scopes, transitive resolution and conflicts are on Dependencies, Scopes & Mediation. The other tools declare the same dependency as an Ivy dependency element (Ant) or an implementation entry (Gradle); the mapping is on Dependency Management Fundamentals.

How do I deploy my JAR to a remote repository?

Deployment needs two things in two places: the repository URL in the POM’s distributionManagement, and the credentials in the user’s settings.xml, matched by the repository id. The guide shows an scp:// URL with a user name and a private key; that is a legacy transport. Today the targets are an HTTPS repository manager or Maven Central, and credentials come from the environment or an encrypted settings.xml entry, never from the POM. A deployment to a real server was not run here. What was run is the same goal against a local file: repository, which shows the layout a remote repository would receive, on the philosophy page. The rest is in Install, Deploy & Distribution Management, settings.xml & Password Encryption and Publishing to Maven Central.

How do I build more than one project at once?

Create a parent POM with pom packaging that lists the projects as modules, and run Maven on the parent. The tidepool scenario of this section is such a build: a parent POM io.tidepool:tidepool-parent:1.0.0 with the modules tidepool-model, tidepool-engine and tidepool-cli. A single command at the top builds all of them in dependency order:

mvn -B -ntp package
[INFO] Reactor Summary for tidepool parent 1.0.0:
[INFO]
[INFO] tidepool parent .................................... SUCCESS [  0.000 s]
[INFO] tidepool model ..................................... SUCCESS [  0.956 s]
[INFO] tidepool engine .................................... SUCCESS [  0.104 s]
[INFO] tidepool cli ....................................... SUCCESS [  0.011 s]
[INFO] BUILD SUCCESS

(The Reactor Summary, the last block of a multi-module build; the module builds before it have the same form as the single-project output above.) The mechanism is the reactor, covered with the glossary on the philosophy page and in full on Multi-Module Builds & the Reactor; inheritance from the parent is on Inheritance, Aggregation & the Effective POM.

The same project on Maven 3.9.16 and 4.0.0-rc-7

The generated project is not specific to one Maven. archetype:generate with the same command on 3.9.16 and on 4.0.0-rc-7 produced a src tree and a pom.xml identical to the 3.10.0 one (verified with diff; both used the archetype plugin 3.4.1), and mvn package on each printed the same goal lines with the same plugin versions (resources:3.3.1, compiler:3.13.0, surefire:3.3.0, jar:3.4.2), the same Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 and BUILD SUCCESS, and, for 4.0.0-rc-7, no warning because the project has a .mvn directory. What differs is the platform around the command, not the result: 3.10.0 and 4.0.0-rc-7 print the archetype catalogue warning described above and 3.9.16 does not, and Maven 4.0.0-rc-7 itself is a release candidate (see Maven 4: What’s New).

The Ant and Gradle equivalents of this page are not a template command in the same sense: Ant has no generator, so a project starts from a copied build.xml (see Build File Structure), and Gradle has the gradle init task (see Java Projects); neither was run for this page.

Where the official guides are outdated

Every command of "Maven in 5 Minutes" ran as printed. What has aged is the sample output and a few surrounding statements:

  • The sample output shows plugin versions of 2018 to 2020 (maven-resources-plugin:3.0.2, maven-compiler-plugin:3.8.0, maven-surefire-plugin:2.22.1, maven-jar-plugin:3.0.2, maven-install-plugin:2.5.2) and a Maven 3.6.3 --version on Java 1.8. Current builds print the versions in the tables above, and the line format has changed (--- compiler:3.13.0:compile without the maven- prefix and [ jar ] banners).

  • The sample POM is described as an 18-line file; the generated POM is more than 80 lines because it now pins plugin versions and imports a BOM.

  • The Getting Started Guide sections use junit:junit:4.11 (JUnit 4), log4j:log4j:1.2.12 (end of life, above) and <source>1.5</source> (rejected by current JDKs).

  • The "Java 9 or later" section is about the maven.compiler.release property; since 3.6.0 of the compiler plugin that property is the way, and the generated POM already uses it.

  • The guide’s deploy example uses scp:// and a private key in settings.xml; use HTTPS and encrypted or environment-provided credentials.

  • Apache Maven 2 Effective Implementation uses archetype:create, which is gone (shown above).

The versions to use today are in Versions & Release History.

Where to go next