// Copyright (C) 2026 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

:gradle-issues: https://github.com/gradle/gradle/issues/

[[config_cache:adoption_guide]]
= Adopting the Configuration Cache

This guide is a linear, step-by-step path for adopting the Configuration Cache (CC) in an existing project or plugin.

[NOTE]
====
Since Gradle 9.0.0, the Configuration Cache is the link:https://blog.gradle.org/road-to-configuration-cache#preferred-mode-of-execution[preferred mode of execution].
The work to make it the default in Gradle 10.0.0 is in progress.
Adopting CC now puts your build ahead of that change.
====

[[config_cache:adoption_guide:overview]]
== Adoption Goals

You have successfully adopted the Configuration Cache when:

. `org.gradle.configuration-cache=true` is set in your `gradle.properties` (committed, not local-only).
. Your everyday development tasks (the ones developers run dozens of times a day) hit the cache.
. CI does not use warning mode, so any change that breaks CC on a developer machine also fails the CI build. Ephemeral CI runners don't benefit from cache reuse across builds — the goal here is regression prevention, not runtime speedup. Individual jobs that legitimately cannot use CC (typically a specific publishing or release step in a plugin without CC support yet) opt out with `--no-configuration-cache`.
. Any task that genuinely cannot be CC-compatible is explicitly marked using <<configuration_cache_debugging.adoc#config_cache:task_opt_out, `notCompatibleWithConfigurationCache("reason")`>>.

[[config_cache:adoption_guide:before_you_start]]
== Before You Start

Run through this checklist before you flip the switch.

[cols="1,3,4"]
|===
| # | Prerequisite | Why it matters

| 1
| Start from a green build
| Make sure your build is passing without CC first. Mixing pre-existing failures with CC-specific failures significantly complicates debugging.

| 2
| Identify your dev loop
| Pick the 2 or 3 tasks your team actually runs most: `test`, `assemble`, `:app:installDebug`, or a custom code-gen task. Frequently run tasks are where CC hits will pay off, so these should be the tasks you iterate against.
|===

[TIP]
====
If you are migrating a large multi-module monorepo, do not try to enable CC across the whole tree on day one.
Pick one well-isolated subproject, get it to a CC hit, then expand.
====

[[config_cache:adoption_guide:step1]]
== Step 1: Update Gradle

CC quality improves substantially with each minor Gradle release, and the major plugin ecosystems have done most of their CC work in their latest versions.
Before enabling CC, update the build to a recent Gradle version with current plugins.

=== Update the Gradle Wrapper

Gradle 9.0.0 or later is strongly recommended.
If you are not on Gradle 9.0.0 yet, update first using the <<upgrading_major_version_9.adoc#upgrading_major_version_9, upgrade guide>>, typically by bumping `gradle/wrapper/gradle-wrapper.properties` to a 9.x.y release.

Update in small increments.
Going from 9.2.0 to 9.3.0 is straightforward; jumping from 8.7 to 9.3.0 in a single step is not.
You will hit deprecations and incompatibilities all at once and lose the ability to attribute each failure to a specific bump.

=== Plan for Cascading Bumps

A Gradle wrapper bump can force matching bumps in adjacent build tooling.
None of the items below are blockers, but they tend to surface in sequence — each as a separate build failure — so budget for them as part of this step rather than as part of CC adoption itself.
This list covers the most common cascading bumps and is not exhaustive; other plugins in your build (code generators, framework plugins, IDE tooling) can pull in their own version couplings that you will need to reconcile the same way:

* **Kotlin tooling** — a Gradle bump can cascade into three places:
** Build logic: Gradle compiles `.gradle.kts` files at a specific Kotlin _language version_ (currently `2.2` for Gradle 9.7.0). That pin advances across Gradle releases, and when it does, deprecated syntax in your build scripts may need touch-ups.
** link:https://kotlinlang.org/docs/gradle-configure-project.html[Kotlin Gradle Plugin] (KGP): KGP is coupled to specific Gradle versions, so a Gradle bump often requires bumping KGP to stay compatible. See the <<compatibility.adoc#kotlin, Kotlin compatibility matrix>>.
** Kotlin source: bumping KGP brings a newer Kotlin compiler, which can surface deprecations or compilation errors in your application code.
* **link:https://developer.android.com/build/releases/gradle-plugin#updating-gradle[Android Gradle Plugin (AGP)]** — Android builds have their own multi-axis cascade:
** AGP-Gradle coupling: each AGP major declares a minimum Gradle version, so a Gradle bump may require an AGP bump.
** AGP-KGP coupling: an AGP bump typically expects a newer KGP version for Kotlin modules, which then cascades into the Kotlin chain above.
** AGP DSL changes: major AGP releases deprecate or remove DSL APIs that your Android `build.gradle(.kts)` files may rely on.
** Minimum JDK: AGP majors also raise the JDK floor required to run the build.
* **Groovy in `buildSrc` and convention plugins** — Gradle's runtime Groovy version applies to Groovy DSL scripts and Groovy code in `buildSrc` or included builds. If those use Spock to test convention plugins, a Gradle major bump can require a matching Spock variant (e.g., Gradle 9 ships Groovy 4, so `spock-core:*-groovy-3.0` will not load and fails with `IncompatibleGroovyVersionException`). Your project's own tests are unaffected — they use the Groovy version declared in the project's own dependencies.
* **JUnit Platform Launcher** — on Gradle 9+, the launcher must be declared explicitly as a `testRuntimeOnly` dependency. Older Gradle versions auto-added it; the new behavior surfaces as a cryptic `Failed to load JUnit Platform` error on the first run of the test suite.

=== Update your Plugins

Spring Boot, Spotless, Kotlin Multiplatform (KMP), and most major plugins now support CC.
Bring them to current versions before enabling CC, and check the <<configuration_cache_status.adoc#config_cache:plugins, plugin status page>> for known limitations on any plugin you depend on.

[[config_cache:adoption_guide:step2]]
== Step 2: Enable the Configuration Cache and Run `help`

Enable CC on the command line using the `--configuration-cache` flag and run `help` as the first test:

[source,bash]
----
$ ./gradlew help --configuration-cache
----

The flag form lets you toggle CC on and off per invocation without changing files.

`help` is a good first task because it configures the build but executes essentially nothing, giving you the cheapest, fastest signal that your **configuration phase** is CC-compatible.

What you should see on a fresh run:

[source,text]
----
Calculating task graph as no cached configuration is available for tasks: help
...
Configuration cache entry stored.
----

On the second run, you should see:

[source,text]
----
Reusing configuration cache.
...
BUILD SUCCESSFUL
----

If `help` itself fails with CC problems, the problems are in your **build-script logic** (your top-level `settings.gradle(.kts)`, your `build.gradle(.kts)` files, your plugins applied in apply-plugin form, your init scripts).
These have to be fixed before anything else makes sense.

If `help` works, the configuration phase is broadly OK.
The remaining problems are mostly in task implementations and only surface when the tasks themselves are configured — skip ahead to <<config_cache:adoption_guide:step5, Step 5: Dry-Run Your Real Dev Loop>>.

[[config_cache:adoption_guide:step3]]
== Step 3: When You Hit a Miss or Failure, Read the Report

[NOTE]
====
If Step 2 succeeded cleanly with `Configuration cache entry stored.`, there is no report to read yet.
Skip ahead to <<config_cache:adoption_guide:step5,Step 5>>.
====

When Gradle detects Configuration Cache problems on a miss or failure, it writes an HTML report at `build/reports/configuration-cache/<hash>/<hash>/configuration-cache-report.html`.
Open it in your browser; this report is the primary debugging tool for CC adoption.
For the full debugging workflow, see <<configuration_cache_debugging.adoc#config_cache:troubleshooting, Use the Configuration Report>> on the Debugging page.

[source,text]
----
$ ./gradlew help --configuration-cache

...

See the complete report at file://Documents/Repositories/my-plugin/build/reports/configuration-cache/<hash>/configuration-cache-report.html

...

BUILD SUCCESSFUL in 9s
2 actionable tasks: 2 executed
Configuration cache entry discarded with 14 problems.
----

[IMPORTANT]
====
Do not confuse the Configuration Cache report with the general Gradle <<reporting_problems.adoc#sec:generated_html_report, Problems report>> at `build/reports/problems/problems-report.html`.
====

[[config_cache:adoption_guide:step4]]
== Step 4: Fix your Build Logic

If `help` failed under CC, the violations are in your **build-script logic**.
Specifically the code that runs during the configuration phase, before any task executes.
That is everything in `settings.gradle(.kts)`, in every `build.gradle(.kts)`, in init scripts, and in any plugin code that runs at apply-plugin time.
These have to be fixed first; everything downstream assumes the configuration phase is clean.

The canonical fix catalog is <<configuration_cache_requirements.adoc#config_cache:requirements, Configuration Cache Requirements for your Build Logic>>.
Every recognized violation pattern is documented there end-to-end, with before-and-after code samples for both Groovy and Kotlin DSLs.
Each entry in the CC report links to the matching section of the requirements doc.

The workflow for each violation:

. Open the linked section of the requirements doc from the CC report entry.
. Apply the canonical fix in your build script, settings script, init script, or plugin source.
. Re-run `./gradlew help --configuration-cache` and check whether the entry is now stored cleanly.

If you only have an error fragment to start from rather than a full report entry, use <<configuration_cache_debugging.adoc#config_cache:debugging:common_errors, Common Errors and Where to Look>> on the Debugging page to translate it to the underlying pattern, then apply the workflow above.

The configuration-time patterns most often surfaced at this stage fall into two categories:

* **Hard failures that prevent storing a cache entry** — registering build listeners such as `Gradle.buildFinished`, `Gradle.projectsEvaluated`, or `TaskExecutionListener`. Replace with a `BuildService` implementing `OperationCompletionListener`.
* **Optional cleanups that improve cache hit rate** — reading the environment (`System.getenv` / `System.getProperty`) or reading files (`File.readText` / `file(...).exists()`) at configuration time. These do not fail the build; Gradle records them as build configuration inputs and the cache invalidates correctly when the value changes. Switching to `providers.environmentVariable` / `providers.systemProperty` / `providers.fileContents` defers the read to execution time, so the same value change re-runs only the affected task instead of the whole configuration phase. Worth doing when a specific input rotates often (e.g., `BUILD_NUMBER` on CI); otherwise optional.
The full pattern catalog is in the <<configuration_cache_requirements.adoc#config_cache:requirements, Configuration Cache Requirements for your Build Logic>>.

Iterate until `help` produces `Configuration cache entry stored.` with no problems reported.
That signals the configuration phase is clean, and you can move on to running your real dev loop tasks.

[TIP]
====
Fix build-logic violations *before* task-execution violations.
A plugin's `apply()` method that registers a listener through `Gradle.buildFinished` will fail every build under CC, regardless of which task is requested; a task implementation problem only fails when that specific task runs.
Fixing configuration-phase issues first unblocks everything downstream.
====

[[config_cache:adoption_guide:step5]]
== Step 5: Dry-Run Your Real Dev Loop

Once `help` succeeds, expand to the tasks your team actually runs (the ones you identified in <<config_cache:adoption_guide:before_you_start, Before You Start>>).
Each task has its own configure-time logic that `help` does not exercise, so each one may surface configuration-phase violations that did not show up at the `help` stage.

In the examples below, replace `<your-task>` with one of those task names: `test`, `assemble`, `:app:installDebug`, `dependencyUpdates`, or whatever your dev loop actually uses.

Surface configuration-time problems for each task without paying for execution by running with `--dry-run`:

[source,bash]
----
$ ./gradlew <your-task> --configuration-cache --dry-run
----

`--dry-run` configures the build and prints the task graph but does not execute tasks.
Any CC problem found at this stage is a **configuration-time** issue in the task's plugin or in the build script that registers it.
Fix these the same way you fixed the `help` problems in <<config_cache:adoption_guide:step4,Step 4>>: open the CC report, click through to the matching section of the requirements doc, apply the canonical fix, re-run.

Iterate the `--dry-run` invocation across each of your dev-loop tasks until each one stores its CC entry cleanly.
Then proceed to running the tasks normally.

[[config_cache:adoption_guide:step6]]
== Step 6: Run Your Real Dev Loop

With the configuration phase clean for each task, drop `--dry-run` and run each task once with `--rerun-tasks` so every task action actually executes:

[source,bash]
----
$ ./gradlew <your-task> --configuration-cache --rerun-tasks
----

`--rerun-tasks` bypasses up-to-date checks and the build cache, forcing every task action to run.
Without it, a task that is UP-TO-DATE or FROM-CACHE has its action skipped, so any execution-time CC violation inside that action stays hidden until inputs change.
Once each task in your dev loop has executed cleanly at least once under CC, you can drop `--rerun-tasks` for subsequent invocations.

Problems found at this stage that did not surface with `--dry-run` are **execution-time** issues, typically tasks reading `project`, using `System.getenv()`, or holding non-serializable state in a field.
Fix these iteratively (see <<config_cache:adoption_guide:step7, Step 7: Fix Problems Iteratively>>).

After that shakedown invocation, run the task once more without `--rerun-tasks` to validate the cache-hit path:

* **First invocation** (`--rerun-tasks`) — Gradle stores the CC entry, immediately loads it back to execute the task graph, and forces every task action to run. This verifies store, deserialization, task execution, and surfaces any execution-time CC violation that a cached task action would otherwise hide.
* **Second invocation** (no `--rerun-tasks`) — Gradle checks the fingerprint against the current build state; if nothing input-relevant has changed, the cache is reused and the output reads `Reusing configuration cache.` This is the first CC hit.

[NOTE]
====
The `--configuration-cache` flag is repeated in every example so each invocation is self-contained.
Once you are confident enough to flip the switch persistently, you can set `org.gradle.configuration-cache=true` in `gradle.properties` and drop the flag from the command line.
That comes later in <<config_cache:adoption_guide:step9, Step 9: Roll Out to Your Team>>.
====

[[config_cache:adoption_guide:step7]]
== Step 7: Fix Problems Iteratively

[TIP]
====
Configuration Cache problems do not blend into the general warning stream — Gradle reports them together at the end of the build as a distinct "Configuration cache entry discarded with N problems" section (or a clean "Configuration cache entry stored." on success) and writes the CC report to `build/reports/configuration-cache/<hash>/<hash>/configuration-cache-report.html`.
So during CC adoption, if you see unrelated deprecation warnings from KGP, AGP, Dokka, or other plugins scrolling by during the build, treat them as *independent* migrations.
They are not CC problems just because they surface at the same time; defer them until your CC story is sorted unless they actively block the build.
====

**Violations fall into two categories.**
Most CC report entries are *mechanical*: they map to a documented pattern, the fix is a small targeted refactor, and the canonical before/after is on the requirements page.
Some entries, especially in plugins built around execution-time `Configuration` mutation or other older Gradle APIs, are *architectural*: the fix is not a refactor but a redesign of how the task does its work.
Both categories are common.
The mechanical category is the focus of this section; the architectural category is covered in <<config_cache:adoption_guide:when_small_fix_isnt_enough, When the Small-Fix Path Isn't Enough>>.

When a task fails under CC, identify the violation pattern from the report and apply the canonical fix from the requirements page.
The patterns are documented end-to-end (with before-and-after code samples for each) in <<configuration_cache_requirements.adoc#config_cache:requirements, Configuration Cache Requirements for your Build Logic>>.

The patterns fall into two groups.

**Hard failures — these prevent CC from storing an entry, so fix them first:**

* <<configuration_cache_requirements.adoc#config_cache:requirements:use_project_during_execution, Using the `Project` Object at Execution Time>>: `project.copy {}`, `project.version`, `project.rootDir`, or anything else reached through `project` from a `@TaskAction` or `doLast {}`.
* <<configuration_cache_requirements.adoc#config_cache:requirements:disallowed_types, Disallowed types referenced by tasks>>: live JVM state (`HttpClient`, `Connection`, `Thread`, `Socket`, `ClassLoader`) or unsupported Gradle types (`Configuration`, `SourceSet`) held in task fields.
* <<configuration_cache_requirements.adoc#config_cache:requirements:build_listeners, Using Build Listeners>>: registering `BuildListener`, `TaskExecutionListener`, `gradle.taskGraph.afterTask`, or `gradle.buildFinished` at all (Gradle emits a CC problem for any such registration); replace with a `BuildService` implementing `OperationCompletionListener`.
* <<configuration_cache_requirements.adoc#config_cache:requirements:task_extensions, Accessing Task Extensions or Conventions>>: reading `task.extensions[...]` or `task.ext` at execution time; expose the value as a typed task property.
* <<configuration_cache_requirements.adoc#config_cache:requirements:gradle_model_types, Gradle Model Types>>: capturing `Project`, `Gradle`, `Settings`, or `SourceSet` references in task fields; capture the value you actually need into a typed `Property` at configuration time.

**Optional cleanups — these builds work under CC, but reading eagerly at configuration time forces the whole configuration phase to re-run whenever the value changes. If a specific input rotates often on CI, switching to the lazy provider is worth it. Otherwise leave them:**

* <<configuration_cache_requirements.adoc#config_cache:requirements:reading_sys_props_and_env_vars, Reading System Properties and Environment Variables>>: `System.getenv` or `System.getProperty` at configuration time → `providers.environmentVariable` or `providers.systemProperty`.
* <<configuration_cache_requirements.adoc#config_cache:requirements:undeclared_file_read, Undeclared Reading of Files>>: direct `File.readText()` or `file().exists()` at configuration time → `providers.fileContents`.

If you only have an error fragment to start from, use <<configuration_cache_debugging.adoc#config_cache:debugging:common_errors, Common Errors and Where to Look>> on the Debugging page to translate it to the underlying pattern and the fix.

[[config_cache:adoption_guide:when_small_fix_isnt_enough]]
=== When the Small-Fix Path Isn't Enough

A subset of CC violations does not fit the small-fix path because the underlying task does fundamentally execution-time work that CC cannot serialize.
The most common pattern is a task that mutates `Configuration` objects at `@TaskAction` time (typically via `Configuration.copyRecursive()`, then mutating dependencies, then resolving the copy).
Live `Configuration` references in task fields are not CC-serializable, runtime configuration creation is not CC-supported, and no `@TaskAction`-level refactor preserves both the behavior and CC-compatibility.

[NOTE]
====
`Configuration` implements `FileCollection`, so consuming a configuration through a `FileCollection`-typed input (`@InputFiles`, `ConfigurableFileCollection`, `Provider<FileSystemLocation>`) *is* CC-safe — CC serializes the resolved file set, not the `Configuration` object.
What breaks CC is storing a `Configuration`-typed field on a task and using it at execution time (calling `copyRecursive()`, resolving, adding dependencies, etc.).
====

Indicators that this pattern applies:

* The violation traces back to `@TaskAction` code that calls `project.dependencies.create(...)`, `Configuration.copyRecursive()`, or otherwise constructs new `Configuration` instances at execution time.
* The fix on the requirements page is structurally hard to apply because what you actually need is a *different* `Configuration` to resolve, and you don't have one created yet at configuration time.
* Multiple report entries trace back to the same `@TaskAction`, suggesting the issue is the task's resolution model rather than any one line of code.

The canonical CC-safe pattern is to move all references to `Configuration` objects to **configuration time**, and to expose the results of resolution as `Provider<ResolvedComponentResult>` (or another resolution-result Provider type) that gets serialized into the CC entry and resolved lazily at execution time.

The shape of the rewrite:

. At configuration time (inside `project.afterEvaluate { ... }` or directly in the plugin's `apply()`), create the sibling/probe `Configuration` objects on the right project.
. Configure them with their substituted dependencies, attributes, resolution-strategy rules, and component-selection rules.
All of this is config-time work, so `project.X` access is fine.
. Capture `sourceConfig.incoming.resolutionResult.rootComponent` (and any sibling probe's equivalent) as `Provider<ResolvedComponentResult>`.
Providers are CC-serializable; the resolution they describe is lazy.
. Pass the captured Providers to the task as inputs (as fields on the task, or as a `ListProperty<MyData>` containing them).
. In `@TaskAction`, call `.get()` on the Providers and walk the `ResolvedComponentResult` tree to extract the per-dependency information you need.

This pattern is what makes resolution lazy under CC: the `Configuration` object is only directly referenced at configuration time.
It is the result of resolving the `Configuration` that is described as a lazy graph that CC can serialize, replay, and invalidate when relevant inputs change.

CC serializes the transitive closure of everything reachable through the task's fields, so it is not enough for the task class itself to be `Project`-free — every helper, reporter, or service the task holds a reference to must also be `Project`-free (and serializable) all the way down.
The typical pattern is to capture the project information those helpers actually use — `projectPath: String` for log labels, `projectDir: File` for output paths, a `Logging.getLogger(...)` instance for the logger — as plain serializable values at configuration time, and hand those to the helpers instead of a live `Project`.

==== What Architectural Rewrites Cost

If your task fits this pattern, plan for the following:

* **The Gradle minimum version of your plugin may go up.** `Provider<ResolvedComponentResult>` (as returned from `ResolutionResult.getRootComponent()`) is a Gradle 7.4+ API. If your plugin currently supports older Gradle versions, the CC-safe rewrite makes those versions unsupportable.
* **Public APIs may need to change.** A `Spec<Configuration>` predicate that takes a live `Configuration` cannot be made CC-safe. Replacing it with a `Spec<String>` predicate that operates on the configuration's name (or another captured value at execution time) is a breaking source-level change for downstream users. There is rarely a fully source-compatible path.
* **The whole object graph reachable from the task will need to be refactored together.** Removing `Project` from one class is usually not enough; every reporter, helper, or service the task holds a reference to (directly or transitively) has to be `Project`-free and serializable as well. Plan the work as a single refactoring effort rather than class-by-class.
* **Some test fixtures may need updating.** Tests that assert on specific output ordering, on dependencies that no longer appear in your report, or on specific Gradle versions you no longer support, will need to be loosened or rewritten.

==== Validating an Architectural Rewrite

Run your plugin's integration tests with the Configuration Cache enabled.
Either pass `--configuration-cache` to your TestKit `GradleRunner` invocations, or set `org.gradle.configuration-cache=true` in the test project's `gradle.properties`.
The rewrite is CC-compatible on the tested Gradle version if those tests pass on a clean run and on a repeated run (verifying both the CC store and the CC hit).
See <<test_kit.adoc#sub:test-kit-configuration-cache, TestKit and the Configuration Cache>> for setup details on the TestKit side.

If your plugin's existing tests use a *self-application pattern* (applying the plugin's own published version to test itself), those tests exercise whatever is published, not the local source — so they will not validate a local rewrite until a new version is published.
Convert the affected tests to a TestKit-based integration test suite that builds and applies the plugin under test directly.

[IMPORTANT]
====
Avoid using `org.gradle.configuration-cache.problems=warn` to suppress problems during migration.

The Gradle Configuration Cache team does not recommend warning mode as an adoption mechanism.
Warning mode lets the build store and load CC entries that may be missing required state, which produces hard-to-diagnose deserialization failures and silent correctness issues later.
The reliable path is what this guide describes: fix problems iteratively, and where a task cannot be fixed yet, mark it incompatible per <<config_cache:adoption_guide:step8, Step 8>>.

For the full caveat about warning mode interacting with incompatible tasks, see <<configuration_cache_debugging.adoc#sec:enable_warning_mode, Enable Warning Mode>> on the debugging page.
====

[[config_cache:adoption_guide:step8]]
== Step 8: Mark Incompatible Tasks

Some tasks (your own, or from a third-party plugin you do not control) cannot reasonably be made CC-compatible on your current timeline.
For these tasks, the appropriate mechanism is the per-task opt-out:

====
[.multi-language-sample]
=====
.build.gradle.kts
[source,kotlin]
----
tasks.named("legacyReport") {
    notCompatibleWithConfigurationCache("Uses the Project at execution time; tracked in TEAM-123")
}
----
=====
[.multi-language-sample]
=====
.build.gradle
[source,groovy]
----
tasks.named("legacyReport") {
    notCompatibleWithConfigurationCache("Uses the Project at execution time; tracked in TEAM-123")
}
----
=====
====

Effects of this opt-out:

* CC problems *in this specific task* no longer cause the build to fail.
* If the task is part of the requested task graph, Gradle discards the cache entry at the end of the build.
* Any invocation that does *not* include this task can still produce and reuse a CC entry.

For the reason string, include a ticket reference or a short explanation so anyone reading the build script later understands why and when the marker can be removed.

For details, see <<configuration_cache_debugging.adoc#config_cache:task_opt_out, Declare Incompatible Tasks>>.
For the interaction between this marker and warning mode (which is one of several reasons not to use warning mode), see <<configuration_cache_debugging.adoc#sec:enable_warning_mode, Enable Warning Mode>>.

[TIP]
====
Track the count of `notCompatibleWithConfigurationCache(...)` calls in your build as a build-health metric.
The number should only decrease over time.
If it grows, the team is adding new incompatible tasks faster than existing ones are being fixed.
====

[[config_cache:adoption_guide:step9]]
== Step 9: Roll Out to Your Team

Once your dev loop is stable locally, broaden the rollout.

Commit `org.gradle.configuration-cache=true` to the project-level `gradle.properties` so every developer picks up CC uniformly.
Note that this also flips CC on for every CI job that reads the same `gradle.properties`.
Any CI job that is not yet CC-compatible — publishing steps, release automation, plugin-specific tasks that predate CC — must opt out on the command line at this point:

[source,bash]
----
./gradlew <task> --no-configuration-cache
----

Add `--no-configuration-cache` only to the specific jobs that need it, not globally, so the rest of CI stays under CC and continues catching regressions.
Step 10 layers on CI-specific setup (encryption key, read-only-mode caveat) once these per-job opt-outs are in place.

Communicate the change to your team:

* Expected impact: faster builds in most cases, occasionally a stricter error from a task they authored.
* How to disable CC for a single invocation: `--no-configuration-cache`.
* Where to find the CC report when something fails.
* The list of currently-incompatible tasks and the tracking issue for each.

[TIP]
====
A common rollout pattern for cautious teams: enable CC by default for one or two weeks; encourage developers to report any regression; then move on to CI.
If you have an internal build-monitoring tool (Develocity, build scans, custom telemetry), use it to confirm that build durations are dropping and CC store/load times are healthy before you treat the rollout as done.
====

[[config_cache:adoption_guide:step10]]
== Step 10: Enable on CI

Once the developer workflow is stable, turn CC on in CI.

**The primary goal of CC on CI is regression prevention, not cache hits.**
CI catches CC-breaking changes — a plugin bump, a build-logic edit — before they hit developers' machines.
Cache hits on CI *can* happen, but they require a specific setup: a `GRADLE_USER_HOME` that persists across runs, a shared `GRADLE_ENCRYPTION_KEY`, and non-ephemeral agents.
Ephemeral runners will always cache-miss, and that is expected.
Even without hits, CC's parallel task execution recovers some of the overhead of storing the entry, so enabling CC on CI is rarely a net negative.

Do not enable warning mode on CI. CI is where regressions should fail visibly.

[source,properties]
.gradle.properties (committed)
----
org.gradle.configuration-cache=true
----

=== Encryption Key on Shared CI Environments

The CC entry on disk is encrypted with a machine-specific AES key stored in `GRADLE_USER_HOME`.
On CI agents that share or rebuild `GRADLE_USER_HOME` across runs, you need to provide a stable key explicitly via the `GRADLE_ENCRYPTION_KEY` environment variable, or each new agent will be unable to decrypt entries written by previous agents.

See <<configuration_cache_requirements.adoc#config_cache:secrets:configuring_encryption_key, Providing an Encryption Key>> and <<configuration_cache_requirements.adoc#config_cache:secrets:generating_encryption_key, Generating an Encryption Key>>.

=== GitHub Actions Specifically

The official `gradle/actions` GitHub Action only saves and restores Configuration Cache entries when a `cache-encryption-key` is provided.
Without it, CC entries are written on every job but never restored, incurring storage cost without realizing the performance benefit.
Configure the action with the encryption key:

[source,yaml]
----
- name: Set up Gradle
  uses: gradle/actions/setup-gradle@v4
  with:
    cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
----

Generate the key once with `openssl rand -base64 16` and store it as a GitHub Actions secret named `GRADLE_ENCRYPTION_KEY`.

[[config_cache:adoption_guide:escape_hatches]]
== When Full Adoption Is Not Possible

Some tasks, plugins, or build features cannot reasonably be made CC-compatible on a given timeline.
Gradle provides escape hatches, listed below in order of preference.

[cols="1,4"]
|===
| Preferred order | Escape hatch

| 1 (recommended)
| `task.notCompatibleWithConfigurationCache("reason")` per task. Surgical, well-understood, supported. See <<configuration_cache_debugging.adoc#config_cache:task_opt_out, Declare Incompatible Tasks>>.

| 2
| Suppressible inputs: `org.gradle.configuration-cache.inputs.unsafe.ignore.file-system-checks` and `org.gradle.configuration-cache.inputs.unsafe.ignore.in-serialization`. Use sparingly to mitigate noisy invalidation while the underlying plugin is being fixed. See <<configuration_cache_enabling.adoc#config_cache:usage:input_opt_outs:input_detection, Input-detection opt-outs>>.

| 3
| `--no-configuration-cache` for a specific invocation. Permanent flag in your CI job for one specific workflow. Reasonable when a single command is fundamentally incompatible.

| Never
| `org.gradle.configuration-cache.problems=warn` as a permanent setting. The Gradle CC team explicitly does not recommend warning mode for adoption. See <<configuration_cache_debugging.adoc#sec:enable_warning_mode, Enable Warning Mode>>.
|===

NOTE: Read-only mode (`org.gradle.configuration-cache.read-only=true`) is a performance optimization for ephemeral CI agents where writing a CC entry would be wasted I/O, not an escape hatch for incomplete adoption. It skips CC serialization on misses, so it also hides CC problems that would otherwise fail the build. Do not use it to work around adoption gaps. See <<configuration_cache_enabling.adoc#config_cache:usage:read_only, Making the Configuration Cache Read-Only>>.

[[config_cache:adoption_guide:faq]]
== Frequently Asked Questions

[[config_cache:adoption_guide:faq:warn_mode]]
=== Should I use `org.gradle.configuration-cache.problems=warn` during migration?

**No.** The Gradle Configuration Cache team does not recommend warning mode as an adoption mechanism.

Warning mode lets the build store and load CC entries that may be missing required state, which produces hard-to-diagnose failures (deserialization errors, silent correctness issues) on subsequent runs.
It also masks problems that would otherwise force you to fix them, allowing incompatibilities to accumulate.

The recommended adoption ratchet is:

. Enable CC in `gradle.properties` (`org.gradle.configuration-cache=true`).
. Run the build; for each failure, fix it iteratively (Step 7) or mark the offending task with `notCompatibleWithConfigurationCache("reason")` (Step 8).
. Track the count of incompatible tasks as a build-health metric and reduce it over time.

See the canonical warning at <<configuration_cache_debugging.adoc#sec:enable_warning_mode, Enable Warning Mode>> and <<configuration_cache_enabling.adoc#config_cache:usage:ignore_problems, Ignoring Configuration Cache Problems>>.

[[config_cache:adoption_guide:faq:dependency_resolution]]
=== Does CC change dependency resolution behavior?

**Yes. Dependency resolution is eager under CC, which is an important behavioral difference.**

Without CC, dependency resolution is lazy.
A `Configuration` is only resolved when a task that consumes its files actually runs.
With CC, the resolved state of every declared `Configuration` in the task graph must be captured before execution begins, so it can be serialized into the cache entry.

A practical consequence: with CC, a typo'd repository URL or an invalid dependency in a `Configuration` that *would have been skipped* in a vanilla build will surface as a resolution error.
The build is more strictly correct under CC, not broken.

If you see a CC build fail with a dependency-resolution error while the same build succeeds without CC, the dependency declaration was already invalid; CC just made it visible.

Eager resolution also has a performance side.
For most builds the parallel task execution and skipped configuration phase on cache hits make CC a clear net win, but there are pathological cases — most often builds with many declared `Configuration` objects that a vanilla task selection would have left unresolved — where the up-front resolution work outweighs the savings and the build runs slower under CC than without it.
If a specific invocation is noticeably slower with CC than without, profile that invocation with a <<build_scans.adoc#build_scans, build scan>> to see whether resolution time (rather than configuration or execution time) is the culprit.

[[config_cache:adoption_guide:faq:parallel]]
=== Does `--no-parallel` work with CC?

Not in the way the flag name implies.

CC enables **intra-project parallel task execution** regardless of `--parallel`, `--no-parallel`, or `org.gradle.parallel`.
Every task has its own isolated deserialized state and no project-level lock contention, so tasks within a project run in parallel automatically.

`--parallel` still controls **cross-project** parallel task execution, exactly as it does without CC.
CC's intra-project parallelism composes with it: with `--parallel`, tasks run in parallel both within and across projects; without it, only within.

`--no-parallel` is effectively ignored for CC's intra-project parallelism — task execution within each project stays parallel — but it does disable cross-project parallelism.

Writing the CC entry itself in parallel is a separate, incubating option: `-Dorg.gradle.configuration-cache.parallel=true`.
It is unrelated to `--parallel`.

[[config_cache:adoption_guide:faq:plugin_without_cc]]
=== Will my plugin still work for users who do not enable CC?

Yes.
Making a plugin CC-compatible is **additive**: a CC-compatible plugin remains fully functional in builds that do not enable CC.
The same code paths run; CC just imposes additional structural rules (no `Project` at execution time, no eager state-sharing across tasks, and so on) that, when satisfied, also produce a correct non-CC build.

You do not need to publish two versions of a plugin (one with CC, one without).
One version that satisfies CC requirements works in both modes.

[[config_cache:adoption_guide:faq:ide]]
=== How does CC interact with IDE syncs?

**IDE syncs do not yet benefit from the Configuration Cache.**
When IntelliJ IDEA, Android Studio, or Eclipse imports or re-syncs a Gradle project, configuration runs from scratch every time.

What does benefit from CC is **running tasks from the IDE**.
A `./gradlew test` triggered from IntelliJ's Gradle tool window goes through the same daemon and the same CC machinery as the command line.

This may change in the future; the Gradle team is actively investigating IDE-sync CC support as a separate initiative.
See <<configuration_cache.adoc#config_cache:ide, Configuration Cache and the IDE>>.

[[config_cache:adoption_guide:faq:secrets]]
=== Are secrets in my build safe under CC?

Generally yes, with one important caveat.
The CC entry on disk is encrypted with a per-machine AES key.
However, any value that flows into a task field (including credentials, tokens, API keys) is serialized into the cache entry.

The recommended pattern is to keep secrets in `GRADLE_USER_HOME/gradle.properties`.
The *contents* of that file are not included in the cache; only its fingerprint is.
See <<configuration_cache_requirements.adoc#config_cache:secrets, Handling of Credentials and Secrets>> for the full guidance and the encryption key details.

[[config_cache:adoption_guide:faq:detect]]
=== Can my plugin detect whether CC is enabled?

Yes.
Inject the link:{javadocPath}/org/gradle/api/configuration/BuildFeatures.html[`BuildFeatures`] service and read its `configurationCache.active` property.
See <<configuration_cache_enabling.adoc#config_cache:reacting, Detecting the Configuration Cache from Build Logic>> for the canonical pattern.

Use this sparingly.
The goal of CC adoption is uniform behavior, not a different code path depending on CC state.
The legitimate use cases are: disabling an optional plugin feature that is not yet CC-compatible; emitting an informational message to users; tweaking telemetry.

[[config_cache:adoption_guide:where_to_help]]
== Where to Get Help

If you hit a CC problem that this guide does not cover:

* For problems with a **Gradle core plugin** or a **core Gradle feature**, search the link:https://github.com/gradle/gradle/issues?q=is%3Aissue+label%3Ain%3Aconfiguration-cache[`in:configuration-cache` issue label] and file a new issue if needed.
* For problems with a **community plugin**, file the issue directly with the plugin's own issue tracker.
* For general discussion, the link:https://discuss.gradle.org/[Gradle community forums] and the link:https://gradle.org/slack-invite[Gradle Community Slack] are good places to ask.

When filing a bug report, include:

* A link to the page in this guide that you were following.
* The Gradle version and any relevant plugin versions.
* The full build failure output.
* The self-contained `configuration-cache-report.html` file from `build/reports/configuration-cache/`.
* A minimal reproducer build if you can produce one.

[[config_cache:adoption_guide:next]]
== What's Next

Once CC is enabled, the metric to watch is the **cache hit rate**: the fraction of builds that reuse an existing entry rather than recomputing one.
A near-100% hit rate on the inner dev loop signals a healthy adoption; a degrading rate over time signals new untracked inputs creeping in.
link:https://gradle.com/develocity/[Develocity] and link:https://scans.gradle.com[Build Scans] expose per-task and per-build CC hit/miss telemetry.
When hit rates drop, open the CC report's "build configuration inputs" list and look for inputs that change every run but are not actually used.
Each is either a candidate for switching to a `provider` (so the input is dropped if unused) or for one of the temporary opt-outs in <<configuration_cache_enabling.adoc#config_cache:usage:input_opt_outs, Temporary Opt-Outs for Configuration Cache Behavior>>.

The next performance step beyond plain CC is typically **Parallel Configuration Cache** (`org.gradle.configuration-cache.parallel=true`), which parallelizes the storing and loading of CC entries themselves.
It is still incubating; see <<configuration_cache_enabling.adoc#config_cache:usage:parallel, Enabling Parallel Configuration Caching>>.

Looking further ahead, Isolated Projects builds on CC's per-project isolation to enable parallel project configuration, but it is pre-alpha and depends on CC being fully adopted first.
For the current state of the Configuration Cache itself, see <<configuration_cache_status.adoc#config_cache_status, Configuration Cache Status>>.
