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.

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.

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

bin/mvn

The launcher script (POSIX shell). It resolves symbolic links to find the installation, sources the mavenrc files, finds Java, and starts the JVM with the class loader bootstrap. bin/mvn.cmd is the Windows launcher (not run here).

bin/mvnDebug

A wrapper that starts mvn with a JDWP debug agent that suspends until a debugger attaches, on port 8000 or the value of MAVEN_DEBUG_ADDRESS.

bin/mvnyjp

A launcher variant for the YourKit profiler; it stops with a message asking for the YJPLIB variable when it cannot find the profiler library.

bin/m2.conf

The class loader configuration of Maven’s core (the plexus.core realm): it loads conf/logging and then every JAR in lib, and loads optional JARs from lib/ext.

boot/plexus-classworlds-2.12.1.jar

The class loader library that reads m2.conf and starts org.apache.maven.cli.MavenCli.

lib/

Maven’s own libraries (85 entries in 3.10.0: JARs and their .license files). lib/ext holds the optional Hazelcast and Redisson support for a shared local repository (optionally entries in m2.conf) and a README.txt. Nothing here is a build dependency; do not put project JARs in it.

conf/settings.xml

The global (installation-wide) settings.xml. It is where the mirror maven-default-http-blocker lives, which blocks external HTTP repositories since 3.8.1.

conf/toolchains.xml

The global toolchains file (read at startup since 3.3.1).

conf/logging/simplelogger.properties

The logging configuration of the SLF4J simple logger that Maven uses (levels, levelInBrackets, output to standard output).

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.

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.

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

brew install maven

macOS

SDKMAN!

sdk install maven

macOS

MacPorts

sudo port install maven3

Linux

APT

sudo apt install maven

Linux

DNF

sudo dnf install maven

Linux

YUM

sudo yum install maven

Windows

Chocolatey

choco install maven

Windows

Scoop

scoop install main/maven

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.

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.

Troubleshooting an installation

Symptom Likely cause What to do

mvn: command not found

bin is not on the PATH of this shell, or the profile that sets it was not read.

Open a new shell, or export PATH=... and run which -a mvn.

The JAVA_HOME environment variable is not defined correctly

JAVA_HOME points at a path without bin/java.

Fix it to the JDK home (the directory that contains bin), or unset it to use the java on the PATH.

mvn -v shows a different Maven than the one you installed

An earlier PATH entry (an IDE, a package manager, an old install) wins.

which -a mvn; reorder the PATH or use the wrapper.

mvn -v shows an unexpected JDK

JAVA_HOME or a .mavenrc selects it.

echo $JAVA_HOME; look at the three mavenrc locations; MAVEN_SKIP_RC=1 to test without them.

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 JAVA_HOME at a JDK 17 or later (the message for this case was not run here).

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.