The Configuration Cache improves build performance by caching the result of the configuration phase and reusing it for subsequent builds.

configuration cache 4

This feature will be enabled by default in Gradle 10. Currently, it has the following limitations:

Since Gradle 9.0.0, the Configuration Cache is the preferred mode of execution.

Configuration Cache Basics

The Gradle Build Lifecycle consists of three phases: initialization, configuration, and execution.

  1. The initialization phase determines the structure of the build.

  2. The configuration phase evaluates what work needs to be done.

  3. The execution phase performs that work.

Gradle’s Build Cache has long optimized the execution phase by reusing previously built outputs and parallelizing as much work as possible.

The Configuration Cache builds on this idea of work avoidance and parallelization. When enabled, the Configuration Cache allows Gradle to skip the configuration phase entirely if nothing that affects the build configuration (such as build scripts) has changed. Additionally, Gradle applies performance optimizations to task execution.

To do this, Gradle does the following:

Cache What It Caches Cache Key Work Avoidance Parallelism

Build Cache

Task outputs and artifact transform outputs

Each task’s declared inputs (sources, classpath, action implementation)

Skips task execution by reusing previous outputs

Composes with --parallel for cross-project parallel execution

Configuration Cache

The result of the configuration phase: the task graph, each task’s configured state, and dependency information

Build logic and environment (build scripts, init scripts, environment variables and properties read at configuration time, and more)

Skips the configuration phase entirely on a cache hit

Enables parallel task execution within the same project

The Configuration Cache and the Build Cache store different types of data:

  • Build Cache: Stores outputs and intermediate files of the build (e.g., task outputs, artifact transform outputs).

  • Configuration Cache: Stores the build configuration for a particular set of tasks, capturing the output of the configuration phase.

configuration cache 2

How It Works

When the Configuration Cache is enabled, and you run Gradle for a set of tasks (for example, ./gradlew check) that you have already run once, Gradle will avoid re-configuring the build if no relevant inputs to configuration have changed.

The Build Lifecycle

Enabling the Configuration Cache changes how Gradle moves through the three build phases. The phases themselves are unchanged: initialization, configuration, and execution. But whether they run, and when they do, depends on a cache hit or miss.

Phase Without the Configuration Cache With the Configuration Cache

Initialization

Runs every build. Detects the settings file, creates the Settings and Project instances, and determines the structure of the build.

Runs every build. Locates the cache entry, verifies if it is up-to-date and deserializes the task graph, or falls back to full initialization if there is no valid entry.

Configuration

Runs every build. Evaluates init scripts, the settings script, buildSrc, and every project’s build script; applies plugins; computes the task graph.

Runs only on a cache miss. On a hit, the entire configuration phase is skipped — init scripts, the settings script, build scripts, and plugins are not re-evaluated. Gradle restores the task graph directly from the cache.

Execution

Runs the scheduled tasks against the just-computed task graph.

Runs the scheduled tasks against the deserialized task graph. Tasks are isolated from each other through serialization (see Serialization), which enables intra-project parallel execution.

configuration cache 6

The Hit-or-Miss Decision

At the start of every invocation, Gradle uses the requested tasks, their arguments, and some other environmental information as a cache key, then checks whether there is a stored entry matching the key. If a candidate entry exists, Gradle compares the build’s current configuration inputs against the entry’s recorded fingerprint (the set of build inputs discovered when preparing the entry and the values they had at that time). A hit means every recorded input still matches; a miss means at least one input has changed (or no entry exists for this key yet).

Cache Miss

configuration cache 1

On a cache miss (the first build with the Configuration Cache enabled, or any build whose configuration inputs have changed), Gradle runs the configuration phase as it would without the cache:

  1. Run init scripts.

  2. Run the settings script, applying any requested settings plugins.

  3. Configure and build the buildSrc project, if present.

  4. Run build scripts, applying any requested project plugins. If plugins come from included builds, Gradle builds them first.

  5. Calculate the task graph, executing deferred configuration actions.

Gradle then serializes the resulting task graph and writes it to the cache before execution begins.

Even on a miss, Gradle then loads the task graph back from the cache it just wrote and executes against the loaded copy. This always-load behavior is intentional: it guarantees that hits and misses execute through the same code path, so any serialization-related issue surfaces on the first run rather than later. It also means that parallel task execution within the same project is available even on a cache miss.

For plugin authors, the always-load semantic has a practical consequence: a task that works during the configuration phase but breaks after a round-trip through serialization will fail on the first build with the Configuration Cache enabled, not on a later run. This is why we recommend testing your plugin with --configuration-cache from the start of development.

Cache Hit

configuration cache 3

On a cache hit (a subsequent invocation of the same tasks where no configuration inputs have changed), Gradle skips the configuration phase entirely. Instead, Gradle restores the task graph by deserializing the cache entry, then proceeds straight to execution.

Because the configuration phase is skipped on a hit, any logic registered through configuration-phase callbacks, such as BuildListener, Gradle.beforeProject, Gradle.afterProject, or Gradle.taskGraph.whenReady, will not re-execute. Build logic that needs to react to task execution should use a Build Service registered as an OperationCompletionListener instead. See Using Build Listeners for the full list of affected APIs and their replacements.

Configuration Cache Entries

A Configuration Cache entry stores:

  • The set of tasks to run

  • Their configuration details

  • Task dependency information (the relationships between tasks that determine execution order)

For the precise contents and how they’re written to disk, see Serialization.

Build Configuration Inputs

The following elements determine whether a Configuration Cache entry is valid:

  1. Gradle environment

    • GRADLE_USER_HOME

    • Gradle Daemon JVM

  2. Init scripts

  3. Contents of buildSrc and included build logic builds (build scripts, sources, and intermediate build outputs)

  4. Build and Settings scripts, including included scripts (apply from: foo.gradle)

  5. Gradle configuration files (Version Catalogs, dependency verification files, dependency lock files, gradle.properties files)

  6. Contents of files read at configuration time

  7. File system state checked at configuration time (file presence, directory contents, etc.)

  8. Custom ValueSource values obtained at configuration time (this also includes built-in providers, like providers.exec and providers.fileContents).

  9. System properties used during the configuration phase

  10. Environment variables used during the configuration phase

Serialization

Serialization is the mechanism that makes the Configuration Cache possible. When Gradle stores a cache entry, it walks the configured task graph and writes a binary snapshot of every task and the state reachable from it. When Gradle loads a cache entry, it reverses the process to reconstruct the task graph for execution.

What gets serialized

A Configuration Cache entry contains:

  • The set of tasks scheduled to run, in dependency order.

  • Each task instance’s complete state: every instance field is serialized, including private fields and fields that are not annotated as @Input, @OutputFile, or @Internal.

  • The graph of objects reachable through those fields (file collections, providers, nested beans, dependency results, build service references).

Anything not reachable from a scheduled task is excluded. Some types that are tied to the Gradle build model cannot be serialized by the Configuration Cache (Project, Gradle, Settings, ClassLoader, Thread, and others enumerated in the requirements). References to these types are rejected with a Configuration Cache problem at store time, before they can cause a runtime failure on a later load.

Because every reachable field is captured, sensitive values held in task state (such as credentials, tokens, and API keys) also land in the cache entry. Gradle encrypts cache entries on disk to mitigate the risk of accidental exposure. See Handling of Credentials and Secrets for details.

The serialized task graph is stored alongside a separate fingerprint: the recorded list of configuration inputs the build observed (see Build Configuration Inputs). The two are distinct: the entry holds the result of configuration, the fingerprint holds the evidence that the result is still valid. Loading an entry on a hit uses both: Gradle replays the fingerprint to confirm validity, then deserializes the entry to reconstruct the task graph.

configuration cache 7

How Gradle serializes task state

Gradle serializes all scheduled task instances and the object graph reachable from them. By default, serialization walks each task’s fields using reflection and recursively serializes each one. Most plugin-defined task and extension types are handled this way without any extra work from the author.

Some types are subject to more specific handling:

  • Providers and Properties — the Configuration Cache attempts to pre-compute and simplify provider chains it discovers while walking the task graph. Some providers have their value computed at store time and the computed value replayed at load time, so those providers do not re-evaluate their original sources during execution. See the Provider javadocs for more details on the provider model.

  • ConfigurationsConfiguration instances are resolved at store time and the result is serialized as a file collection. This is why dependency resolution is eager under the Configuration Cache: every declared configuration must be resolved at store time so its files can be captured into the cache entry. At load time, the restored object is a FileCollection, not the original Configuration. Task authors who only need files (for classpath or other processing) should declare their task fields as FileCollection rather than Configuration.

Gradle handles many standard Java and Gradle-specific types (collections, files, file collections, dependency results, build services, lambdas, and more) in a customized fashion. See Sharing Mutable Objects for details on how this affects object identity. Types that cannot be safely serialized (ClassLoader, Thread, and others) are rejected at store time rather than producing a failure on later load.

Gradle does not use Java Serialization, but it understands a subset of its features for classes that opt in via Externalizable or writeObject / readObject. Using those features is supported but incurs a performance penalty and should be avoided in new code.

Task isolation through serialization

Serialization gives each task its own deserialized copy of the state it references. Two tasks that, before serialization, both held a reference to the same ArrayList will end up holding independent (but equal) lists after the cache is loaded. This isolation is what makes intra-project parallel task execution safe: tasks cannot leak mutable state to each other through a shared reference.

configuration cache 8

Reference equality is preserved for instances of user-defined classes, but is not preserved for String, File, or many java.util.Collection types. Sharing Mutable Objects walks through the implications with a runnable example.

After deserialization, each task operates on its own isolated copy of the object graph. If two tasks shared the same ArrayList at configuration time, they each get an independent copy at execution time. When task A adds an element to its list during execution, task B will not see the change — its copy is a separate object.

This is a common source of subtle bugs: code that worked without the Configuration Cache (where both tasks referenced the same in-memory list) silently breaks when the cache isolates them. To share mutable state across tasks, use a Build Service.

Detecting configuration inputs

Throughout the configuration phase, Gradle records the build’s configuration inputs: every environment variable, system property, file read, and file-system check the build performs during configuration. To capture those reads without requiring plugins to declare them, Gradle uses a Java agent and rewrites the bytecode of build-script and plugin classes at load time. Bytecode Modifications and Java Agent describes the implications and the Worker API escape hatch for libraries that can’t tolerate rewriting.

Performance Improvements

Beyond skipping the configuration phase on a cache hit, the Configuration Cache enables two performance optimizations that are not available without it.

running help

Parallel Task Execution

When the Configuration Cache is enabled, tasks within the same project can run in parallel, subject to their declared input/output dependencies. Without the Configuration Cache, intra-project parallelism is constrained because tasks within a project share access to the Project instance and contend on project-level locks. The Configuration Cache eliminates this contention by giving each task an isolated, deserialized copy of its state (see Serialization), so tasks no longer need to acquire project-level locks at execution time.

This complements Gradle’s existing --parallel flag, which executes tasks across different projects in parallel. With the Configuration Cache, parallelism applies within a single project as well.

Intra-project parallel execution is always enabled when the Configuration Cache is active. The --no-parallel flag and org.gradle.parallel=false do not disable it. Builds that depend on tasks within the same project running sequentially should properly wire task inputs and outputs to enforce task order or use shared build services to coordinate access to shared resources.

Cached Dependency Resolution

Dependency resolution is one of the more expensive parts of a build’s configuration phase. Gradle walks the dependency graph, resolves versions, and computes the resulting classpaths.

When the Configuration Cache is enabled, the resolved dependency state becomes part of the cache entry: the resolved configurations, their files, and the artifact metadata are all serialized and replayed on a cache hit. On a hit, dependency resolution does not re-run; tasks see the same resolved classpaths that the previous build computed. On a miss, resolution runs as part of the configuration phase, and the result is stored alongside the rest of the entry.

Resolution under the Configuration Cache covers every resolvable configuration used as a task input, regardless of whether the tasks needing it run. See Behavioral Differences for the implications.

Behavioral Differences

Enabling the Configuration Cache changes how every build executes — task isolation, eager dependency resolution, and the skipped configuration phase affect every build, whether it hits or misses the cache. For well-structured builds these differences are invisible, but builds that rely on patterns the cache doesn’t support will see new failures. The sections below describe the most important differences to be aware of.

Dependency resolution is eager

Without the Configuration Cache, dependency resolution is lazy: a configuration is resolved only when a task that needs its files actually runs. A build containing a configuration that cannot be resolved due to an invalid dependency or a misconfigured repository may still succeed if no scheduled task ever resolves that configuration.

With the Configuration Cache enabled, the resolved state of every declared configuration must be captured before execution begins so that it can be serialized into the cache entry. Resolution happens during serialization, at the end of the configuration phase, regardless of whether the tasks that need a given configuration are scheduled to run.

This includes tasks that would otherwise be skipped at execution time. For example, the Java plugin’s compile task is skipped when its source set is empty; without the Configuration Cache the task’s compile classpath is never resolved (so a configuration with an invalid dependency may go undetected). With the Configuration Cache enabled, resolution still happens at store time because the resolved state must be captured into the cache entry.

Builds with latent dependency-declaration errors (such as an invalid coordinate or a misconfigured repository in an otherwise-unused configuration) may pass without the Configuration Cache and fail with it enabled.

Although dependency resolution by the Configuration Cache happens at what is considered configuration time, this doesn’t mean build logic should stop following the general rule of resolving configurations only at execution time. Gradle can apply optimizations to dependency resolution performed by the Configuration Cache that aren’t possible for general build logic, so a well-formed build is still likely to perform better.

Configuration-time problems surface earlier

More generally, the Configuration Cache promotes a number of latent configuration-time problems from "silent" to "failure." Build scripts that rely on patterns the Configuration Cache rejects (such as referencing the Project object at execution time, capturing live JVM state in a task field, registering build listeners, and others) will fail at store time, even if the offending code path is rarely exercised at runtime. The full set of rules build logic must follow under the Configuration Cache is enumerated in the requirements.

Security Considerations

The Configuration Cache serializes the configured state of every scheduled task into a cache entry stored under .gradle/configuration-cache in the project directory. This includes any values reachable from task fields — which may contain credentials, tokens, API keys, or other sensitive data if they are held in task state.

Gradle encrypts all Configuration Cache data on disk, both the serialized task graph and the fingerprint, using a machine-specific key stored in GRADLE_USER_HOME. However, anything with access to both the cache directory and the encryption key can read the entries.

System properties and environment variables referenced during the configuration phase are recorded in the fingerprint. If credentials are passed through these mechanisms, their values become part of the stored data. Use lazy wiring, like providers.environmentVariable(), to feed sensitive data to tasks and only obtain the values at execution time.

To reduce the risk of exposing sensitive values through the Configuration Cache:

  • Store credentials in GRADLE_USER_HOME/gradle.properties. The content of this file is not serialized into the cache entry — only the file’s fingerprint is recorded. This is the recommended approach for repository credentials and other secrets.

  • Use the Gradle credentials API for repository authentication. Declaring credential types via the repository credentials API lets Gradle look up values from Gradle properties at build time, keeping them out of your build scripts. See Handling credentials for details.

  • Mark non-essential fields as transient. Java’s transient keyword excludes a field from Configuration Cache serialization. Use this for fields that hold runtime state not needed after deserialization.

  • Restrict access to the .gradle/configuration-cache directory and the encryption key in GRADLE_USER_HOME, especially in shared or CI environments.

  • Use the GRADLE_ENCRYPTION_KEY environment variable in environments where the default keystore location is undesirable, such as when GRADLE_USER_HOME is shared across machines.

For a full discussion of encryption key management, see Handling of Credentials and Secrets.

IDE Support

If you enable and configure the Configuration Cache in your gradle.properties file, it will be automatically enabled when your IDE delegates builds to Gradle. No additional setup is required.

Because gradle.properties is typically checked into source control, enabling the Configuration Cache this way will apply to your entire team. If you prefer to enable it only for your local environment, you can configure it directly in your IDE instead.

Syncing a project in an IDE does not benefit from the Configuration Cache. Only running tasks through the IDE will leverage the cache.

IntelliJ-based IDEs

In IntelliJ IDEA or Android Studio, this can be done in two ways, either globally or per run configuration.

To enable it for the whole build, go to Run > Edit configurations…​. This will open the IntelliJ IDEA or Android Studio dialog to configure Run/Debug configurations. Select Templates > Gradle and add the necessary system properties to the VM options field.

For example, to enable the Configuration Cache, turning problems into warnings, add the following:

-Dorg.gradle.configuration-cache=true -Dorg.gradle.configuration-cache.problems=warn

You can also choose to only enable it for a given run configuration. In this case, leave the Templates > Gradle configuration untouched and edit each run configuration as you see fit.

Using these methods together, you can enable the Configuration Cache globally while disabling it for certain run configurations, or vice versa.

You can use the gradle-idea-ext-plugin to configure IntelliJ run configurations from your build.

This is a good way to enable the Configuration Cache only for the IDE.

Eclipse-based IDEs

In Eclipse-based IDEs, you can enable the Configuration Cache through Buildship, either globally or per run configuration.

To enable it globally:

  1. Go to Preferences > Gradle.

  2. Add the following JVM arguments:

    • -Dorg.gradle.configuration-cache=true

    • -Dorg.gradle.configuration-cache.problems=warn

To enable it for a specific run configuration:

  1. Open Run Configurations…​.

  2. Select the desired configuration.

  3. Navigate to Project Settings, check Override project settings, and add the same system properties as JVM arguments.

Using these methods together, you can enable the Configuration Cache globally while disabling it for certain run configurations, or vice versa.