Maven: Installation
|
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), with 3.9.16 and Maven 4.0.0-rc-7 (a release
candidate) shown where they differ. Every example marked as run was executed on JDK 26 on 2026-10-16, on macOS (Apple silicon),
in a scratch directory; output that comes from a run is shown as printed, with the long scratch path shortened to <scratch>, the
directory the archive was unpacked into shown as /opt, and the home directory shown as <home>. Anything that could not be run
here (Windows, package managers, a JDK older than 17, a real signature check) is labelled "not run here". The commands use a POSIX
shell; the Windows equivalents are given from the official page.
Maven is installed by unpacking an archive. As with Ant there is no installer: an installation is a directory with a launcher
script, a class loader bootstrap, about eighty JARs and a global settings.xml. Everything else Maven needs (plugins, libraries)
is downloaded on demand into a local repository. This page covers getting the archive, checking it, the layout, and the two
environment questions that cause most installation trouble: which Java runs Maven and which mvn is first on the PATH. Which
release to choose is the subject of
Versions & Release History; the command line
is in Running Maven (CLI) and the configuration files and
variables in Configuring Maven. The running scenario
(tidepool) is described on the section landing page; for the other two
tools see Ant: Installation and
Gradle: Installation.
Prerequisites
Maven needs a Java Development Kit. The official page asks for one of two things: set JAVA_HOME to the JDK installation, or have
the java executable on the PATH. It adds that the current stable version, 3.10.0, "requires JDK 8+, but any recent version will
work just fine". The same holds for 3.9.x. Maven 4 requires Java 17 (the first release that does is 4.0.0-alpha-13).
The Java that runs Maven and the Java your project is compiled for are two different settings. JAVA_HOME chooses the first. The
second is chosen by the compiler plugin (maven.compiler.release) and, when the project needs a JDK that is not the one running
Maven, by toolchains; the download page says Maven 3.10 "still allows you to build against 1.3 and other JDK versions by using
toolchains". See Compiling & Toolchains.
Source: Maven: Downloading Apache Maven
Getting Maven
The binary distribution
The download page offers, for 3.10.0, a binary .tar.gz and a binary .zip (same content; the .zip is the natural choice on
Windows) and the same two formats for the sources, each with a .sha512 checksum file and an .asc PGP signature next to it.
3.9.16 (the "previous stable" release, still maintained) and Maven 4.0.0-rc-7 (listed as "Preview") have their own sections. The
page also links the KEYS file with the public keys of the Maven developers and says it is "highly recommended to verify the
signature of the release bundles against the public KEYS". Older releases are in the Maven 3 archives (3.0.4 and later) and the
legacy archives; the history page lists every release. Maven Daemon (mvnd) is distributed on the same page, with 1.0.6 as the
current release and 2.0.0-rc-3 as a preview that "is NOT safe for production use"; it is covered in
Build Performance: Cache, Parallel Builds & mvnd.
The detailed steps on the installation page are four: download the archive, extract it in any directory (unzip
apache-maven-3.10.0-bin.zip or tar xzvf apache-maven-3.10.0-bin.tar.gz), add the bin directory of the created
apache-maven-3.10.0 directory to the PATH, and confirm with mvn -v in a new shell.
Source: Maven: Downloading Apache Maven
Verifying the download
Apache’s rule is to check two things for each archive: that the digest matches and that the signature is valid for a key you have
reason to trust. Fetch the .sha512 and .asc files and the KEYS file from downloads.apache.org (the main distribution
site), not from a mirror, because a mirror can serve a tampered archive together with a matching tampered digest. The digest file
of Maven 3.10.0 holds the bare hexadecimal SHA-512 and no file name:
EXPECTED=$(cat apache-maven-3.10.0-bin.tar.gz.sha512)
ACTUAL=$(shasum -a 512 apache-maven-3.10.0-bin.tar.gz | cut -d' ' -f1)
[ "$EXPECTED" = "$ACTUAL" ] && echo "SHA-512 matches"
echo "${#EXPECTED} hexadecimal characters"
SHA-512 matches
128 hexadecimal characters
The 128 characters are the 512 bits. To use the shasum -c form, which prints OK or FAILED per file, add the file name to
the line. Appending one byte to a copy of the archive shows what a failure looks like:
echo "$(cat apache-maven-3.10.0-bin.tar.gz.sha512) apache-maven-3.10.0-bin.tar.gz" | shasum -a 512 -c -
cp apache-maven-3.10.0-bin.tar.gz tampered.tar.gz && printf 'x' >> tampered.tar.gz
echo "$(cat apache-maven-3.10.0-bin.tar.gz.sha512) tampered.tar.gz" | shasum -a 512 -c -
echo "exit status of the second check: $?"
apache-maven-3.10.0-bin.tar.gz: OK
tampered.tar.gz: FAILED
shasum: WARNING: 1 computed checksum did NOT match
exit status of the second check: 1
The digest proves the file arrived intact; it does not prove who published it, because whoever replaces the archive can replace the
digest next to it. The signature does. With the .asc file and the KEYS file in the same directory the check is:
gpg --import KEYS
gpg --verify apache-maven-3.10.0-bin.tar.gz.asc apache-maven-3.10.0-bin.tar.gz
The signature check was not run here ("not run here"): this series of pages downloaded the archive and its digest only, so no
.asc or KEYS file was available to check against, and no output is invented. What to expect is what Apache’s verification page
describes: "Good signature" for a key that is in KEYS, together with GnuPG’s warning that the key is not certified with a trusted
signature, because a fresh keyring has no web of trust. That warning is a statement about your keyring, not about the archive; the
stronger step is to compare the key fingerprint with one you obtained over a different channel. The
Supply-Chain Security page shows the same procedure with a
real signature check for another Apache tool, and covers the checksum of the Maven Wrapper download.
Unpacking and the directory layout
Unpack the archive in a directory of your choice. The tarball verified above is 10 MB and the unpacked installation is 11 MB on disk:
tar -xzf apache-maven-3.10.0-bin.tar.gz -C /opt
cd /opt
ls apache-maven-3.10.0 apache-maven-3.10.0/bin apache-maven-3.10.0/conf apache-maven-3.10.0/boot
ls apache-maven-3.10.0/lib | wc -l
ls apache-maven-3.10.0/lib/ext
apache-maven-3.10.0:
LICENSE
NOTICE
README.txt
bin
boot
conf
lib
apache-maven-3.10.0/bin:
m2.conf
mvn
mvn.cmd
mvnDebug
mvnDebug.cmd
mvnyjp
apache-maven-3.10.0/boot:
plexus-classworlds-2.12.1.jar
plexus-classworlds.license
apache-maven-3.10.0/conf:
logging
settings.xml
toolchains.xml
85
README.txt
hazelcast
redisson
The bare number is the count of entries in lib (85). What each part is:
| Path | Purpose |
|---|---|
|
The launcher script (POSIX shell). It resolves symbolic links to find the installation, sources the |
|
A wrapper that starts |
|
A launcher variant for the YourKit profiler; it stops with a message asking for the |
|
The class loader configuration of Maven’s core (the |
|
The class loader library that reads |
|
Maven’s own libraries (85 entries in 3.10.0: JARs and their |
|
The global (installation-wide) |
|
The global toolchains file (read at startup since 3.3.1). |
|
The logging configuration of the SLF4J simple logger that Maven uses (levels, |
The three configuration files in conf are the installation-wide layer. The user-wide layer is the .m2 directory in the home
directory, and the project layer is the .mvn directory; the precedence is covered in
Configuring Maven and
settings.xml & Password Encryption.
Putting your own settings in conf/settings.xml is discouraged because they vanish when you replace the installation by a newer
one; use ~/.m2/settings.xml instead.
The Maven 4.0.0-rc-7 (release candidate) distribution has more tools in bin and more files in conf:
JvmConfigParser.java m2.conf mvn mvn.cmd mvnDebug mvnDebug.cmd mvnenc mvnenc.cmd mvnsh mvnsh.cmd mvnup mvnup.cmd mvnyjp
logging maven-system.properties maven-user.properties settings.xml toolchains.xml
mvnup is the upgrade tool (mvnup check and mvnup apply), mvnenc is a launcher for the password encryption (it runs mvn
--enc), mvnsh is the Maven shell, and maven-system.properties and maven-user.properties hold default system and user
properties: the first defines where the installation, user and project configuration, settings and toolchains are located, the
second carries default user properties such as the resolver and cache settings. The tools are
covered in Migrating to Maven 4 and
settings.xml & Password Encryption.
Source: Maven: Configuring Apache Maven
The PATH and mvn -v
Add the bin directory to the PATH (for a shell profile on macOS and Linux, export PATH=/opt/apache-maven-3.10.0/bin:$PATH;
on Windows, the same through the system environment variables) and open a new shell. mvn -v (or mvn --version) prints the
banner and exits without building anything:
export PATH=/opt/apache-maven-3.10.0/bin:$PATH
mvn -v
Apache Maven 3.10.0 (c43a36b8d67be7e0805a411bc0898af1a51f5472)
Maven home: /opt/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"
The first line is the version and, in parentheses, the commit it was built from. "Maven home" is the installation directory
(Maven computes it from the location of the script, so M2_HOME and MAVEN_HOME are not needed). The "Java version" line is the
JDK that was chosen, with its vendor and home. The locale, the platform encoding and, since 3.10, the time zone follow, then the
operating system. The banner of 3.9.16 and of the 4.0.0-rc-7 release candidate, for comparison; note that 3.9.16 has no time zone:
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5)
Maven home: <home>/.local/maven
Java version: 26, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home
Default locale: es_ES, platform encoding: UTF-8
OS name: "mac os x", version: "27.0.1", arch: "aarch64", family: "mac"
Apache Maven 4.0.0-rc-7 (6d8f301b38af7399b36b862969d520974944f0b9)
Maven home: <tools>/apache-maven-4.0.0-rc-7
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"
The 3.10 release notes list "the time zone is part of the Maven startup banner" as a change. A common surprise is that mvn -v
reports another Maven than the one you just unpacked, because an earlier directory on the PATH wins; which -a lists every
mvn the shell can see in order (here, three installations, with the first being used):
which -a mvn
<scratch>/work/mvn-versions-install/v/lnk/mvn
<tools>/apache-maven-4.0.0-rc-7/bin/mvn
<home>/.local/bin/mvn
The launcher resolves symbolic links, so a link to bin/mvn placed in a directory that is already on the PATH is a fine way
to switch versions; Maven home still shows the real directory:
ln -s /opt/apache-maven-3.10.0/bin/mvn lnk/mvn
ls -l lnk/mvn
lnk/mvn -v | sed -n 1,2p
$ ls -l lnk/mvn
lnk/mvn -> /opt/apache-maven-3.10.0/bin/mvn
Apache Maven 3.10.0 (c43a36b8d67be7e0805a411bc0898af1a51f5472)
Maven home: /opt/apache-maven-3.10.0
Maven 3.5.0 removed M2_HOME ("the usage of M2_HOME has been removed and is not supported anymore" in its release notes), and on
Windows replaced %HOME% by %USERPROFILE%. A book or blog that tells you to set M2_HOME predates that release.
JAVA_HOME and how Maven finds Java
The launcher decides in a fixed order, which can be read in bin/mvn: if JAVA_HOME is set, Maven runs $JAVA_HOME/bin/java;
if it is not set, it uses the first java on the PATH; if the chosen file is not executable, it stops with a message. Setting
JAVA_HOME therefore overrides whatever the PATH says. The header comment of the script documents JAVA_HOME as the one
required variable and MAVEN_ARGS, MAVEN_OPTS and MAVEN_SKIP_RC as optional ones (the configuration variables are covered in
Configuring Maven).
Without JAVA_HOME, the banner above showed JDK 26 because that is the java on the PATH of the machine used. With JAVA_HOME
pointing at a JetBrains Runtime 17 (and at a JetBrains Runtime 21 for Maven 4.0.0-rc-7), only the first three lines being shown:
JAVA_HOME=<home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home mvn -v | sed -n 1,3p
JAVA_HOME=<home>/Library/Java/JavaVirtualMachines/jbr-21.0.10/Contents/Home mvn4 -v | sed -n 1,3p
Apache Maven 3.10.0 (c43a36b8d67be7e0805a411bc0898af1a51f5472)
Maven home: /opt/apache-maven-3.10.0
Java version: 17.0.14, vendor: JetBrains s.r.o., runtime: <home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home
Apache Maven 4.0.0-rc-7 (6d8f301b38af7399b36b862969d520974944f0b9)
Maven home: <tools>/apache-maven-4.0.0-rc-7
Java version: 21.0.10, vendor: JetBrains s.r.o., runtime: <home>/Library/Java/JavaVirtualMachines/jbr-21.0.10/Contents/Home
(mvn4 stands for the bin/mvn of the 4.0.0-rc-7 installation.) A JAVA_HOME that does not contain bin/java gives Maven’s
own message and exit status 1:
JAVA_HOME=/nonexistent mvn -v
echo "exit status: $?"
The JAVA_HOME environment variable is not defined correctly,
this environment variable is needed to run this program.
exit status: 1
The message says "not defined correctly" for two different mistakes: a path that does not exist and a path that is not a JDK
home (a directory without bin/java). Typical mistakes are a JAVA_HOME that points at the bin directory or at a JDK that has since been
removed; in both cases the directory has no bin/java where Maven looks.
The script also reads mavenrc files before it looks for Java, in this order: /usr/local/etc/mavenrc, /etc/mavenrc and
~/.mavenrc; setting MAVEN_SKIP_RC skips all three. This is the place to pin a JDK for Maven without touching the shell
profile (the configuration page says .mavenrc "defines environment variables used when Maven builds the project"; on Windows
the file is mavenrc_pre.cmd for Maven 3 and mavenrc.cmd for Maven 4, not run here). The run below uses a scratch home whose
.mavenrc contains JAVA_HOME=$HOME/jbr17, a link to the JBR 17 home; the first line printed is the file, then the Java line
with the file honoured, then with MAVEN_SKIP_RC=1:
JAVA_HOME=$HOME/jbr17
Java version: 17.0.14, vendor: JetBrains s.r.o., runtime: <home>/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home
Java version: 26, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk-26.jdk/Contents/Home
A project that must build on a particular JDK is better served by the toolchain mechanism or by the Maven Wrapper with a
.mvn/jvm.config than by a .mavenrc, because those travel with the project.
Java 8 versus Java 17
| Maven | Needs to run | Notes |
|---|---|---|
3.9.16 and 3.10.0 |
Java 8 or later |
The banner works on any JDK from 8 up; run here on JDK 17 and 26. Builds can target older Java through the compiler plugin and toolchains. |
4.0.0-rc-7 (release candidate) |
Java 17 or later |
"Java 17 is only needed to run Maven" (the Maven 4 article). Run here on JDK 21 and 26; a start on JDK 8 or 11 was not run here because none was installed. |
A team whose build must run on a Java 8 CI image can stay on 3.9.x or 3.10.x; a build that uses Maven 4 needs a Java 17 JDK for
Maven itself even when it compiles for Java 8, which is where toolchains or maven.compiler.release come in. The history page
records when each line changed its floor.
Source: Maven 4: Required Java version
Package managers
The installation page lists the package managers that carry Maven; they install a copy that they update on their own schedule, usually after the release:
| System | Package manager | Command from the official page |
|---|---|---|
macOS |
Homebrew |
|
macOS |
SDKMAN! |
|
macOS |
MacPorts |
|
Linux |
APT |
|
Linux |
DNF |
|
Linux |
YUM |
|
Windows |
Chocolatey |
|
Windows |
Scoop |
|
On 2026-10-16 the Homebrew formula’s JSON (on formulae.brew.sh) reported version 3.10.0 and a dependency on openjdk, and the
SDKMAN! API reported 3.10.0 as the default Maven candidate. The versions that APT, DNF, YUM, MacPorts, Chocolatey and Scoop carry
were not looked up, and none of the commands in the table was run here ("not run here"). Check with mvn -v after installing, and remember that a package manager installs one version per machine,
while a project needs one version per build, which is what the next section solves.
Source: Maven: Installation, Homebrew
Source: Maven: Installation, SDKMAN!
Source: Maven: Installation, MacPorts
Source: Maven: Installation, APT
Source: Maven: Installation, DNF
Source: Maven: Installation, YUM
Source: Maven: Installation, Chocolatey
Source: Maven: Installation, Scoop
The Maven Wrapper: one version per project
An installed Maven is a property of the machine; a build wants a property of the project. The Maven Wrapper closes the gap: the
project carries mvnw, mvnw.cmd and .mvn/wrapper/maven-wrapper.properties, and the first ./mvnw downloads the Maven version
named in the properties file and uses it afterwards. The wrapper page says why this matters in its own words: Maven "has been very
stable" and is "available on most systems", but with recent changes it is easier for users "to have a fully encapsulated build setup
provided by the project", and a wrapper "should work with any Maven 3.x version or later" (Maven 2.x is not supported). Because
developers and CI then run the same Maven, the wrapper is preferred over a documented "install Maven X" step. The wrapper page
sets it up with the wrapper plugin. Run here with Maven 3.10.0 in an empty directory:
mvn wrapper:wrapper -Dmaven=3.10.0
[INFO] --- wrapper:3.3.4:wrapper (default-cli) @ standalone-pom ---
[INFO] Unpacked only-script type wrapper distribution org.apache.maven.wrapper:maven-wrapper-distribution:zip:only-script:3.3.4
[INFO] Configuring .mvn/wrapper/maven-wrapper.properties to use Maven 3.10.0 and download from https://repo.maven.apache.org/maven2
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
(The line "Loaded 74 auto-discovered prefixes" was removed from the output.) The files the goal created, and the properties:
./.mvn/wrapper/maven-wrapper.properties
./mvnw
./mvnw.cmd
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.10.0/apache-maven-3.10.0-bin.zip
The default distributionType is only-script, which means mvnw downloads the Maven distribution itself with shell tools
instead of shipping a maven-wrapper.jar. Running ./mvnw -v downloads the 3.10.0 zip from Maven Central and was not run here
(it would be a second download of a distribution that is already unpacked above); the supply-chain page ran it with a
distributionSha256Sum set and shows the checksum failure when the value is wrong. The wrapper does not replace an installed
Maven for the first step, because creating the wrapper needs one (or an IDE feature that does it); the wrapper, its options and
the distributionSha256Sum property are covered in
Maven Wrapper. Gradle projects use the same idea with
gradlew; Ant has no wrapper.
Source: Maven: Maven Wrapper
Troubleshooting an installation
| Symptom | Likely cause | What to do |
|---|---|---|
|
|
Open a new shell, or |
|
|
Fix it to the JDK home (the directory that contains |
|
An earlier |
|
|
|
|
A plugin "has unmet prerequisites" |
The plugin needs a newer Maven than the one running (for instance a 4.x plugin on Maven 3). |
Pin an earlier plugin version or use the matching Maven; see the plugin compatibility section of the history page. |
Maven 4 cannot start |
Java older than 17. |
Point |
The first row, run in a shell whose PATH has no Maven (env -i PATH=/usr/bin:/bin /bin/sh -c 'mvn -v'):
/bin/sh: mvn: command not found
exit status: 127
The exit status 127 is the shell’s code for "command not found", not Maven’s. Maven’s own installation-related errors are the
JAVA_HOME message shown above and the plugin prerequisite message shown on the history page; the wider catalogue of build
failures and their causes is in
Troubleshooting & Best Practices.
Where the Maven 2 book is outdated
Apache Maven 2 Effective Implementation (Packt, 2009) starts with installing Maven 2.2.1. Three things in that start have
changed. Maven 2.x has been end of life since 2014-02-18, and the maintained lines are 3.10.0 and 3.9.16 (4.0.0-rc-7 is a release
candidate). Java 5 is no longer enough: 3.9 and 3.10 need Java 8, and Maven 4 needs Java 17. And M2_HOME plus an unpacked copy
on every machine is replaced by Maven finding its own home from the script location and, per project, by the Maven Wrapper.
Plain HTTP repositories, which the book’s examples use, are blocked by default since 3.8.1.
Source: Maven: Releases History