Dynamic versions such as 1.+ or a range let Gradle pick a different version over time, which can break builds with no code change. Dependency locking records the resolved versions in a lock file, so builds fail instead of drifting. You then refresh the lock on purpose.
What the Gradle documentation says
Gradle's dependency locking page warns that dynamic versions can cause builds to break unexpectedly, because the exact version that gets resolved can change over time. Locking is enabled per dependency configuration. Once it is on, you create an initial lock state, and Gradle then verifies that resolution results do not change. If the selected dependencies differ from the locked ones because newer versions exist, the build fails.
The documentation also points to a balance: teams that want the latest versions can use dynamic versions during development and testing and lock them for releases.
Turning it on
You can lock one configuration:
configurations {
compileClasspath {
resolutionStrategy.activateDependencyLocking()
}
}
Or all of them at once, which the documentation notes does not include the buildscript configurations:
dependencyLocking {
lockAllConfigurations()
}
Then write the lock state with the write-locks argument:
./gradlew dependencies --write-locks
That creates or updates the lock state for each resolved configuration. If a lock state already exists, it is overwritten. The result is a gradle.lockfile that the documentation says is generated and should not be edited by hand.
Limits you should know
Locking works with dynamic versions, but the documentation says it should not be used with changing versions such as SNAPSHOT, where the coordinates stay the same while the content may change. A lock cannot tell you the content moved.
Also, locking only applies to configurations that can be resolved. On a non-resolvable configuration it does nothing.
A workable policy
- Commit gradle.lockfile.
- Keep direct dependencies as exact versions where you can, and use dynamic versions only where you want a deliberately moving target.
- Use lock-aware CI so a changed resolution fails the build.
- Update locks in a separate pull request, with tests, and review the diff.
- Do not lock SNAPSHOT dependencies and expect stability. Avoid them in release builds.
| Approach | Repeatable | Gets fixes automatically |
|---|---|---|
| Exact versions | Yes | No, you bump them |
| Dynamic versions, no lock | No | Yes, unreviewed |
| Dynamic versions with lock | Yes | When you refresh the lock |
The third row is the useful middle: you keep a flexible declaration, but each new version arrives through a diff.
Locking is not verification
Locking says "these are the versions". It does not say the files are authentic. Gradle has a separate dependency verification feature for checksums and signatures. Use both if your risk calls for it.
Where Safeguard fits
Safeguard's dependency scanning and SBOM generation can check the versions your locked Gradle build uses against known vulnerabilities, and a free scan is available at gold.safeguard.sh/scan.
Try it yourself
Open the demo and notice how monitoring catches a newly disclosed issue in a version that was locked and unchanged.
Click through it at your own pace, or open it full screen.
Quick check
Does Gradle locking stop all updates?
No. It makes resolution changes fail the build until you refresh the lock on purpose with the write-locks argument.
Can I lock SNAPSHOT dependencies?
Gradle advises against it. The coordinates stay the same while the content may change, so a lock cannot keep it stable.
Sources
Last reviewed: 19 October 2026.