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

Since Gradle 9.0.0, the Configuration Cache is the 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.

Adoption Goals

You have successfully adopted the Configuration Cache when:

  1. org.gradle.configuration-cache=true is set in your gradle.properties (committed, not local-only).

  2. Your everyday development tasks (the ones developers run dozens of times a day) hit the cache.

  3. 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.

  4. Any task that genuinely cannot be CC-compatible is explicitly marked using notCompatibleWithConfigurationCache("reason").

Before You Start

Run through this checklist before you flip the switch.

# 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.

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.

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 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.

    • Kotlin Gradle Plugin (KGP): KGP is coupled to specific Gradle versions, so a Gradle bump often requires bumping KGP to stay compatible. See the Kotlin compatibility matrix.

    • Kotlin source: bumping KGP brings a newer Kotlin compiler, which can surface deprecations or compilation errors in your application code.

  • 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 plugin status page for known limitations on any plugin you depend on.

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:

$ ./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:

Calculating task graph as no cached configuration is available for tasks: help
...
Configuration cache entry stored.

On the second run, you should see:

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 Step 5: Dry-Run Your Real Dev Loop.

Step 3: When You Hit a Miss or Failure, Read the Report

If Step 2 succeeded cleanly with Configuration cache entry stored., there is no report to read yet. Skip ahead to 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 Use the Configuration Report on the Debugging page.

$ ./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.

Do not confuse the Configuration Cache report with the general Gradle Problems report at build/reports/problems/problems-report.html.

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 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:

  1. Open the linked section of the requirements doc from the CC report entry.

  2. Apply the canonical fix in your build script, settings script, init script, or plugin source.

  3. 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 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 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.

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.

Step 5: Dry-Run Your Real Dev Loop

Once help succeeds, expand to the tasks your team actually runs (the ones you identified in 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:

$ ./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 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.

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:

$ ./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 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.

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 Step 9: Roll Out to Your Team.

Step 7: Fix Problems Iteratively

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 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 for your Build Logic.

The patterns fall into two groups.

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

  • Using the Project Object at Execution Time: project.copy {}, project.version, project.rootDir, or anything else reached through project from a @TaskAction or doLast {}.

  • Disallowed types referenced by tasks: live JVM state (HttpClient, Connection, Thread, Socket, ClassLoader) or unsupported Gradle types (Configuration, SourceSet) held in task fields.

  • 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.

  • Accessing Task Extensions or Conventions: reading task.extensions[…​] or task.ext at execution time; expose the value as a typed task property.

  • 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:

If you only have an error fragment to start from, use Common Errors and Where to Look on the Debugging page to translate it to the underlying pattern and the fix.

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.

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:

  1. At configuration time (inside project.afterEvaluate { …​ } or directly in the plugin’s apply()), create the sibling/probe Configuration objects on the right project.

  2. 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.

  3. Capture sourceConfig.incoming.resolutionResult.rootComponent (and any sibling probe’s equivalent) as Provider<ResolvedComponentResult>. Providers are CC-serializable; the resolution they describe is lazy.

  4. Pass the captured Providers to the task as inputs (as fields on the task, or as a ListProperty<MyData> containing them).

  5. 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 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.

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 Step 8.

For the full caveat about warning mode interacting with incompatible tasks, see Enable Warning Mode on the debugging page.

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:

build.gradle.kts
tasks.named("legacyReport") {
    notCompatibleWithConfigurationCache("Uses the Project at execution time; tracked in TEAM-123")
}
build.gradle
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 Declare Incompatible Tasks. For the interaction between this marker and warning mode (which is one of several reasons not to use warning mode), see Enable Warning Mode.

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.

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:

./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.

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.

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.

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.

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:

- 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.

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.

Preferred order Escape hatch

1 (recommended)

task.notCompatibleWithConfigurationCache("reason") per task. Surgical, well-understood, supported. See 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 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 Enable Warning Mode.

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 Making the Configuration Cache Read-Only.

Frequently Asked Questions

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:

  1. Enable CC in gradle.properties (org.gradle.configuration-cache=true).

  2. Run the build; for each failure, fix it iteratively (Step 7) or mark the offending task with notCompatibleWithConfigurationCache("reason") (Step 8).

  3. Track the count of incompatible tasks as a build-health metric and reduce it over time.

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 scan to see whether resolution time (rather than configuration or execution time) is the culprit.

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.

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.

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 and the IDE.

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 Handling of Credentials and Secrets for the full guidance and the encryption key details.

Can my plugin detect whether CC is enabled?

Yes. Inject the BuildFeatures service and read its configurationCache.active property. See 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.

Where to Get Help

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

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.

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. Develocity and 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 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 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.