The complete guide

Build cache guide: local, CI, and remote build caching explained

A build cache stores completed task outputs so identical work does not run twice. This guide explains local, CI, and remote build caching, content-hashed cache keys, which tools support shared caching (Nx, Lerna, Turborepo, Bazel, Gradle), how to operate one safely, and how to measure whether it is paying off.

What is a remote cache?

A remote cache (also called a remote build cache) is shared storage for the outputs of build tasks, keyed by a hash of everything that went into producing them. When your build tool is about to run a task - compile a package, run a test suite, lint a project - it first computes that hash and asks the cache: has anyone, on any machine, already run this exact work? If yes, it downloads the stored outputs and replays them in milliseconds. If no, it runs the task and uploads the result so the next machine gets the hit.

The core idea is simple: the same inputs always produce the same outputs, so identical work should only ever be done once - across the whole team, not just per machine. A monorepo with fifty projects rarely changes more than a few of them per commit; everything downstream of unchanged code is, by definition, work someone has already done. A remote cache turns that observation into saved minutes on every build.

The terms remote caching, remote build cache, and distributed build cache all describe the same mechanism. Nx calls it the remote cache, Lerna delegates it to Nx, Turborepo calls it Remote Caching, Bazel calls it a remote cache, and Gradle calls it the remote build cache - the protocol details differ, the concept does not.

How remote caching works

Every remote cache implementation follows the same four-step loop:

  • 1. Hash the inputs. The build tool computes a cache key from the complete set of inputs to a task: the source files it reads, the resolved dependencies, the command and its flags, the toolchain version, and any environment variables that affect the output. Change any input and the key changes.
  • 2. Look up the key. The tool sends the key to the cache server - typically a GET over HTTPS with an auth token. A hit returns the stored artifact; a miss returns a 404.
  • 3. Restore or run. On a hit, the tool unpacks the artifact - output files, plus the recorded terminal output so the replayed task looks exactly like a real run. On a miss, the task simply executes as it would without any cache.
  • 4. Upload the result. After a miss, the freshly built outputs are uploaded under the computed key, so every subsequent machine with the same inputs gets a hit.

Because the key is a content hash of the inputs, there is no invalidation logic to get wrong: stale entries are never served, they are simply never looked up again, because changed inputs produce a different key. This is what separates a build cache from the hard cache-invalidation problems of application caching - correctness falls out of the keying scheme. (What belongs in the key, and how under- and over-keying break caches, is covered in depth in What makes a good build cache.)

Local cache vs remote cache

Most build tools with caching ship a local cache first: results are stored on the machine that produced them, so re-running an unchanged task on the same machine is instant. That alone is valuable, but its blind spot is everything that is not your machine:

  • CI runners usually start clean. An ephemeral runner has an empty local cache on every job, so it rebuilds the world - even when the previous pipeline built the exact same code minutes earlier.
  • Teammates repeat each other's work. When a colleague builds a branch you then check out, your machine has none of their results.
  • Fresh clones and new machines start from zero. Onboarding builds are always cold.

A remote cache removes the blind spot by making the cache a shared, network-accessible service. The first build of any given input state is done once, by whoever happens to run it first - a developer or a CI job - and everyone else replays it. The biggest single win is usually CI: a warm remote cache means a pull-request pipeline only truly executes the tasks affected by the diff.

Remote cache vs CI cache vs artifact storage

"Cache" is an overloaded word in CI, and three different mechanisms get conflated. They solve different problems and are usually complementary, not alternatives:

Remote build cache compared with CI dependency caches and artifact storage.
MechanismWhat it storesKeyed byScope
Remote build cachePer-task outputs (compiled files, test results, terminal output)Fine-grained content hash of each task's inputsEvery machine: CI and developer laptops
CI cache (e.g. actions/cache)Whole directories (node_modules, ~/.gradle, build dirs)Coarse keys you write by hand (often a lockfile hash)The CI provider only; developers never benefit
Artifact storageFinal deliverables (binaries, images, reports) for humans and deploysA name and a run/version, not content hashesConsumers of the build, not the build itself

A CI cache like GitHub Actions' actions/cache is great at what it does - skipping npm install by restoring node_modules - but it cannot skip the build itself: it has no notion of tasks, no dependency graph, and its coarse keys either miss too often or restore stale directories. A remote build cache sits at a different layer: it skips the compile, test, and lint work task by task, and the same cache serves CI and every developer machine. Most teams end up running both - the CI cache for dependency installs, the remote cache for the actual build graph. For a broader look at where CI time actually goes, see How to improve your CI times. If misses are higher than expected, follow the build cache hit-rate troubleshooting guide.

Which build tools support remote caching

Nx

Nx computes a hash per task from its inputs (source files, dependency outputs, runtime values) and caches the declared outputs. Remote caching uses the official self-hosted remote cache API: point Nx at any conforming server with two environment variables and every nx build, nx test, or nx affected run consults it automatically:

NX_SELF_HOSTED_REMOTE_CACHE_SERVER=https://remote.cachely.dev
NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN=<your token>

Getting the hit rate up is mostly about tuning what goes into the hash - see Tuning Nx cache inputs and outputs. For the full Nx-specific picture - the task hash, setup, Nx Cloud vs self-hosted vs managed, and troubleshooting - see the Nx remote cache guide.

If the managed category is the likely fit, the managed remote build cache buyer guide covers protocol compatibility, security, operations, and provider evaluation in depth.

Lerna

Modern Lerna uses Nx for task scheduling and caching. A lerna run therefore honors the same NX_SELF_HOSTED_REMOTE_CACHE_SERVER and NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN variables as a direct Nx command and sends the same Nx artifact format. Mark the relevant targets cacheable in nx.json; no Lerna-specific cache server or protocol is required.

Turborepo

Turborepo speaks the Vercel Remote Cache protocol. Any server implementing it works, configured with TURBO_API, TURBO_TOKEN, and TURBO_TEAM. Artifact signing (signature: true) adds an HMAC so clients verify artifacts were produced by someone holding the signing key. The full setup - hosted, self-hosted, and managed - is covered in the Turborepo remote cache guide.

Bazel

Bazel pioneered remote caching at scale: content-addressed storage over gRPC or HTTP (--remote_cache), plus optional remote execution. Its hermetic, sandboxed action model is the strictest interpretation of the same idea - every action declares its full input set, so keys are precise and hits are trustworthy. The action cache, CAS, HTTP setup, CI security, and backend choices are covered in the Bazel remote cache guide.

Gradle

Gradle's build cache (--build-cache with a remote HTTP backend) caches task outputs the same way. The recommended rollout is a trusted CI build that pushes entries while developer and pull-request builds read them. See the Gradle remote build cache guide for HttpBuildCache setup, FROM-CACHE verification, and troubleshooting.

sccache and others

sccache wraps Rust/C/C++ compilers and stores object files in shared storage. Pants, Buck2, and moon all ship the same mechanism. The pattern is universal enough that the question is rarely "does my tool support it" but "where should the cache live and who operates it".

Security: cache poisoning and read-only tokens

A remote cache is a supply-chain component: whatever is in it gets executed, linked, or shipped by trusted builds. The attack that matters is cache poisoning - an attacker with write access plants a malicious artifact under a key that a trusted build will later look up. For Nx this is not theoretical: CVE-2025-36852 (CREEP) showed that when untrusted pull-request builds share write credentials with trusted builds - the default in most DIY bucket setups - a fork PR can poison the artifact that the main branch build then consumes.

Two properties close the attack, and they are worth demanding from any remote cache setup:

  • Read-only tokens for untrusted builds. PR and fork pipelines get tokens that can read (so they stay fast) but never write - enforced server-side, not by convention.
  • Immutable, content-addressed artifacts. Once a key exists, writes to it are rejected (409), so a known-good artifact can never be silently replaced.

The full attack walkthrough is in Cache poisoning and CVE-2025-36852, and the trust model Cachely enforces is documented on the security page. One more reassuring property: a remote cache does not need repository access. Your build tool hashes inputs locally and sends only the task outputs you configure, though those outputs can contain source-derived material.

Measuring the impact

Two numbers tell you whether a remote cache is paying off:

  • Hit rate - the share of task executions served from cache. Healthy monorepos see high rates because most commits touch few projects. A low hit rate usually means over-keying: something volatile (a timestamp, an absolute path, an always-changing env var) is leaking into the hash.
  • Time saved - each task's measured cold run time multiplied by how often it was a hit. Sum it and you get the CI minutes (and, at your runner rates, the dollars) the cache returned. Teams with warm caches routinely cut 30-70% off CI wall time.

Whatever cache you run, instrument it - a cache nobody measures quietly decays as inputs drift. The economics of measuring (and what typical savings look like) are worked through in How much can a managed remote cache reduce CI build times?. For a quick what-if model, try the CI savings calculator.

Self-hosted vs managed remote cache

Once you want a remote cache, there are three ways to get one:

  • The build tool vendor's platform (Nx Cloud, Vercel). Caching bundled with a broader platform - distributed execution, runners, analytics - typically priced per seat or usage.
  • Self-hosted / DIY. An open-source cache server or an object-storage bucket with an adapter. Full control, but you own hosting, scaling, token security, immutability enforcement, monitoring, and upkeep - and DIY bucket setups are exactly where CVE-2025-36852 lived.
  • A managed remote cache. A service that implements the tool's open cache protocol and operates the storage, auth, and safety properties for you.

Cachely is in the third category: a managed implementation of the Nx self-hosted remote cache API, the Turborepo remote cache protocol, the Gradle HTTP Build Cache protocol, and the Bazel HTTP remote cache protocol, with read-only tokens and immutable artifacts enforced at the API, hit-rate and ROI reporting built in, and flat pricing with no per-seat fees. The remote build cache comparison lay the options side by side - including vs Nx Cloud, vs a self-hosted Nx cache, and vs Vercel Remote Cache - and the honest answer is that each is right for someone. If you want the cache without the operational load, setup takes about five minutes: the docs walk through it.

Related guides

Try a remote cache on your monorepo
Free for developers. Two environment variables and your next build starts sharing cache.
Start freeSee pricing
FAQ

Remote cache: frequently asked questions

What is a remote cache?
A remote cache is shared storage for build outputs, keyed by a hash of everything that went into producing them. When a task runs with inputs someone has already built - on any machine - the build tool downloads the stored result instead of recomputing it. It turns "someone on the team already ran this" into "nobody has to run it again".
How is a remote cache different from a local build cache?
A local cache only helps the machine that produced it: your laptop reuses your own previous builds. A remote cache shares those results across every machine - teammates, CI runners, ephemeral containers - so a task built once anywhere is a cache hit everywhere. CI runners, which usually start from a clean slate, benefit the most.
Is a remote cache the same as a CI cache like actions/cache?
No. A CI cache (like GitHub Actions cache) restores whole directories keyed by coarse keys such as a lockfile hash, and its scope is the pipeline. A remote build cache operates per task with fine-grained content-hashed keys, understands the dependency graph, and works identically in CI and on developer machines.
Which build tools support remote caching?
Nx (self-hosted remote cache API), Turborepo (the Vercel Remote Cache protocol), Bazel (remote caching via gRPC/HTTP), Gradle (build cache), sccache for Rust/C++, and others. The concept is the same everywhere: content-hashed task outputs stored in shared storage.
Is remote caching safe? What about cache poisoning?
It is safe when writes are controlled. The known risk is cache poisoning (for Nx, CVE-2025-36852 / CREEP): if an untrusted build - like a fork pull request - can write to the cache, it can plant a malicious artifact that trusted builds later execute. The fix is read-only tokens for untrusted builds and immutable, content-addressed artifacts so an existing entry can never be overwritten.
What happens on a cache miss?
The build tool just runs the task locally, exactly as it would without a cache, and uploads the fresh result for the next machine. A miss costs nothing beyond the build you were already going to do - a remote cache is an optimization, never a dependency.
How much time does a remote cache actually save?
It depends on your hit rate and how expensive your tasks are. Teams with warm caches routinely see 30-70% shorter CI runs, because most commits only change a few projects and everything downstream of unchanged code is served from cache. Measure it: saved time is roughly each task's cold run time multiplied by how often it was a hit.
Should I self-host a remote cache or use a managed service?
Self-hosting gives you full control but makes you responsible for hosting, scaling, token security, immutability enforcement, and upkeep. A managed service runs the same protocol without the operational load. Small teams and teams without platform engineers usually come out ahead with managed; regulated environments sometimes need self-hosted.