skip to content

Auto-Provisioning & Foojay Resolver

Auto-downloading a missing JDK through the foojay resolver plugin declared in settings, and disabling it where downloads are not allowed. Asked because provisioning does nothing until that plugin is added, a very common first stumble.

on this pageshow

questions

5

What is JDK auto-provisioning in Gradle, and what makes it work?

level: juniorimportance: must knowfreq 55%

answer

  1. Gradle ships no JDK URLs
  2. resolver plugin in settings
  3. foojay-resolver-convention
  4. Disco API catalog
  5. cached in ~/.gradle/jdks

basics

~10 s

If a build needs a JDK that isn't installed locally, Gradle can automatically download it. By default Gradle needs a toolchain resolver plugin (foojay-resolver) registered in settings to know where to fetch JDKs from.

solid answer

~30 s

Auto-provisioning lets Gradle download a matching JDK when the toolchain you declared (e.g. `languageVersion = JavaLanguageVersion.of(21)`) isn't found among locally installed JDKs. Gradle itself ships no download URLs; it delegates to **toolchain resolver plugins**. The standard one is `org.gradle.toolchains.foojay-resolver-convention`, applied in `settings.gradle.kts`. It queries the foojay Disco API to find a JDK matching your language version, vendor, and implementation, downloads it, and caches it under the Gradle user home (`~/.gradle/jdks`). Once downloaded it's reused across builds. Auto-provisioning only triggers when no suitable local JDK exists, so adding the plugin is safe even on machines that already have the right JDK.

code

kotlin · 11 lines
kotlin
// settings.gradle.kts
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
}

// build.gradle.kts
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

go deeper

for a junior

Know that a missing JDK can be auto-downloaded and that the foojay-resolver plugin enables it in settings.

for a middle

Explain the resolver SPI, that Gradle ships no URLs, and that foojay queries the Disco API and caches under ~/.gradle/jdks.

for a senior

Discuss the convention plugin wiring toolchainManagement automatically, trigger conditions, and reuse/caching semantics.

for a principal

Frame provisioning as a vendor-neutral SPI choice; weigh build-time downloads vs. pre-provisioned images for org-wide reproducibility.

## The problem auto-provisioning solves A Gradle build can declare which JDK it wants to compile and run with via a **toolchain**: ```kotlin java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } ``` Gradle first scans for **locally installed JDKs** (from well-known locations, `JAVA_HOME`, SDKMAN, asdf, etc.). If it finds a JDK 21, it uses it. If it doesn't, rather than failing the build, Gradle can **auto-provision** — download a matching JDK on the fly. ## Why Gradle needs a resolver plugin Gradle core deliberately does **not** know where to download JDKs from — it ships no vendor URLs. Instead it exposes a **toolchain provisioning SPI** and delegates to **toolchain resolver plugins** registered in `settings.gradle.kts` under `toolchainManagement`. The conventional resolver is the **Foojay Disco resolver**: ```kotlin // settings.gradle.kts plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" } ``` The `-convention` variant is convenient: it both registers the resolver **and** wires it into `toolchainManagement.jvm.javaRepositories` for you with sensible defaults, so you don't have to write the repository block by hand. ## What foojay does [foojay.io](https://foojay.io) hosts the **Disco API**, a vendor-neutral catalog of JDK builds (Temurin, Zulu, Liberica, Oracle, etc.). When provisioning is needed, the resolver asks the Disco API for a build matching the requested **language version**, **vendor**, and **implementation** (e.g. J9 vs HotSpot), gets back a download URL, and Gradle fetches and unpacks it. ## Caching and reuse Provisioned JDKs land in `~/.gradle/jdks/` (the Gradle user home). They are reused across all builds on that machine — provisioning happens once per (version, vendor) combination, not per build. Gradle verifies the download's integrity. ## When it triggers Auto-provisioning fires **only** when: 1. A toolchain is requested, and 2. No matching JDK is found locally, and 3. `org.gradle.java.installations.auto-download` is not `false`. So applying the plugin is harmless on machines that already have the JDK — it simply never downloads.

  • Why doesn't Gradle download JDKs out of the box without any plugin?
    Gradle core stays vendor-neutral and ships no download URLs; it delegates to pluggable toolchain resolvers so the choice of JDK source isn't baked into Gradle.
  • Where are auto-provisioned JDKs stored?
    Under the Gradle user home, in `~/.gradle/jdks/`, and reused across builds on that machine.

Gradle is like a build coordinator who knows it needs a 'JDK 21 specialist' but has no phone book; the foojay resolver is the phone book it calls to find and hire one.

saying these in an interview costs you the question

  • Claiming Gradle downloads JDKs natively with no plugin needed (it needs a toolchain resolver).
  • Saying provisioning happens on every build (it's cached and reused).

context

open as a page

How do you disable JDK auto-download, and why might a team do that in CI?

level: middleimportance: must knowfreq 48%

basics

~10 s

Set org.gradle.java.installations.auto-download=false in gradle.properties (or pass it with -P/-D). Gradle then refuses to download missing JDKs and fails if no matching local JDK exists. Teams do this so CI uses only pre-installed, controlled JDKs.

open as a page

What's the difference between the foojay-resolver and foojay-resolver-convention plugin IDs, and which do you apply where?

level: middleimportance: should knowfreq 40%

basics

~10 s

Both come from the same project. foojay-resolver only registers the resolver, so you must wire toolchainManagement yourself. foojay-resolver-convention registers the resolver AND wires up the default javaRepositories for you. You apply either in settings.gradle.kts.

open as a page

When a toolchain is requested, how does Gradle decide between a local JDK and provisioning a new one?

level: seniorimportance: should knowfreq 32%

basics

~10 s

Gradle always prefers a matching locally installed JDK. It scans known locations first; only if nothing matches the requested version/vendor/implementation does it provision via a resolver — and only if auto-download isn't disabled.

open as a page

How would you govern toolchain auto-provisioning across an organization for security and reproducibility?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Pre-provision JDKs in build images so foojay isn't hit at build time, disable auto-download in CI, optionally point a resolver at an internal mirror instead of foojay, and pin/verify the JDKs so builds stay reproducible and auditable.

open as a page