From Gradle 9.0.0, yes. Archive tasks such as Jar and Zip produce reproducible output by default, with deterministic file order, fixed timestamps and fixed permissions. On older Gradle versions you can get the same effect by setting file timestamp, file order and permission options on each archive task.
What the Gradle docs say it now does
The Gradle user guide explains why this matters. The order of files inside a ZIP, TAR, JAR, WAR or EAR can change each time it is built, and files on disk can carry different timestamps or permissions depending on the environment.
Starting with Gradle 9.0.0, tasks that extend AbstractArchiveTask produce archives where:
- file order is deterministic,
- files have fixed timestamps, with exact values depending on the archive type,
- directories have fixed permissions of 0755,
- files have fixed permissions of 0644.
You can still customise permissions or timestamps while keeping the archive reproducible. If a script needs the executable bit, set that explicitly instead of reverting to filesystem permissions.
What to do on an older Gradle
The Reproducible Builds JVM page documents the pre Gradle 9 approach. Gradle supported reproducible archives since version 3.4, and you opt in per task:
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
That page also suggests setting directory and file permissions to ignore the system umask, and keeping the executable bit by checking the owner execute permission. Treat that as a pattern to adapt, then test it.
Reverting by accident
Gradle 9 lets you revert each aspect. Setting the preserve timestamps option to true, turning reproducible file order off, or calling the filesystem permissions method each brings back an environment dependent behaviour. There is also a project property to use filesystem permissions across all archive tasks.
Search your build scripts for those settings. If one exists for a good reason, such as permissions managed by version control, record it in a comment so a reviewer knows it was deliberate.
Other Gradle traps
Reproducible archives fix packaging. They do not fix everything.
| Source of variation | What to do |
|---|---|
| Properties files written with java.util.Properties.store | Set the java.properties.date system property to a fixed value |
| Default charset on Java 17 and older | Set file.encoding to UTF-8 |
| Locale dependent output | Set user.language and user.country consistently |
| Generated pom.xml edited with withXml | Configure it on the publication, not on the generate task, so element order is stable |
| Dynamic or changing dependency versions | Lock them so each build resolves the same artifacts |
The JVM page lists the first four and gives gradle.properties examples for the charset and locale settings.
A quick test
Build twice from clean checkouts, ideally on different machines, and compare checksums of the jar. If they differ, run diffoscope on the two jars and read the first difference. Do not assume the Gradle default covers a custom task that writes its own files.
Quick check
Do I need to change anything if I use Gradle 9?
Not for basic archive reproducibility, but check for settings that revert it, and handle properties files, locale and changing dependencies yourself.
Does a reproducible jar mean my dependencies are pinned?
No. Dependency versions are a separate input. Pin or lock them so each build uses the same artifacts.
Safeguard's SBOM and dependency scanning, generally available, lists the exact components your artifacts contain. The free scan at gold.safeguard.sh/scan is a quick way to see your dependency list.
Sources
Last reviewed: 19 October 2026.