mvn Command Not Found: Install and Fix PATH on Any OS
Fix mvn command not found fast: diagnose PATH vs missing install, set up macOS, Linux, and Windows, and use the wrapper..
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
- ✓A terminal on macOS, Linux, or Windows
- ✓Admin rights to install software
- ✓A Java JDK installed or ready to install
- which mvn tells the truth: empty output means Maven is not installed or not on PATH
- Install with Homebrew on macOS, apt or SDKMAN on Linux, Chocolatey on Windows
- Set M2_HOME to the install dir and add $M2_HOME/bin to PATH, then source the profile
- If new terminals forget mvn, your export is sitting in the wrong profile file
- For teams and CI, commit the mvnw wrapper so nobody needs a manual install
Think of your terminal as a receptionist with a short list of rooms to check — that's your PATH. When you ask for mvn, the receptionist only looks in those listed rooms. If Maven was installed in a room that's not on the list, the receptionist says nobody's here even though Maven is sitting in the building. Fixing it means either moving Maven into a listed room or adding its room to the list. The Maven wrapper skips the whole problem by keeping a personal copy of Maven inside your project.
You type mvn -version, hit enter, and the terminal answers back: mvn: command not found. Your code is fine, your pom is fine, but nothing Maven-related will run. If you're new to Java tooling, it feels like the machine is broken.
It's not broken — the shell just can't find the mvn program. That happens for two very different reasons: Maven was never installed, or it was installed somewhere your PATH doesn't cover. Guessing wrong wastes an afternoon of reinstalls that were never needed.
This guide sorts it out fast. You'll learn the one command that tells installed from not-on-PATH, then follow the exact install steps for macOS, Linux, and Windows. You'll set M2_HOME and PATH correctly, fix the classic profile-not-sourced trap where new terminals forget mvn, and see why the Maven wrapper (mvnw) is often the better answer for teams.
By the end you'll have mvn working in every terminal, know how to keep it working in CI images that ship without Maven, and never reinstall a tool that's already sitting on your disk.
Not Installed vs Not on PATH: Diagnosing Which One You've Got
The shell's error message is blunt: it searched every directory in your PATH and found no program called mvn. That leaves exactly two possibilities, and the fix for each is completely different. If Maven was never installed, you need to install it. If it's installed outside PATH, reinstalling changes nothing — you need to tell the shell where it lives.
The which command settles it in one second. Run which mvn: if it prints a path like /opt/homebrew/bin/mvn, Maven is installed and reachable, so your failing session must have a different PATH. If which prints nothing, check whether Maven files exist with ls on the common locations (/opt/maven, /usr/share/maven, ~/maven). Files present plus empty which means a PATH gap. No files anywhere means a genuine missing install.
A useful middle test is running the full path directly. If /opt/maven/bin/mvn -version prints Maven's banner, the binary is healthy and only PATH is wrong. That one result saves you from a pointless reinstall and points you at the export fix in a later section.
Build this diagnosis into muscle memory and teach it to your team. Every mvn not found report should start with which mvn and echo $PATH pasted into the thread. Half of all cases resolve right there without touching an installer.
Installing Maven on macOS: Homebrew and Manual Setup
On macOS the fastest path is Homebrew: brew install maven gives you a current Maven wired into /opt/homebrew/bin, which is already on PATH for most setups. Verify with mvn -version and you're done in under a minute. When Homebrew upgrades Maven unexpectedly, pin with brew pin maven or switch to the manual method for version control.
The manual install takes five minutes and gives you exact version control. Download the -bin.tar.gz for your chosen version from the Apache archive, unpack it under /opt/maven, and point M2_HOME there. Always grab the binary archive, not the source archive — the source bundle won't contain a runnable mvn. Verify the download's checksum when the network is untrusted.
Apple Silicon versus Intel changes nothing about Maven itself since it runs on the JVM, but it changes where Homebrew lives (/opt/homebrew versus /usr/local). If mvn works under one architecture's terminal and not another, compare PATH in each — Rosetta terminals can load different profiles. The manual /opt/maven path avoids the issue entirely because it's architecture-neutral.
Whichever route you pick, finish with mvn -version in a brand-new terminal window, not the one where you ran the install. That fresh-shell check catches profile mistakes immediately, while reusing the install terminal hides them until tomorrow morning.
Installing Maven on Linux and Windows: apt, SDKMAN, and Chocolatey
On Debian and Ubuntu, sudo apt install maven is the one-line answer. It's fast and integrated with the system, but the version lags upstream — fine for learning, risky when your project needs a newer Maven. Check mvn -version against your project's requirements before committing to apt as your only source.
SDKMAN is the better answer when versions matter. It installs Maven per user under ~/.sdkman, lets you run sdk install maven with an exact version, and switches with sdk default or sdk use. Multiple projects needing different Maven versions coexist peacefully. Because it's per user, it needs no sudo and works on shared CI agents without touching the system.
On Windows, Chocolatey gives you the same one-liner: choco install maven run as Administrator. It sets PATH for you in most cases. If you'd rather avoid package managers, unzip the binary archive to C:\Program Files\Maven and add its bin directory to the system PATH through the Environment Variables dialog, then open a new prompt.
After any method, confirm both mvn -version and the version your project expects. If they disagree, you've installed Maven but not the right Maven — switch with SDKMAN, pin the Chocolatey version, or point M2_HOME at the manual install from the previous section.
Setting M2_HOME and Updating PATH Correctly
M2_HOME and PATH do different jobs and you need both set correctly. M2_HOME points at Maven's install root — the directory containing bin and lib, such as /opt/maven. Some scripts, IDEs, and CI plugins read it to locate Maven's libraries. PATH is the colon-separated list your shell searches for commands; adding $M2_HOME/bin to it is what actually makes typing mvn work.
Order matters in PATH. Prepend with export PATH="$M2_HOME/bin:$PATH" so your chosen Maven wins over any older copy earlier in the search. A common trap is appending behind /usr/bin where an ancient system Maven shadows the new one — mvn -version then reports the wrong version and you'll swear the install failed. Run which mvn after exporting to confirm which binary the shell picks.
JAVA_HOME matters just as much. Maven runs on Java and needs a full JDK, so export JAVA_HOME to the JDK root and confirm javac -version succeeds. If java -version works but javac is missing, you've got a JRE — Maven's startup checks will fail even with a perfect M2_HOME and PATH.
Persist the exports in the right file so they survive reboots. On zsh that's typically ~/.zprofile for login shells; on bash for macOS it's ~/.bash_profile. After editing, prove it with a fresh terminal running mvn -version — sourcing in the current window only proves the current window.
Profile Not Sourced: Why New Terminals Forget mvn
Here's the most reported variant of this error: mvn works in the terminal where you installed it, then fails in every new window. Nothing uninstalled it overnight — your export simply lived only in that window's memory. Each new terminal starts fresh from its profile files, and if your export isn't in the file it reads, Maven vanishes.
Shells don't all read the same files. Zsh login shells read ~/.zprofile, interactive zsh reads ~/.zshrc, bash on macOS reads ~/.bash_profile, and bash on Linux often reads ~/.bashrc. An export in ~/.bashrc while you run zsh might as well not exist. Confirm your shell with echo $SHELL, then check which file holds your Maven lines with a grep across all five candidates.
The fix is mechanical: move the M2_HOME and PATH exports into the file your shell actually loads, source that file once, and verify in a new terminal. For teams, document both the zsh and bash locations so the next hire doesn't repeat the archaeology. For scripts and IDEs launched from the dock, remember they may not load any profile — set PATH in the IDE's environment settings or launch it from a terminal that has.
CI has its own version of this trap: each pipeline step may start a fresh non-login shell that skips profiles entirely. That's why pipelines should export PATH inline or use the environment block rather than relying on ~/.bashrc. A setup step that echoes $PATH to the log makes the gap visible instead of mysterious.
Using the Maven Wrapper and Fixing CI Images Without Maven
The Maven wrapper (mvnw) ends this entire category of problem for teams. It's a small script plus a .mvn/wrapper directory committed to your repo. When anyone runs ./mvnw, it downloads the pinned Maven version on first use and reuses it after. Nobody installs Maven by hand, nobody drifts to a different version, and mvn not found becomes someone else's problem.
Adopting it takes one command on a machine that already has Maven: mvn wrapper:wrapper pins the version and generates mvnw, mvnw.cmd, and the wrapper config. Commit all three — a missing .mvn/wrapper directory is the most common broken-wrapper report. Then update your README and scripts to call ./mvnw instead of mvn. Windows contributors use mvnw.cmd the same way.
CI images are the second half of this story. Minimal images like plain JDK or Alpine tags omit Maven to stay small, so pipelines fail with mvn: command not found while laptops stay green. You have three good answers: run ./mvnw so the job self-provisions Maven, add an explicit setup step with the platform's Java action or tool installer, or switch to a pinned image that bundles both, such as maven:3.9-eclipse-temurin-17.
Whichever you choose, log the toolchain first. A pipeline that prints mvn -version or ./mvnw -version before compiling turns the next missing-tool failure into a one-line diagnosis. Pin the image tag too — floating tags can swap a Maven-bundled variant for a slim one overnight, exactly the incident described above.
A Slim CI Image Dropped Maven and Blocked Every Java Deploy
- Pin CI image tags to variants that include your build tools — floating tags can silently drop Maven overnight.
- Log mvn -version as the first pipeline step so missing-tool failures are obvious in seconds, not after an hour of code review.
- Commit the Maven wrapper so any agent can build even when a system Maven goes missing.
| File | Command / Code | Purpose |
|---|---|---|
| diagnose-mvn-missing.sh | which mvn | Not Installed vs Not on PATH |
| install-maven-macos.sh | brew install maven | Installing Maven on macOS |
| install-maven-linux-windows.sh | sudo apt update && sudo apt install -y maven | Installing Maven on Linux and Windows |
| set-m2home-path.sh | export M2_HOME=/opt/maven | Setting M2_HOME and Updating PATH Correctly |
| maven-wrapper-ci.sh | ls mvnw .mvn/wrapper/maven-wrapper.properties | Using the Maven Wrapper and Fixing CI Images Without Maven |
Key takeaways
Common mistakes to avoid
5 patternsReinstalling Maven when it's just missing from PATH
Adding PATH exports to the wrong shell profile file
Installing a JRE instead of a JDK and blaming Maven
Telling every contributor to install Maven manually instead of using the wrapper
Assuming CI images ship with Maven preinstalled
Interview Questions on This Topic
What does mvn: command not found actually mean?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.
That's CI/CD. Mark it forged?
6 min read · try the examples if you haven't