Maven only works as well as the Java branch behind it, so installing Apache Maven on Debian should prove both the mvn command and the JDK compiler. Debian’s package is named maven, while the command you run afterward is mvn. Most systems should use APT for the distro-managed package; use the Apache binary tarball only when you need the current upstream Maven 3 release before it reaches your Debian release.
Install Apache Maven on Debian
Choose an Apache Maven Installation Method
Choose one Maven installation path unless you deliberately want both. If APT Maven and a manual /opt/maven install coexist, the active command depends on which directory appears first in PATH; verify it with command -v mvn.
| Method | Source or Channel | Update Behavior | Best For | Trade-offs |
|---|---|---|---|---|
| APT package | Debian package repository | Updates through normal APT upgrades | Most Debian desktops, servers, and build hosts | Version follows the Debian release, not the newest upstream Maven release |
| Apache binary tarball | Apache Maven downloads | Manual repeat with checksum and signature verification | Systems that need the current recommended Maven 3 release | You own PATH setup, update checks, and cleanup under /opt |
Debian 13 (Trixie) currently provides Maven 3.9.9 with the default JDK 21 package, Debian 12 (Bookworm) provides Maven 3.8.7 with default JDK 17, and Debian 11 (Bullseye) provides Maven 3.6.3 with default JDK 11. Apache’s download page currently recommends Maven 3.9.16 for Maven 3 users, while Maven 4 remains a preview branch for testing rather than production build hosts.
Install Apache Maven with APT
Refresh Debian’s package index so APT uses current package metadata:
sudo apt update
These commands use sudo for package changes. If your account cannot use sudo yet, add a user to sudoers on Debian before continuing.
Install Maven with the default JDK so Java compilation tools are available for project builds:
sudo apt install maven default-jdk
The maven package provides mvn. The default-jdk package adds javac, which Maven needs when a project compiles Java sources.
Verify the APT Maven Install
Confirm that your shell resolves Maven from Debian’s package path:
command -v mvn
Expected output for the APT method:
/usr/bin/mvn
Check Maven and the Java runtime Maven is using:
mvn -version
Example output from Debian 13:
Apache Maven 3.9.9 Maven home: /usr/share/maven Java version: 21.0.11, vendor: Debian, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Confirm the Java compiler is available:
javac -version
Example output from Debian 13:
javac 21.0.11
Release-specific version numbers differ. Debian 12 reports Java 17 and Maven 3.8.7, while Debian 11 reports Java 11 and Maven 3.6.3 from the default package repository.
Install Apache Maven from the Binary Tarball
The manual method installs the current recommended Apache Maven 3 binary under /opt. It keeps Debian’s package database separate from the upstream Maven files, but you must handle checksum and signature checks, PATH setup, updates, and removal yourself.
Install Manual Method Prerequisites
Install the JDK, CA certificates, curl, and gpg before downloading and verifying files from Apache over HTTPS:
sudo apt update
sudo apt install default-jdk ca-certificates curl gpg
Verify Java before continuing:
java -version
javac -version
Example output from Debian 13:
openjdk version "21.0.11" 2026-04-21 javac 21.0.11
Download and Verify the Apache Maven Tarball
This manual path has two separate phases: download the archive and verifier files, then prove the archive before extraction. Keep the same terminal open because the extraction step reuses MAVEN_VERSION, MAVEN_ARCHIVE, and the verification flag created by these Bash blocks.
cd /tmp
MAVEN_VERSION=$(curl -fsSL https://maven.apache.org/download.cgi | grep -Eo 'apache-maven-[0-9]+\.[0-9]+\.[0-9]+-bin\.tar\.gz' | sed -E 's/apache-maven-([0-9.]+)-bin\.tar\.gz/\1/' | sort -Vu | tail -n 1)
if [ -z "$MAVEN_VERSION" ]; then
echo "Could not determine the current Maven 3 release."
else
MAVEN_ARCHIVE="apache-maven-${MAVEN_VERSION}-bin.tar.gz"
echo "Using Apache Maven ${MAVEN_VERSION}."
curl -fsSL -o "$MAVEN_ARCHIVE" "https://dlcdn.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/${MAVEN_ARCHIVE}"
curl -fsSL -o "${MAVEN_ARCHIVE}.sha512" "https://downloads.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/${MAVEN_ARCHIVE}.sha512"
curl -fsSL -o "${MAVEN_ARCHIVE}.asc" "https://downloads.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/${MAVEN_ARCHIVE}.asc"
curl -fsSL -o maven-KEYS https://downloads.apache.org/maven/KEYS
echo "Downloaded ${MAVEN_ARCHIVE} and verifier files."
fi
The download block should identify the Maven release and confirm that the tarball, checksum, signature, and KEYS file were saved in /tmp.
Using Apache Maven 3.9.16. Downloaded apache-maven-3.9.16-bin.tar.gz and verifier files.
Run the verification block before extracting anything. The checksum proves the downloaded file matches Apache’s SHA-512 sidecar. The GPG commands import Apache’s KEYS file into a temporary keyring, verify the detached signature, then remove that temporary keyring so your normal GPG keyring is not changed.
maven_verified=no
if [ -z "${MAVEN_ARCHIVE:-}" ] || [ ! -f "$MAVEN_ARCHIVE" ]; then
echo "Maven archive is missing. Rerun the download block first."
else
if printf '%s %s\n' "$(cat "${MAVEN_ARCHIVE}.sha512")" "$MAVEN_ARCHIVE" | sha512sum -c -; then
checksum_ok=yes
else
checksum_ok=no
fi
GNUPGHOME=$(mktemp -d)
export GNUPGHOME
chmod 700 "$GNUPGHOME"
if gpg --batch --import maven-KEYS && gpg --batch --verify "${MAVEN_ARCHIVE}.asc" "$MAVEN_ARCHIVE"; then
signature_ok=yes
else
signature_ok=no
fi
rm -rf "$GNUPGHOME"
unset GNUPGHOME
if [ "$checksum_ok" = yes ] && [ "$signature_ok" = yes ]; then
maven_verified=yes
echo "Maven archive verification passed."
else
echo "Maven archive verification failed."
fi
fi
Relevant verification output when Apache Maven 3.9.16 is current:
apache-maven-3.9.16-bin.tar.gz: OK gpg: Good signature from "Slawomir Jaranowski <sjaranowski@apache.org>" [unknown] Maven archive verification passed.
GPG may also print a trust warning because Apache’s KEYS file was imported into a temporary local keyring. Continue only when the checksum reports OK, GPG reports a good signature, and the final line says Maven archive verification passed. If the resolver fails, open the Apache Maven download page, set MAVEN_VERSION manually to the recommended Maven 3 release, then rerun the download and verification lines. For more transfer options, see the curl command guide.
Extract Maven and Create the Stable Symlink
Extract the verified archive to /opt, then point /opt/maven at the versioned directory. The guard stops if /opt/maven already exists as a real directory instead of a symlink, which prevents ln from creating a nested link in the wrong place.
if [ "${maven_verified:-no}" != yes ]; then
echo "Verification has not passed in this shell; do not extract the archive."
elif [ -e /opt/maven ] && [ ! -L /opt/maven ]; then
echo "/opt/maven exists and is not a symlink; move it before continuing."
else
sudo tar xzf "$MAVEN_ARCHIVE" -C /opt/
sudo ln -sfn "/opt/apache-maven-${MAVEN_VERSION}" /opt/maven
readlink -f /opt/maven
fi
Expected output after a successful extraction:
/opt/apache-maven-3.9.16
The stable /opt/maven symlink lets PATH stay the same after future manual updates. The versioned directory remains available separately, which makes rollback easier if a project build fails after an update.
Add Manual Maven to PATH
Create a system-wide profile script that sets M2_HOME and prepends the manual Maven binary directory only when it is not already present. The quoted heredoc keeps $PATH and $M2_HOME literal while the file is written, and the case guard prevents duplicate PATH entries when the script is sourced more than once.
sudo tee /etc/profile.d/maven.sh >/dev/null <<'EOF'
export M2_HOME=/opt/maven
case ":$PATH:" in
*":$M2_HOME/bin:"*) ;;
*) export PATH="$M2_HOME/bin:$PATH" ;;
esac
EOF
Apply the profile script in the current shell, or open a new terminal so the profile is loaded automatically:
source /etc/profile.d/maven.sh
printf 'M2_HOME=%s\n' "$M2_HOME"
Expected output:
M2_HOME=/opt/maven
Verify the Manual Maven Install
Confirm the active Maven command comes from /opt/maven:
command -v mvn
Expected output for the manual method:
/opt/maven/bin/mvn
Check Maven and the Java runtime it selected:
mvn -version
Example output from Debian 13:
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5) Maven home: /opt/maven Java version: 21.0.11, vendor: Debian, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Build a Test Maven Project on Debian
A version check proves the launcher works, but a small build proves Maven can resolve plugins, compile Java, run tests, and produce a JAR. The quickstart archetype is pinned so the same sample builds across Debian 13, 12, and 11.
Generate a Maven Quickstart Project
Create a small Java project from the Maven quickstart archetype. The first run can download several plugins and archetype files, so the important success signal is Maven’s final build status.
mvn -B archetype:generate \
-DgroupId=com.example.app \
-DartifactId=my-app \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DinteractiveMode=false
Relevant output includes:
[INFO] BUILD SUCCESS
The command creates a my-app directory with a pom.xml, sample source file, and sample test.
Compile and Package the Project
Build the sample project. The compiler properties target Java 8 bytecode so the same test project works with Debian 11’s default JDK 11 through Debian 13’s default JDK 21:
cd my-app
mvn package -Dmaven.compiler.source=8 -Dmaven.compiler.target=8
test -f target/my-app-1.0-SNAPSHOT.jar && echo "JAR created"
Relevant output includes the test summary, Maven’s success line, and the final JAR check:
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS JAR created
Run the packaged class from the generated JAR:
java -cp target/my-app-1.0-SNAPSHOT.jar com.example.app.App
Expected output:
Hello World!
Older quickstart examples can fail on modern JDKs with messages such as
Source option 5 is no longer supportedorSource option 7 is no longer supported. Pinning the archetype and passing Java 8 compiler properties keeps the sample portable across the supported Debian releases.
Configure Maven Proxy Settings
If Maven cannot download dependencies behind a corporate proxy, configure ~/.m2/settings.xml for your user account.
Create the Maven settings directory and open the settings file with nano:
mkdir -p ~/.m2
nano ~/.m2/settings.xml
Add your proxy host and port:
<settings>
<proxies>
<proxy>
<id>corporate-proxy</id>
<active>true</active>
<protocol>http</protocol>
<host>proxy.example.com</host>
<port>8080</port>
</proxy>
</proxies>
</settings>
Save the file, then rerun the Maven command that failed. If your proxy requires authentication or separate HTTPS handling, add those fields according to your organization’s proxy policy.
Update Apache Maven on Debian
Update APT-Installed Maven
APT updates Debian’s Maven package with other packages from the enabled Debian sources. For a targeted Maven package refresh, use:
sudo apt update
sudo apt install --only-upgrade maven
The --only-upgrade option upgrades Maven only when it is already installed.
Update Manually Installed Maven
Manual Maven updates should stay deliberate because a new Maven branch can change plugin behavior in existing projects. Use a small root-owned helper so the update path repeats the same release lookup, checksum verification, signature verification, extraction, and symlink refresh every time.
sudo tee /usr/local/sbin/update-apache-maven >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
printf 'Missing required command: %s\n' "$1" >&2
exit 1
fi
}
for cmd in awk cat chmod curl gpg grep id mkdir mktemp rm sed sha512sum sort tail tar; do
need_cmd "$cmd"
done
if [ "$(id -u)" -ne 0 ]; then
printf 'Run this updater with sudo: sudo /usr/local/sbin/update-apache-maven\n' >&2
exit 1
fi
if [ -e /opt/maven ] && [ ! -L /opt/maven ]; then
printf '/opt/maven exists but is not a symlink; move it before updating.\n' >&2
exit 1
fi
current_version=""
if [ -x /opt/maven/bin/mvn ]; then
current_version=$(/opt/maven/bin/mvn -version | awk '/^Apache Maven / {print $3; exit}')
fi
latest_version=$(curl -fsSL https://maven.apache.org/download.cgi | grep -Eo 'apache-maven-[0-9]+\.[0-9]+\.[0-9]+-bin\.tar\.gz' | sed -E 's/apache-maven-([0-9.]+)-bin\.tar\.gz/\1/' | sort -Vu | tail -n 1)
if [ -z "$latest_version" ]; then
printf 'Could not determine the current Maven 3 release.\n' >&2
exit 1
fi
if [ "$current_version" = "$latest_version" ]; then
printf 'Apache Maven %s is already installed at /opt/maven.\n' "$latest_version"
exit 0
fi
workdir=$(mktemp -d)
cleanup() {
rm -rf "$workdir"
}
trap cleanup EXIT
archive="apache-maven-${latest_version}-bin.tar.gz"
base_url="https://downloads.apache.org/maven/maven-3/${latest_version}/binaries"
download_url="https://dlcdn.apache.org/maven/maven-3/${latest_version}/binaries/${archive}"
printf 'Downloading Apache Maven %s...\n' "$latest_version"
curl -fsSLo "$workdir/$archive" "$download_url"
curl -fsSLo "$workdir/${archive}.sha512" "$base_url/${archive}.sha512"
curl -fsSLo "$workdir/${archive}.asc" "$base_url/${archive}.asc"
curl -fsSLo "$workdir/maven-KEYS" https://downloads.apache.org/maven/KEYS
cd "$workdir"
printf 'Verifying SHA-512 checksum...\n'
printf '%s %s\n' "$(cat "${archive}.sha512")" "$archive" | sha512sum -c -
printf 'Verifying Apache release signature...\n'
GNUPGHOME="$workdir/gnupg"
export GNUPGHOME
mkdir -p "$GNUPGHOME"
chmod 700 "$GNUPGHOME"
gpg --batch --import maven-KEYS
gpg --batch --verify "${archive}.asc" "$archive"
unset GNUPGHOME
printf 'Installing Apache Maven %s under /opt...\n' "$latest_version"
tar xzf "$archive" -C /opt/
ln -sfn "/opt/apache-maven-${latest_version}" /opt/maven
mvn_output=$(/opt/maven/bin/mvn -version)
printf '%s\n' "$mvn_output" | sed -n '1,3p'
printf 'Apache Maven %s is active at /opt/maven.\n' "$latest_version"
EOF
sudo chmod 755 /usr/local/sbin/update-apache-maven
The helper is intentionally verbose. It checks required commands, refuses to overwrite a non-symlink /opt/maven, uses a temporary GPG keyring, removes temporary download files automatically, and leaves older versioned Maven directories in place for rollback.
Run the helper when you want to check for a newer Apache Maven 3 release:
sudo /usr/local/sbin/update-apache-maven
Relevant output when an update is installed on Debian 13:
Downloading Apache Maven 3.9.16... Verifying SHA-512 checksum... apache-maven-3.9.16-bin.tar.gz: OK Verifying Apache release signature... gpg: Good signature from "Slawomir Jaranowski <sjaranowski@apache.org>" [unknown] Installing Apache Maven 3.9.16 under /opt... Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5) Maven home: /opt/maven Java version: 21.0.11, vendor: Debian, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Apache Maven 3.9.16 is active at /opt/maven.
If the current recommended Maven 3 release is already active, the helper exits without downloading anything:
Apache Maven 3.9.16 is already installed at /opt/maven.
Do not automate this manual update with cron. Keep the verification visible, review Apache Maven release notes, and test your builds before making a new manual version your default. After your projects pass, list old versioned directories with find /opt -maxdepth 1 -type d -name 'apache-maven-*' -print and remove only the versions you no longer need.
Remove Apache Maven from Debian
Remove APT-Installed Maven
Remove Maven from APT when you installed the Debian package:
sudo apt remove maven
sudo apt autoremove
Review the autoremove package list before confirming so APT does not remove dependencies you still need for other Java tools. The default-jdk package remains installed because the install command added it explicitly; remove it separately only when no other Java workflow needs the compiler.
Clear Bash’s command cache and confirm mvn is gone:
hash -r
command -v mvn || echo "mvn not found"
mvn not found
Remove Manually Installed Maven
The manual cleanup deletes versioned Maven directories under
/opt, the/opt/mavensymlink, the profile script that adds manual Maven to PATH, and the optional updater helper. Keep any versioned directory you still need before running the removal command.
List the manual Maven paths first:
for path in /opt/maven /opt/apache-maven-* /etc/profile.d/maven.sh /usr/local/sbin/update-apache-maven; do
[ -e "$path" ] && printf '%s\n' "$path"
done
Example output before removing a manual Maven 3.9.16 install:
/opt/maven /opt/apache-maven-3.9.16 /etc/profile.d/maven.sh /usr/local/sbin/update-apache-maven
Remove the manual Maven files and clear the command cache:
sudo rm -rf /opt/apache-maven-*
sudo rm -f /opt/maven /etc/profile.d/maven.sh /usr/local/sbin/update-apache-maven
hash -r
Check the active command afterward:
command -v mvn || echo "mvn not found"
If APT Maven is still installed, the command may print /usr/bin/mvn. Remove the APT package too if you want no Maven command available.
Remove Maven User Data
The
~/.m2directory can contain downloaded dependencies, plugin caches, proxy settings, repository credentials, and custom build configuration. Back up anything you need before deleting it.
Inspect the user-level Maven directory before deleting it:
ls -ld ~/.m2 ~/.m2/repository ~/.m2/settings.xml 2>/dev/null
Delete the user-level Maven directory only when you no longer need its cache or settings:
rm -rf ~/.m2
Troubleshoot Apache Maven on Debian
Maven Command Not Found
If the shell cannot find mvn after a manual install, the profile script has not been loaded or /opt/maven/bin is not on PATH.
bash: mvn: command not found
Check whether the Maven binary exists and whether your shell can resolve it:
command -v mvn || echo "mvn not found in PATH"
ls -l /opt/maven/bin/mvn
Reload the manual Maven profile script and check the path again:
source /etc/profile.d/maven.sh
command -v mvn
mvn -version
Expected path after a manual install:
/opt/maven/bin/mvn
JAVA_HOME Is Missing or Incorrect
Maven can fail when JAVA_HOME points to a missing JDK path, even when java is available on PATH.
The JAVA_HOME environment variable is not defined correctly, this environment variable is needed to run this program.
Check the current value and the Java executable path:
printf 'JAVA_HOME=%s\n' "$JAVA_HOME"
readlink -f /usr/bin/java
Example diagnostic output:
JAVA_HOME=/bad /usr/lib/jvm/java-21-openjdk-amd64/bin/java
Set JAVA_HOME to Debian’s default JDK path when your Maven workflow needs a persistent value:
printf '%s\n' 'export JAVA_HOME=/usr/lib/jvm/default-java' | sudo tee /etc/profile.d/java-home.sh >/dev/null
source /etc/profile.d/java-home.sh
mvn -version
Example output from Debian 13 with manual Maven should show Maven using the default JDK path instead of the stale value:
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5) Java version: 21.0.11, vendor: Debian, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Projects pinned to a specific Java branch should install that branch separately, such as OpenJDK 17 on Debian or OpenJDK 21 on Debian, then point JAVA_HOME at the matching JDK.
Dependency Downloads Fail Behind a Proxy
If Maven starts but cannot download plugins or dependencies, check whether your network requires a proxy. Configure ~/.m2/settings.xml, then rerun the same Maven command. For DNS-specific download failures, curl host-resolution troubleshooting can help separate a proxy problem from a resolver problem.
Conclusion
Apache Maven is installed on Debian with a verified JDK, a working mvn command, and a sample project build path that works across Debian 13, 12, and 11. Use APT for the lowest-maintenance setup, or keep the Apache tarball method when a project needs the current upstream Maven 3 release. Next, install Git on Debian for source control or install Docker on Debian for containerized build environments.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>