3.6.6 Cache Warm/Cold Considerations
recommendedCI cache in Bazel is not one on/off switch. A run can be cold in the Bazel server, warm in a machine-local cache, and warm again in a shared remote cache at the same time. This article explains how to read those layers and choose the everyday reuse policy. Continue to 5.7 Cache & Execution for forensic comparison and 6.2 Shared Remote Cache for production cache operations.1,2
Cache Temperature Has Layers
3.6.2 Bazel Server Lifecycle in CI already established that a fresh runner loses the warm Bazel server. The important addition here is that losing the server does not mean every cache is empty. Bazel reuse lives in several layers with different scope and lifetime.1,3
- Skyframe / analysis cache lives in the long-running Bazel server. When that server dies, loading and analysis go cold.1,2
- Repository cache survives across workspaces on the same machine and avoids re-downloading external archives.1
- Output tree / local action cache survives a server restart as long as the same output base is still available, so Bazel can often skip re-executing actions even without
--disk_cache.1 - Disk cache (
--disk_cache) moves action-result reuse outside one output base, which is why it helps when CI restores cache directories onto a later run or a different output base.3 - Remote cache shares action results across workers, developers, and branches when the action keys still match.3,4
The ci-cache-baseline README cache-layer narrative walks through these layers in one place: separate Bazelisk, repository, and disk cache directories, plus commented --remote_cache placeholders that keep the trust decisions visible without enabling an unconfigured backend.
That is why a CI build can be cold and warm simultaneously: cold in analysis, warm in execution. A shared cache shortens the part after Bazel has already reconstructed the graph, but it does not remove the cost of rebuilding that graph on every ephemeral runner. Warm worker pools, stateful containers, and snapshotting only become central later in 6.5 Bazel CI at Scale, once this distinction starts dominating your CI time budget.2,5
One practical symptom is a build that shows many remote hits but still feels slow. That usually points to cold analysis rather than a broken cache server. The timing profile's skyframe evaluator sections make that visible, so "high cache hit rate" is not enough evidence that the run was truly warm.6
Shared Cache Needs Trust Boundaries
Shared cache pays off when one build can amortize work for another: branch switches, multiple checkouts of the same repo, stateless CI drones, and developers consuming artifacts CI already produced.3,4 This is the good version of "warm cache" in CI: the worker itself may be fresh, but the organization has already paid for most of the work.
However, shared cache is only safe when the action key captures everything that matters. Bazel hashes declared inputs, tools, command line, and relevant environment, but wrong hits still happen when reality leaks in through host-installed tools, platform details, or other inputs Bazel cannot see directly.1,3 A robust practice is to add an extra cache key for platform facts Bazel does not reliably track on its own, instead of pretending all workers are interchangeable.7
The same trust boundary shows up in write policy. A solid default is: CI writes the shared cache, developers read from it. Restricting writes this way is the recommended default, because local environment quirks are exactly how cache poisoning spreads.3 The mechanics of enabling the caches live in 3.6.4 Disk Cache in CI and 3.6.5 Simple Remote Cache Setup. The deeper AC/CAS model and poisoning mitigations live in 6.2 Shared Remote Cache.
If your CI system adds its own cache namespaces around Bazel, use them for facts Bazel cannot safely infer itself. One example is platform identifiers Bazel does not track reliably. The opposite pressure also exists, where keeping artifacts reusable across checkouts is exactly what makes later builds on another branch feel warm.4,7
Keep The Fast Path Fast
Routine CI should preserve incrementality. The guidance is blunt: CI should not clean before every build or test run, because Bazel does not need clean builds for correctness.8 If correctness depends on cleaning first, the problem is usually not "stale cache" but a violation of 2.3 Hermeticity & Sandboxing: undeclared inputs, host tools, timestamps, network access, or other non-determinism escaping the declared build context.3,8
That does not make cold builds useless. It changes their role: a deliberately cold run is an audit path, not the default lane. Once an audit diverges from the warm result, stop treating this as cache-policy tuning and hand the investigation to execution-log comparison in 5.7 Cache & Execution or cache-poisoning operations in 6.2.4 Wrong Cache Result Diagnostics.3,9
Keep one explicit cold-path audit outside the normal PR lane: rerun from a clean output base and disable remote-cache reads (for example with --noremote_accept_cached) when a cached success looks suspicious. Its job is only to tell you that warm and cold results differ. Execution-log comparison in 5.7 Cache & Execution and cache-poisoning operations in 6.2.4 Wrong Cache Result Diagnostics own finding and repairing the cause.3,9
The strategic takeaway is simple: use warm cache as the default acceleration path, but keep one colder verification path available when correctness is in doubt. If the pain is repeated cold analysis, the next topic is 6.5 Bazel CI at Scale. If the pain is poisoned shared artifacts, the next topic is 6.2.4 Wrong Cache Result Diagnostics. If the pain is low-quality reuse or falling hit rates, the next topic is 6.2.4 Wrong Cache Result Diagnostics.
"Warm cache" in CI really means three different things: warm Bazel server state, warm machine-local caches, and warm shared action results. Remote cache helps the third layer. It does not replace the first. Day-to-day CI should preserve incrementality, keep shared-cache writes trusted, and use cold builds deliberately as audits when you need to surface hidden correctness problems.
Check your understanding · 3 questions
1.Why does the Bazel FAQ explicitly recommend that CI should NOT run bazel clean before every build?
Select one answer
2.True or false about cache correctness and hygiene:
Choose True or False for each sentence
3.Match each cache layer to what it provides in a typical CI run:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
Footnotes
-
The Many Caches of Bazel — Skyframe, repository cache, output-tree cache, disk cache, and what survives a server restart ↩1 ↩2 ↩3 ↩4 ↩5 ↩6
-
Bazel Caching Explained (4-part series) — warm JVM state in CI, why it is easy to miss, and why stateful workers can save minutes ↩1 ↩2 ↩3
-
Remote Caching — disk cache across branches, CI-vs-developer write policy, and the known issues that break safe cache sharing ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Setting up a shared build cache using Bazel — shared cache reuse across branches, developers, and stateless CI drones ↩1 ↩2 ↩3
-
Meet BuildBuddy Workflows — warm hosted Bazel processes after remote cache and network bottlenecks are removed ↩
-
Why is my Bazel build so slow? — cold analysis diagnosis via timing profiles and
skyframe evaluatorsections ↩ -
How Bazel built its CI system on top of Buildkite — extra cache-key dimensions for platform facts Bazel does not track itself ↩1 ↩2
-
FAQ — CI should not clean before every run, plus the hermeticity caveats that still threaten correctness ↩1 ↩2
-
Bazel and action (non-) determinism — why clean builds are not the default, and when forcing a cold path helps surface hidden non-determinism ↩1 ↩2