How to Install Apache Maven on Debian 13, 12 and 11

Last updated Tuesday, May 19, 2026 10:19 am Joshua James 8 min read

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.

MethodSource or ChannelUpdate BehaviorBest ForTrade-offs
APT packageDebian package repositoryUpdates through normal APT upgradesMost Debian desktops, servers, and build hostsVersion follows the Debian release, not the newest upstream Maven release
Apache binary tarballApache Maven downloadsManual repeat with checksum and signature verificationSystems that need the current recommended Maven 3 releaseYou 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 supported or Source 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/maven symlink, 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 ~/.m2 directory 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.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Let us know you are human: