Stop Shipping Maven: Why the Wrapper Belongs in Your Repository
Maven Wrapper pins the build tool version in your repository so every developer and CI agent runs the exact same Maven — no system install, no version drift, one-line upgrades.
16 Oct 2025, 06:53 UTC

The version drift problem
Every team has seen it: a build passes locally but fails in CI because one developer runs Maven 3.8.6 while the build server ships 3.9.2. Plugin behavior changes, default settings shift, and suddenly mvn clean install produces different artifacts. The usual fix — documenting the required version in a README — relies on humans to install and switch versions correctly. That doesn't scale.
Maven Wrapper (mvnw) solves this by pinning the Maven version in the repository itself. The wrapper is a tiny script (plus a 60 KB JAR) that downloads the declared Maven distribution on first run, caches it under ~/.m2/wrapper/dists, and reuses it for every subsequent build. No system-wide Maven install required. Developers and CI agents only need a JDK.
What you actually commit
Three files go into version control:
mvnw— POSIX shell script for Linux/macOSmvnw.cmd— Windows batch script.mvn/wrapper/maven-wrapper.properties— configuration pointing to a Maven distribution URL and its SHA-256 checksum
The wrapper JAR (.mvn/wrapper/maven-wrapper.jar) is also committed; it handles the download, checksum verification, and delegation to the real Maven. Because the JAR is a binary blob, high-assurance teams should verify its checksum against the official Apache Maven Wrapper release page or build it from source using the maven-wrapper-plugin.
Worked example: adding and upgrading the wrapper
Assume a fresh Git repo with a pom.xml but no wrapper. Run the following from the project root (requires an existing Maven install just this once):
mvn -N io.takari:maven:wrapper -Dmaven=3.9.6This generates the three wrapper files and the JAR, configuring distributionUrl to the Apache mirror for Maven 3.9.6. Commit everything:
git add mvnw mvnw.cmd .mvn/wrapper/maven-wrapper.properties .mvn/wrapper/maven-wrapper.jar
git commit -m "Add Maven Wrapper 3.9.6"Now any contributor (or CI job) can run ./mvnw -v on a machine without Maven in PATH. The wrapper downloads 3.9.6, verifies its checksum, and prints the version. To upgrade later, edit .mvn/wrapper/maven-wrapper.properties:
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jarCommit the change. The next ./mvnw -v run on any machine fetches 3.9.8 automatically. No manual installs, no CI image rebuilds.
Trade-offs and limitations
The wrapper pins Maven, not the JDK. If your build depends on a specific Java version (say, 17 vs 21), pair the wrapper with .tool-versions (asdf), SDKMAN, or pinned CI container images. Otherwise you've only solved half the reproducibility problem.
Network access is required on the first run unless you pre-populate ~/.m2/wrapper/dists or point distributionUrl at an internal mirror (Artifactory, Nexus, or a file:// URL for air-gapped environments). The wrapper honors MAVEN_OPTS=-Dmaven.wrapper.offline=true to skip download attempts entirely.
On Windows, mvnw.cmd can misbehave if Git checks out CRLF line endings. Enforce LF in .gitattributes:
mvnw eol=lf
mvnw.cmd eol=crlfFinally, the wrapper's own JVM (used to launch Maven) inherits the caller's JAVA_HOME and default heap. Custom JVM args passed to ./mvnw are forwarded to the downloaded Maven, but the wrapper launch process itself uses the host JVM settings.
Verify it works, then ship it
- On a clean VM or container with only a JDK installed, run
./mvnw -v. Confirm the output shows the version frommaven-wrapper.properties. - Inspect
~/.m2/wrapper/dists— you should see a folder named after the Maven version and platform. - Run
sha256sum .mvn/wrapper/maven-wrapper.jarand compare the hash to the Apache Maven Wrapper release page for the wrapper version you're using. - Test offline: copy the cached dists to an air-gapped machine, set
MAVEN_OPTS=-Dmaven.wrapper.offline=true, and run./mvnw compile.
Once verified, the wrapper becomes a non-negotiable part of the repo. New contributors clone and build. CI pipelines shrink to ./mvnw verify. Version drift disappears because the truth lives in Git, not in someone's ~/.bashrc.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.