skip to content

What is the Daemon JVM Criteria (gradle/gradle-daemon-jvm.properties), how do you generate it, and what problem does it solve?

level: middleimportance: must knowfreq 40%

answer

  1. gradle/gradle-daemon-jvm.properties
  2. toolchainVersion / toolchainVendor
  3. ./gradlew updateDaemonJvm
  4. discovery then provisioning
  5. committed = reproducible, replaces JAVA_HOME

basics

~20 s

It's a committed file declaring the version/vendor of the JDK that should run the Gradle daemon. You generate it with ./gradlew updateDaemonJvm. It makes the daemon JVM reproducible across machines instead of relying on a path or JAVA_HOME.

solid answer

~40 s

**Daemon JVM Criteria** is Gradle's portable way to pin the JVM that runs the daemon. Instead of a machine-specific path (`org.gradle.java.home`) or implicit `JAVA_HOME`, you commit `gradle/gradle-daemon-jvm.properties` declaring a **version** (and optionally **vendor**/implementation): e.g. `toolchainVersion=17`, `toolchainVendor=ADOPTIUM`. You generate/update it with `./gradlew updateDaemonJvm --jvm-version=17 --jvm-vendor=adoptium`. On any machine, Gradle then *discovers* an installed JDK matching the criteria — and in newer Gradle versions can **auto-provision** one if none is found. Because the file is version-controlled, every developer and CI agent runs the daemon on the same JVM, removing 'works on my machine' drift. It sits alongside the wrapper as the second half of a fully reproducible bootstrap: the wrapper pins the Gradle version, the criteria pins the JVM Gradle runs on.

code

bash · 6 lines
bash
# Generate / update the criteria file (commit the result)
./gradlew updateDaemonJvm --jvm-version=17 --jvm-vendor=adoptium

# Resulting gradle/gradle-daemon-jvm.properties:
#   toolchainVersion=17
#   toolchainVendor=ADOPTIUM

go deeper

for a junior

Know the file name and that it pins the daemon JVM version reproducibly.

for a middle

Explain updateDaemonJvm, the version/vendor keys, and discovery-vs-provisioning resolution.

for a senior

Pair it with the wrapper as a full reproducible bootstrap; contrast with org.gradle.java.home and JAVA_HOME.

for a principal

Define a fleet-wide standard daemon JVM, govern vendor/version, and ensure provisioning repositories are configured for CI.

## Daemon JVM Criteria ### The problem it solves Before this feature, the JVM running the daemon came from either the ambient `JAVA_HOME` (implicit, per-machine) or `org.gradle.java.home` (an absolute path, non-portable). Neither is reproducible: two developers could be silently running Gradle on different JDKs, and CI could differ from both. The **Daemon JVM Criteria** fixes this by letting you declare the daemon JVM *by specification* and **commit that to the repo**. ### The file Gradle reads `gradle/gradle-daemon-jvm.properties`. Typical contents: ```properties toolchainVersion=17 toolchainVendor=ADOPTIUM ``` - `toolchainVersion` — the required Java major version. - `toolchainVendor` — optional vendor constraint (e.g. ADOPTIUM, AZUL, BELLSOFT). - (Newer Gradle also supports a native-image/implementation hint.) ### Generating it You don't hand-write it; you run the built-in task: ```bash ./gradlew updateDaemonJvm --jvm-version=17 --jvm-vendor=adoptium ``` This writes/updates the properties file. Commit it like you commit `gradle/wrapper/gradle-wrapper.properties`. ### How Gradle resolves the criteria at runtime 1. **Discovery** — Gradle searches known JDK installations (auto-detected via OS locations, environment variables like `JDK<n>`, asdf/SDKMAN!/jabba candidates, etc.) for one matching the version+vendor. 2. **Provisioning** — in Gradle versions that support it, if no installed JDK matches, Gradle can download a matching JDK from a configured toolchain repository. 3. **Launch** — the daemon starts on the resolved JVM. ### Why it matters architecturally - **Reproducibility.** Pairing the wrapper (pins Gradle version) with criteria (pins the daemon JVM) means a fresh clone bootstraps identically everywhere — a real CI-strategy win. - **Removes JAVA_HOME coupling.** Developers no longer need to manually switch `JAVA_HOME` before building a project that requires a specific JDK. - **Separate from build toolchains.** It only governs the JVM that *runs Gradle*; your code's compile/test toolchain is still configured in `build.gradle(.kts)`. ### Caveats - It targets the Gradle/daemon JDK, which must satisfy Gradle's own supported-JVM range. - Provisioning support and the exact property keys depend on the Gradle version; confirm against the version your wrapper pins.

  • How is the Daemon JVM Criteria different from a build java { toolchain { ... } } block?
    Criteria pins the JVM that runs Gradle itself (the daemon); the toolchain block pins the JDK that compiles and tests your project's code. Different JVMs, different config files.
  • What is the advantage of the criteria file over org.gradle.java.home?
    It declares a version/vendor rather than an absolute path, so it's portable and version-controllable, and Gradle can discover or provision a matching JDK on any machine.
  • Which task generates the file, and where does it live?
    ./gradlew updateDaemonJvm generates gradle/gradle-daemon-jvm.properties, committed alongside the wrapper config.

The wrapper is a recipe that pins the brand of flour (Gradle version); the daemon JVM criteria pins the brand of oven (the JDK Gradle runs on) — so anyone who clones the repo bakes in identical conditions.

saying these in an interview costs you the question

  • Confusing it with the build java { toolchain } block (that's for compiling your code).
  • Hand-editing it instead of using updateDaemonJvm (acceptable but the task is canonical).
  • Assuming every Gradle 8.x version provisions a missing daemon JDK — support varies by version.

context