Set the project.build.outputTimestamp property in your pom.xml, upgrade plugins to reproducible versions, and test with the compare goal of the maven-artifact-plugin. Starting with Maven 3.10.0 and 4.0.0-beta-5, reproducible mode is active by default, though setting your own value remains useful for overriding the inherited one.
The three setup steps
Maven's own guide says there is no Maven version prerequisite, because everything happens at plugin level.
- Upgrade your plugins to reproducible versions. Run mvn artifact:check-buildplan to see which plugins need upgrading.
- Add the output timestamp property:
<properties>
<project.build.outputTimestamp>2023-01-01T00:00:00Z</project.build.outputTimestamp>
</properties>
- Rebuild and compare, as described below.
The guide says this gets the output mostly reproducible. Keep the value in the pom or parent pom so every module uses the same one, and update it when you cut a release if you want the artifact date to reflect that release.
Test it locally
The guide gives a simple two build test:
mvn clean install
mvn clean verify artifact:compare
The first command builds and installs. The second rebuilds without installing and checks against the previous install. Maven is explicit that this local test does not prove a third party can reproduce your build, since environment leaks such as username or current directory may remain. It does catch the basic problems, such as timestamps.
For a real check, rebuild from a different setup. Containers can make that easier. For releases staged in a remote repository, someone else can run verify with artifact:compare against the staging location.
If something still differs
The guide recommends this loop:
- Use diffoscope on the two outputs. The artifact:buildinfo goal prints a command with the file paths.
- Work out which plugin produced the unstable output.
- Check whether a reproducible version of that plugin exists. If not, open an issue with the plugin maintainers.
Other things the guide warns about
Several habits make Maven builds unstable.
| Habit | Why it matters |
|---|---|
| Version ranges in dependencies | The same build can resolve different artifacts on different days |
| Building on Windows and Unix | Different newlines can change outputs |
| Using only the major version of the JDK | Different minor builds can produce different results |
The guide also points to Reproducible Central, a list of project releases that were checked by rebuilding independently from the reference build published in Maven Central. If you publish a library, being on that list is a visible signal that others can verify your releases.
Pin what the build pulls in
Version ranges are the most common reason a Maven build changes without a code change. Replace ranges with exact versions, and review any snapshot dependency, because a snapshot can resolve to a different artifact tomorrow. Keep the JDK identical too, including the minor version, by using the same container image or toolchain on every build machine.
When you change a version on purpose, make that its own commit, so the diff between two release artifacts maps to a change you can name.
A reproducible jar is a statement about how the jar was built. It does not say the code or the dependencies are free of vulnerabilities. Keep scanning dependencies as you would anyway.
Safeguard's dependency scanning and SBOM generation are generally available, and the free scan at gold.safeguard.sh/scan covers repository dependencies.
Try it yourself
Click through the demo and look at how each dependency is listed with its version, which is the list you pin.
Click through it at your own pace, or open it full screen.
Quick check
Does artifact:compare prove my build is reproducible?
No. It shows two builds on your setup match. Environment leaks can still differ on another machine.
Do I still need the timestamp property on new Maven versions?
It is useful if you want to override the inherited default value.
Sources
Last reviewed: 19 October 2026.