skip to content

Configuration Cache

Caching the configuration phase itself: what gets serialized, which APIs become illegal, how problems are reported, and how incompatible code is migrated. Interviewers ask because this is where Gradle is heading and where most legacy builds break.

on this pageshow

explore

questions

30

How do you turn on Gradle's configuration cache for a build, and what are the two main ways to do it?

level: juniorimportance: must knowfreq 70%

answer

  1. --configuration-cache flag
  2. org.gradle.configuration-cache=true
  3. gradle.properties (project or ~/.gradle)
  4. --no-configuration-cache overrides
  5. caches the task graph, not outputs

basics

~10 s

Pass --configuration-cache on the command line for a single run, or set org.gradle.configuration-cache=true in gradle.properties to enable it persistently for every build.

solid answer

~30 s

There are two ways. For a one-off run, add the `--configuration-cache` flag, e.g. `./gradlew build --configuration-cache`. To enable it permanently for the project, put `org.gradle.configuration-cache=true` in `gradle.properties` (committed for the team, or in `~/.gradle/gradle.properties` per-developer). The command-line flag wins over the property for that invocation, and `--no-configuration-cache` force-disables it even if the property is set. When enabled, Gradle caches the result of the *configuration phase* (the task graph) keyed by inputs like the requested tasks, build scripts, and environment, so subsequent compatible runs skip configuration entirely and go straight to executing tasks.

code

bash · 8 lines
bash
# one-off
./gradlew build --configuration-cache

# persistent: gradle.properties
# org.gradle.configuration-cache=true

# force off for one run even if the property is set
./gradlew build --no-configuration-cache

go deeper

for a junior

Name both mechanisms: the --configuration-cache flag and org.gradle.configuration-cache=true in gradle.properties.

for a middle

Explain precedence (flag beats property, --no- overrides), where to put the property (project vs ~/.gradle), and that it caches the task graph.

for a senior

Tie it to the build phases, explain the cache key (requested tasks + config inputs), and contrast clearly with the build cache.

for a principal

Discuss rollout policy: committing the property for the whole team vs per-developer opt-in, and the interaction with reproducible config inputs across machines.

## What the configuration cache is A Gradle build runs in phases: **initialization** (settle which projects participate), **configuration** (run every `build.gradle(.kts)` script to build the in-memory *task graph*), and **execution** (run the selected tasks). The configuration phase can be expensive on large multi-project builds. The **configuration cache** stores a serialized snapshot of the configured task graph so that, on a later run with the *same inputs*, Gradle skips configuration entirely and goes straight to execution. It is distinct from the **build cache** (which caches *task outputs*) — configuration cache caches the *task graph itself*. ## Two ways to enable it 1. **Per-invocation flag:** `./gradlew assemble --configuration-cache`. Good for trying it out without committing anything. 2. **Project/user property:** add to `gradle.properties`: ``` org.gradle.configuration-cache=true ``` Put it in the project's `gradle.properties` to enable it for everyone, or in `~/.gradle/gradle.properties` to opt in just for yourself. ## Precedence The command line beats the property for that run. `--configuration-cache` forces it on; `--no-configuration-cache` forces it off even when the property says `true`. This lets you disable it ad hoc to debug a flaky build. ## The cache key The cache entry is keyed on the **set of requested task names plus the build configuration inputs** — build script contents, plugin versions, and any environment/system-property/file inputs the configuration phase read. Change any of those and Gradle computes a *miss* and reconfigures, storing a fresh entry. ## What you see On a store, Gradle prints `Calculating task graph as no configuration cache is available for tasks: <tasks>`. On a hit, it prints `Reusing configuration cache.` — the signal that configuration was skipped. ```bash # first run — stores the entry ./gradlew assemble --configuration-cache # > Calculating task graph as no configuration cache is available... # second identical run — reuses it ./gradlew assemble --configuration-cache # > Reusing configuration cache. ```

  • If org.gradle.configuration-cache=true is set but you pass --no-configuration-cache, what happens?
    The command-line flag wins for that invocation, so the configuration cache is disabled for that run only; the property still applies to subsequent runs.
  • How is the configuration cache different from the build cache?
    The build cache caches task *outputs* keyed by task inputs; the configuration cache caches the *task graph* produced by the configuration phase. They are independent and can be used together.

saying these in an interview costs you the question

  • Confusing the configuration cache with the build cache (output cache).
  • Claiming the property goes in settings.gradle — it goes in gradle.properties.
  • Thinking the flag caches task outputs rather than the task graph.

context

open as a page

When you enable the configuration cache and a build reports problems, Gradle prints a link to a configuration-cache-report.html file. What is in that report and how do you use it?

level: juniorimportance: must knowfreq 55%

basics

~20 s

It is an HTML report Gradle generates listing every configuration-cache problem found, grouped by category, with the task or input that caused each one and a stack-trace-like location so you can find and fix the offending code.

open as a page

When the configuration cache is enabled, what does Gradle serialize and store at the end of the configuration phase, and what does that let it skip on the next build?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Gradle serializes the resolved task graph — the tasks to run plus their configured state — to disk. On a cache hit it loads that snapshot and skips the whole configuration phase, going straight to executing tasks.

open as a page

Walk through the difference between configurationCache.requested and configurationCache.active. Give a concrete scenario where requested is true but active is false.

level: middleimportance: must knowfreq 40%

basics

~20 s

requested means the user asked for the configuration cache; active means it is actually operating. They diverge when, for example, the user sets it in gradle.properties but passes --no-configuration-cache on the command line — requested true, active false.

open as a page

What is the BuildFeatures service in Gradle, and how does a plugin obtain it to learn whether the configuration cache is in play?

level: middleimportance: must knowfreq 45%

basics

~10 s

BuildFeatures is a Gradle service exposing whether opt-in features like the configuration cache are requested and active. A plugin gets it via gradle.serviceOf<BuildFeatures>() (or constructor injection) and reads configurationCache.requested / .active.

open as a page

Walk through registering a BuildService with gradle.sharedServices.registerIfAbsent and passing parameters. Why is registerIfAbsent preferred over a plain register, and what does it return?

level: middleimportance: must knowfreq 45%

basics

~10 s

Call gradle.sharedServices.registerIfAbsent("name", MyService::class) { parameters { ... } }. It returns a Provider<MyService>. registerIfAbsent is idempotent, so registering the same name twice (e.g. from two plugins) reuses one instance instead of failing.

open as a page

Why does enabling the configuration cache break code that holds shared mutable state (e.g. a static counter or a shared object referenced by tasks), and how does a BuildService solve it?

level: middleimportance: must knowfreq 55%

basics

~20 s

The configuration cache serializes the task graph and reuses it, so plain shared objects or statics aren't re-created or shared correctly across tasks and parallel workers. A BuildService is the supported holder for shared state that the config cache understands and reuses safely.

open as a page

What does org.gradle.configuration-cache.problems=warn do, and when would you choose it over the default?

level: middleimportance: must knowfreq 50%

basics

~10 s

By default configuration-cache problems fail the build; setting org.gradle.configuration-cache.problems=warn downgrades them to warnings so the build proceeds. It's a transitional setting used while migrating an incompatible build.

open as a page

Why does the configuration cache forbid accessing the Project object at execution time, and what error do you see when a task does it?

level: middleimportance: must knowfreq 70%

basics

~20 s

The configuration cache stores the task graph and reuses it without re-running configuration. Tasks must not touch the live Project at execution time; doing so triggers a configuration-cache problem because Project cannot be serialized into the cached graph.

open as a page

Reading an environment variable with System.getenv() breaks the configuration cache. Why, and what is the correct migration?

level: middleimportance: must knowfreq 65%

basics

~10 s

System.getenv() reads ambient state Gradle can't track, so a cache hit could reuse a stale value. Use providers.environmentVariable("NAME"), which records the variable as a build input and invalidates the cache when it changes.

open as a page

Explain the --configuration-cache-problems=fail|warn flag (and the org.gradle.configuration-cache.problems property). When would you choose warn versus fail?

level: middleimportance: must knowfreq 50%

basics

~20 s

It controls what Gradle does when configuration-cache problems are found. fail (the default) fails the build after reporting them; warn reports them but lets the build continue. Use warn during migration to keep working, fail once you're clean to prevent regressions.

open as a page

What build inputs make up the configuration cache key, and what causes Gradle to discard the entry and recompute the task graph?

level: middleimportance: must knowfreq 55%

basics

~20 s

The key includes the set of requested tasks, plus the values of any system properties, environment variables, Gradle properties, and files read during configuration. Change any of these and the entry is invalidated, so Gradle recomputes the graph.

open as a page

Why would a team enable the configuration cache, and what concrete performance benefit does enabling it deliver?

level: juniorimportance: should knowfreq 45%

basics

~10 s

Enabling it lets Gradle skip the configuration phase on repeat runs by reusing a cached task graph, so builds start executing tasks faster — especially noticeable on large multi-project builds.

open as a page

What does the 'Reusing configuration cache.' message mean, and what message do you expect on the very first run?

level: middleimportance: should knowfreq 55%

basics

~20 s

'Reusing configuration cache.' means Gradle found a valid cached task graph and skipped the configuration phase. On the first run there is no entry, so it instead reports that it is calculating/storing the task graph.

open as a page

A custom task captures `project` in a `doLast { }` closure. Walk through diagnosing and fixing this for the configuration cache.

level: middleimportance: should knowfreq 50%

basics

~20 s

The closure runs at execution time but holds a reference to the non-serializable Project, so the cache reports a problem. Capture the value you need into a local val (or task property) during configuration, then reference that local inside doLast.

open as a page

Walk me through the common categories of problems you see in a configuration-cache report and what each generally points to.

level: middleimportance: should knowfreq 42%

basics

~20 s

Common categories: reading a non-serializable value into cached state, accessing the Project (or task.project) at execution time, reading system properties / env vars / files at configuration time, and using unsupported types. Each category points at a different rule the build logic broke.

open as a page

Why must configuration-time values be captured through Provider/Property and lazy task inputs rather than eagerly, for the task graph to serialize correctly?

level: middleimportance: should knowfreq 45%

basics

~20 s

Because the configured task state must be serializable. Capturing values lazily through Provider/Property and declared task inputs lets Gradle store and restore them. Capturing a Project or other live objects in a closure makes the graph unserializable.

open as a page

How do you write a plugin that uses a configuration-cache-incompatible API only when the cache is not active, degrading gracefully instead of breaking the build?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Query buildFeatures.configurationCache.active. If false, take the legacy/incompatible path; if true, take the cache-safe path. This lets older plugins keep working with the cache on without crashing.

open as a page

What does the STABLE_CONFIGURATION_CACHE feature flag do, and why would you enable it before the configuration cache is even turned on?

level: seniorimportance: should knowfreq 32%

basics

~10 s

STABLE_CONFIGURATION_CACHE is a Gradle feature preview that turns on the stricter configuration-cache checks even without the cache active. Enabling it early surfaces incompatibilities so you can fix them before committing to the cache.

open as a page

A BuildService is shared across tasks running in parallel. What guarantees does Gradle give about its instance and lifecycle, and what concurrency responsibilities remain yours?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Gradle creates exactly one instance per build, lazily, and closes it (if AutoCloseable) when the build ends. It does NOT synchronize your methods, so any mutable internal state must be made thread-safe by you. maxParallelUsages can throttle concurrent users.

open as a page

How do you make a task depend on a BuildService so it's tracked as a dependency, and what does @ServiceReference add over manually wiring usesService?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Expose an abstract Property<MyService> on the task and either set it from the provider plus call usesService(provider), or annotate it with @ServiceReference("name") so Gradle auto-wires and registers the usage. @ServiceReference removes the manual wiring boilerplate.

open as a page

A teammate committed org.gradle.configuration-cache=true, but your local build behaves differently from CI. How do the command-line flag, project property, and user property interact, and how would you control activation per environment?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Command-line flags override gradle.properties for that run. Properties resolve from project gradle.properties, then ~/.gradle/gradle.properties, then -D/-P overrides. Control per-environment by setting the flag in CI scripts and the property (or its override) locally.

open as a page

Which injected services replace common Project APIs for configuration-cache compatibility, and how do you obtain them in a task or plugin?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Inject ProjectLayout (replaces project.file/buildDir), ExecOperations (replaces project.exec), FileSystemOperations (replaces project.copy/delete), ObjectFactory, and ProviderFactory. Obtain them via constructor @Inject (or an abstract @get:Inject getter) — never reference Project.

open as a page

What is a ValueSource and when would you use one to make a build configuration-cache compatible?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A ValueSource wraps a read of external state (like a git command's output) so its result is tracked as a build input. You use it when no built-in provider (environmentVariable, systemProperty, fileContents) covers the source.

open as a page

Your team is rolling out the configuration cache across a large multi-module build. Design a workflow using the problems report and the problems mode to migrate safely and prevent regressions.

level: seniorimportance: should knowfreq 28%

basics

~20 s

Start in warn mode so builds keep working, use the HTML report to triage problems by category, fix or mark tasks incompatible, drive the count down, then flip CI to fail mode to lock in the clean state and catch any new regressions.

open as a page

What does markTaskAsNotCompatibleWithConfigurationCache() (or notCompatibleWithConfigurationCache()) do, and when is it the right tool? What are its downsides?

level: seniorimportance: should knowfreq 35%

basics

~20 s

It marks a specific task as incompatible with the configuration cache, telling Gradle to skip reusing the cached config when that task runs (and not report it as a hard failure). It's a stopgap for tasks you can't fix yet; the downside is you lose cache benefit for any build including that task.

open as a page

A teammate reports the configuration cache never hits — every run prints 'Calculating task graph'. How would you diagnose whether it's a serialization failure or an input-invalidation issue?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Run twice with the identical command. If the store run reports configuration cache problems (e.g. unserializable state), it can't be reused — fix those. If there are no problems but it still misses, a tracked input (a property, env var, or file) is changing between runs.

open as a page

Walk through what happens on a configuration cache 'store' (miss) versus a 'load' (hit) run, and how Gradle decides which one occurs.

level: seniorimportance: should knowfreq 40%

basics

~20 s

On a miss, Gradle runs configuration, serializes the task graph to an entry keyed by the build inputs, then executes. On a hit, it finds an entry whose key matches the current inputs, deserializes the graph, and executes without re-running configuration.

open as a page

You inherit a plugin that uses a static field and a captured Project reference to accumulate build-wide data, and it fails with configuration cache enabled. Outline the migration to a BuildService.

level: principalimportance: should knowfreq 28%

basics

~20 s

Move the static state into an abstract BuildService<Params>, pass any needed config (paths, flags) via Params instead of capturing Project, register it with registerIfAbsent, and have each task reference it via @ServiceReference. Remove statics and execution-time Project access.

open as a page

Besides the configuration cache, what other build feature does BuildFeatures expose, and how would you introspect it for diagnostics?

level: middleimportance: nice to knowfreq 18%

basics

~10 s

BuildFeatures also exposes isolatedProjects, with the same requested/active Provider<Boolean> pair. You introspect it the same way: gradle.serviceOf<BuildFeatures>().isolatedProjects.active.getOrElse(false).

open as a page