How To

Install Apache Maven on Fedora 45 / 44 and Rocky Linux 10

On Fedora 45, sudo dnf install maven installs 256 packages and 816 MiB, with PipeWire and GTK 3 among them. That is a lot of desktop for a build tool that never opens a window. The weight comes from weak dependencies, which dnf installs by default. Turn them off for that one transaction and name the JDK compiler package yourself, and you get 61 packages and 276 MiB with javac still in place.

Original content from computingforgeeks.com - post 4762

You’ll install Maven the lean way from the distro repositories, check which JDK it runs on, and add the upstream release from apache.org for when the packaged one lags. Then you’ll build a sample project, pin the Maven version with the wrapper and put Maven behind a proxy, which it handles differently from most tools. Every step ran on Fedora 45 in October 2026, with the package installs repeated on Fedora 44 and Rocky Linux 10.2 and the sample build on Rocky. AlmaLinux 10 and RHEL 10 ship the same AppStream packages as Rocky, but we only ran Rocky.

Install Maven with dnf

Both distros carry Maven in their main repositories, so there is nothing to enable first. The packaged version trails upstream, though, and each distro ties Maven to a particular JDK through a small binding package:

Fedora 45Fedora 44Rocky Linux 10
Maven package3.9.113.9.113.9.9
JDK bindingsmaven-openjdk25 (default), maven-unboundmaven-openjdk25 (default), maven-unboundmaven-openjdk21 (default), maven-openjdk25
Default install256 packages, 816 MiB283 packages, 844 MiB194 packages, 634 MiB
Without weak dependencies61 packages, 276 MiB68 packages, 278 MiB59 packages, 239 MiB

Upstream released Maven 3.10.0 at the end of September 2026. Fedora also packages a maven4 build of a 4.0 release candidate, but Maven 4 is not GA yet, so leave it alone for real projects. The size gap in the table comes from one chain of dependencies, and two of its four links are only recommendations:

  1. maven-openjdk25 requires just the headless JDK
  2. the same binding recommends java-25-openjdk-devel for javac
  3. the devel package requires the full JDK and its X11 libraries
  4. the full JDK recommends gtk3 and pipewire

You can see the recommendations yourself with repoquery:

dnf -q repoquery --recommends java-25-openjdk java-25-openjdk-headless maven maven-openjdk25

On Fedora 45 the three recommendations are the desktop toolkit, the compiler package and the audio server:

gtk3(x86-64)
java-25-openjdk-devel
pipewire(x86-64)

So the lean install turns weak dependencies off for this one transaction and asks for the compiler package by name, because java-25-openjdk-devel is itself one of the weak dependencies that the setting would otherwise drop. On Fedora:

sudo dnf install -y --setopt=install_weak_deps=False maven java-25-openjdk-devel

Rocky Linux 10 binds Maven to JDK 21 by default, so name that compiler package instead. If you need JDK 25 there too, our JDK 25 guide for Rocky Linux 10 covers it:

sudo dnf install -y --setopt=install_weak_deps=False maven java-21-openjdk-devel

The setting applies only to that command, so later installs on the same machine still pull in weak dependencies as usual. On a desktop that already has GTK and PipeWire, the plain sudo dnf install maven costs far less and the flag hardly matters. It pays off on servers, CI runners and container images, where 540 MiB of mostly desktop packages is pure attack surface and download time. Confirm Maven and the compiler:

mvn -v
javac -version

Fedora 45 reports the packaged Maven running on JDK 25, with the compiler from the same build:

Apache Maven 3.9.11 (Red Hat 3.9.11-16)
Maven home: /usr/share/maven
Java version: 25.0.4.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-25-openjdk
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "7.2.7-300.fc45.x86_64", arch: "amd64", family: "unix"
javac 25.0.4.1

The screenshot shows the lean transaction on the Fedora 45 box, followed by the version check. The package count went from 555 before the install to 616 after it, and neither gtk3 nor pipewire got installed.

Lean dnf install of Maven on Fedora 45 with weak dependencies off, 61 packages, and mvn -v on JDK 25

If you would rather keep Maven off the system entirely and only need a newer release, skip ahead to the apache.org tarball. The two installs can live side by side, which has its own catch with sudo, covered there.

Check which JDK Maven runs on

With the default binding, packaged Maven ignores the java on your PATH. Its mvn launcher script reads JAVA_HOME from /etc/java/maven.conf whenever you haven’t set JAVA_HOME yourself, and that file comes from a binding package. Fedora offers two: maven-openjdk25, the default, which pins JDK 25, and maven-unbound, which leaves the choice to JAVA_HOME or the system java. JDK 25 is the only long-term-support JDK in the Fedora 45 repositories; the other one listed, java-latest-openjdk-devel, is a JDK 27 early-access build. Our Java on Fedora guide covers adding JDK 21 or 17 when a project needs one. Read the active binding:

cat /etc/java/maven.conf

On Fedora 45 with the default binding, it points at the JDK 25 runtime:

JAVA_HOME=/usr/lib/jvm/jre-25-openjdk

To see the difference between the two bindings, we installed the JDK 27 build, made it the system java with alternatives and left JAVA_HOME unset. With maven-openjdk25 in place, Maven stayed on JDK 25 regardless. Swapping the binding is a single transaction:

sudo dnf swap -y maven-openjdk25 maven-unbound

After the swap, the same check reports the JDK the system java points to:

/usr/bin/mvn -v 2>&1 | grep "Java version"

Maven now runs on the early-access build:

Java version: 27-ea, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-latest-openjdk

To go back, run sudo dnf swap -y maven-unbound maven-openjdk25, which is how we restored the box. Pick maven-unbound when you manage JDKs yourself, for example a Temurin 21 install that a project requires. Keep the default when you want Maven to stay on the distro JDK no matter what else gets installed. A global JAVA_HOME beats both: with JAVA_HOME set to the JDK 27 directory, even the default maven-openjdk25 binding ran on JDK 27. So if you export JAVA_HOME system-wide following our JAVA_HOME guide for Rocky and Fedora, that variable, not the binding, decides which JDK packaged Maven uses.

Rocky Linux 10 has no maven-unbound, but it ships bindings for both JDK 21 and JDK 25, and they aren’t exclusive. Install maven-openjdk25 next to the default and both packages stay installed, while /usr/share/jpbinding/maven.conf becomes a symlink to whichever one went in last. Maven silently moves to JDK 25, and the packaged 3.9.9 build then prints a block of warnings from its bundled jansi library on every run:

WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/usr/share/maven/lib/jansi-2.4.1.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

Fedora’s newer 3.9.11 build printed no such warning on the same JDK. On our Rocky box, removing maven-openjdk25 flipped the symlink back to openjdk21. For one command or one user, override the binding instead of touching packages: prefix the command with JAVA_HOME=/usr/lib/jvm/java-21-openjdk, or put a JAVA_HOME= line in ~/.mavenrc. Both beat the binding on Rocky. Whatever you pick, trust the Java version line of mvn -v, not java -version, because the two can disagree.

Install the latest Maven from apache.org

Use the tarball when you need the current upstream release on any of these systems, which matters when a plugin or your team’s build requires a newer Maven than the distro packages. It needs a JDK on the PATH and nothing else; the lean dnf install above already provides one, or install java-25-openjdk-devel (Fedora) or java-21-openjdk-devel (Rocky) on its own. Detect the latest 3.x release from the Apache download site rather than hardcoding it:

MVN_VER=$(curl -s https://dlcdn.apache.org/maven/maven-3/ | grep -oE "3\.[0-9]+\.[0-9]+/" | sort -V | tail -1 | tr -d /)
echo $MVN_VER

On our boxes that was the September release.

3.10.0

Download the binary archive from the CDN and its SHA-512 checksum from the main Apache server, then verify one against the other. Fetching the checksum from a different host than the archive is the point: a tampered mirror cannot hand you a matching pair.

cd /tmp
curl -sSLO https://dlcdn.apache.org/maven/maven-3/${MVN_VER}/binaries/apache-maven-${MVN_VER}-bin.tar.gz
curl -sSLO https://downloads.apache.org/maven/maven-3/${MVN_VER}/binaries/apache-maven-${MVN_VER}-bin.tar.gz.sha512
echo "$(cat apache-maven-${MVN_VER}-bin.tar.gz.sha512)  apache-maven-${MVN_VER}-bin.tar.gz" | sha512sum -c

Anything other than OK means a broken or altered download, so stop there:

apache-maven-3.10.0-bin.tar.gz: OK

Extract it under /opt and point a version-free symlink at it. Upgrading later is a new extract plus one ln -sfn, and rolling back is the same command with the old directory:

sudo tar -xzf /tmp/apache-maven-${MVN_VER}-bin.tar.gz -C /opt
sudo ln -sfn /opt/apache-maven-${MVN_VER} /opt/maven

Put /opt/maven/bin first on the PATH for every login shell with a profile script. The escaped \$ keeps the variables literal inside the file, so they expand when a shell reads it rather than now:

echo "export MAVEN_HOME=/opt/maven" | sudo tee /etc/profile.d/maven.sh
echo "export PATH=\$MAVEN_HOME/bin:\$PATH" | sudo tee -a /etc/profile.d/maven.sh
source /etc/profile.d/maven.sh

Check that your shell now finds the upstream build. We did not set JAVA_HOME at all; the tarball’s mvn found JDK 25 through the java on the PATH:

mvn -v

The Maven home line is the one that tells you which install answered:

Apache Maven 3.10.0 (c43a36b8d67be7e0805a411bc0898af1a51f5472)
Maven home: /opt/maven
Java version: 25.0.4.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-25-openjdk
Default locale: en_US, platform encoding: UTF-8, time zone: UTC
OS name: "linux", version: "7.2.7-300.fc45.x86_64", arch: "amd64", family: "unix"

With the dnf package still installed, there are now two mvn commands on the machine, and which one runs depends on how you start it. A fresh login shell lists the tarball first:

which -a mvn

Behind it sits the packaged one.

/opt/maven/bin/mvn
/usr/bin/mvn

Under sudo the order flips. Fedora’s sudoers replaces your PATH with its secure_path, which does not include /opt/maven/bin, so sudo mvn -v reported the packaged 3.9.11 from /usr/share/maven while sudo -i mvn -v, which runs root’s login scripts, reported 3.10.0. Builds should not run as root anyway: they write root-owned files into target/ and ~/.m2. If you want only one Maven, remove the package with sudo dnf remove maven once the tarball works. After the lean install that removes 28 packages and leaves the JDK alone, because you asked for java-25-openjdk-devel by name. After a plain dnf install maven, the JDK arrived as a dependency, so read the removal list before you confirm, or the tarball loses its java.

Build a sample project

A quickstart project proves the whole chain: Maven downloads plugins from Maven Central, javac compiles, the tests run and a JAR comes out. Always pass -DarchetypeVersion. Without it, the archetype plugin falls back to an old version of the quickstart template, and our generated pom.xml came with JUnit 3.8.1. Version 1.5 is the current one and generates a JUnit 5 project that compiles for Java 17:

cd ~
mvn -B archetype:generate -DgroupId=com.example -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false

The run ends with the project directory and a success line. The warning about .mvn only says the template’s empty .mvn folder was already created:

[WARNING] CP Don't override file /home/josphat/my-app/.mvn
[INFO] Project created from Archetype in dir: /home/josphat/my-app
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

The package phase runs every phase before it, so one command compiles, tests and builds the JAR:

cd ~/my-app
mvn -B package

Look for zero failures and the JAR path. The first run took 19.6 seconds on our Fedora 45 VM, nearly all of it downloads, and a second mvn package with a warm cache finished in 4.5 seconds:

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.047 s -- in com.example.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] Building jar: /home/josphat/my-app/target/my-app-1.0-SNAPSHOT.jar
[INFO] BUILD SUCCESS
[INFO] Total time:  19.570 s

Run the class from the JAR:

java -cp target/my-app-1.0-SNAPSHOT.jar com.example.App

It prints Hello World!, the same result we got on Rocky Linux 10 with the packaged Maven and JDK 21. That one small project left 86 MiB and 118 JARs in ~/.m2/repository, which is worth knowing before you point a CI runner at a small disk. The local repository grows with every project and is safe to delete; Maven downloads what it needs again.

mvn package on Fedora 45 with one passing test, BUILD SUCCESS and the sample JAR printing Hello World

You’ll run a handful of phases and goals against a project like this one most days.

CommandWhat it does
mvn compileCompiles src/main/java into target/classes
mvn testCompiles, then runs the unit tests
mvn packageCompiles, tests and builds the JAR or WAR
mvn installPackages, then copies the artifact into ~/.m2/repository for other local projects
mvn clean package -DskipTestsRebuilds from scratch without running tests
mvn dependency:treeShows every dependency and where it came from
mvn help:effective-settingsPrints the settings Maven actually uses, including proxies

Pin the Maven version with the wrapper

On a team, the installed Maven matters less than you would think, because the project can carry its own. The Maven Wrapper adds a small mvnw script and a properties file to the repository. Its first run downloads the Maven version named there into ~/.m2/wrapper, and from then on every developer and CI job builds with exactly that release, whatever their distro ships. We’d commit the wrapper on any project more than one person builds. Generate it from the project directory:

mvn -B -q wrapper:wrapper
cat .mvn/wrapper/maven-wrapper.properties

Maven records its own version in the file, here the tarball’s 3.10.0.

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

To hold a project on a specific release instead, regenerate with -Dmaven. This pins it to 3.9.11, the version Fedora packages, and the next ./mvnw run fetches that release:

./mvnw -B -q wrapper:wrapper -Dmaven=3.9.11
./mvnw -v

Check the first line of the version output for the switch.

Apache Maven 3.9.11 (3e54c93a704957b63ee3494413a2b544fd3d825b)

Each pinned version lands in its own directory under ~/.m2/wrapper/dists/, so switching between projects costs one download per version, not one per build. Three things go into version control so everyone gets the same Maven:

  • mvnw for Linux and macOS
  • mvnw.cmd for Windows
  • the .mvn/wrapper/ directory with its properties file

The Maven download itself lands in each user’s ~/.m2/wrapper on the first run, so the repository only carries those small files. The Maven Wrapper documentation covers the other distribution types if your CI cannot run the default script-only mode.

Send Maven through a proxy

Maven ignores the proxy environment variables that curl and pip respect. We tested that with a Squid proxy on a second machine and direct outbound HTTP and HTTPS blocked on the Fedora 45 box, then cleared ~/.m2/repository so the build had to download. With HTTPS_PROXY and HTTP_PROXY exported, and again with -Dhttps.proxyHost and -Dhttps.proxyPort passed through MAVEN_OPTS, the tarball’s Maven 3.10.0 tried to connect directly and failed the same way:

[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Non-resolvable import POM: The following artifacts could not be resolved: org.junit:junit-bom:pom:5.11.0 (absent): Could not transfer artifact org.junit:junit-bom:pom:5.11.0 from/to central (https://repo.maven.apache.org/maven2): Network is unreachable @ line 21, column 19
[ERROR] 'dependencies.dependency.version' for org.junit.jupiter:junit-jupiter-api:jar is missing. @ line 32, column 17

The exact error depends on how your network blocks direct traffic: a timeout, “Connection refused” or an unknown host are just as likely. The giveaway is a transfer failure to repo.maven.apache.org while curl gets through the same proxy, which ours did with a 200. If you set a system-wide proxy following our proxy guide for Rocky Linux and Fedora, Maven is one of the tools it does not reach. The Maven project’s own proxy guide says some transports can pick up Java system properties but calls that implementation-specific, not the supported way. The supported way is a <proxies> block in your user settings file:

mkdir -p ~/.m2
vim ~/.m2/settings.xml

This is the exact file from the test. Replace the host and port with your proxy’s; nonProxyHosts takes pipe-separated names with wildcards for anything Maven should reach directly, such as an internal Nexus:

<settings>
  <proxies>
    <proxy>
      <id>office-proxy</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>10.0.1.52</host>
      <port>3128</port>
      <nonProxyHosts>localhost|127.0.0.1|*.example.com</nonProxyHosts>
    </proxy>
  </proxies>
</settings>

Run the same build again:

mvn -B package

With the proxy block in place it went through, tests and all.

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

Squid’s access log on the proxy recorded 26 CONNECT repo.maven.apache.org:443 tunnels from the Fedora box during these tests, so the traffic really took the proxy rather than some leftover route. The packaged 3.9.11 at /usr/bin/mvn read the same file and built too, since both installs share ~/.m2/settings.xml. The old version of this page used <protocol>https</protocol>, and against our plain HTTP Squid that worked as well. We would still use http, because it describes how Maven talks to a proxy like Squid on port 3128. When a proxy needs credentials, <username> and <password> go inside the same <proxy> element. That puts a password in a plain text file in your home directory, so keep it at mode 600. To confirm what Maven actually loaded, run mvn help:effective-settings and look for your proxy in the <proxies> section of its output. The Maven proxy guide lists the remaining fields.

Maven on Fedora 45 failing with HTTPS_PROXY exported, then building through Squid after a proxies block in settings.xml

For a Java web app, the JAR or WAR that Maven produces usually ends up on an application server next; our Apache Tomcat install guide picks up from there.

Keep reading

How to Upgrade Fedora 44 to Fedora 45 (GUI, CLI and Rollback) Fedora How to Upgrade Fedora 44 to Fedora 45 (GUI, CLI and Rollback) Enable RPM Fusion and Install Multimedia Codecs on Fedora 45 / 44 / 43 Fedora Enable RPM Fusion and Install Multimedia Codecs on Fedora 45 / 44 / 43 Harden Fedora 45 / 44 for Desktop and Server: Complete Guide Fedora Harden Fedora 45 / 44 for Desktop and Server: Complete Guide Switch Fedora From GRUB to systemd-boot With bootctl Fedora Switch Fedora From GRUB to systemd-boot With bootctl DNF5 Cheatsheet for Fedora 45 / 44: Tested Commands and Fixes Fedora DNF5 Cheatsheet for Fedora 45 / 44: Tested Commands and Fixes How To Install Microsoft Teams on Fedora 45 / 44 Fedora How To Install Microsoft Teams on Fedora 45 / 44

Leave a Comment

Press ESC to close