Monorepo CI/CD: Caching, Affected Builds,
and Real Build Times in 2026

Most "use caching" advice skips the measured deltas. This page shows real build time numbers, the three CI strategies for monorepos, and the failure modes that kill cache hit rates.

If total CI bill is the driver: CI/CD cost calculator | CI/CD cost guide

The Three CI Strategies

1. Rebuild-everything

Run all build, test, lint tasks on every CI run regardless of what changed.

P50 build: grows linearly with package count.

When: Works fine under 5 packages. Becomes painful at 10+.

# GitHub Actions
npx nx run-many -t build test lint
npm run -w packages/* build

2. Affected-only

Use project dependency graph to only run tasks on packages affected by changed files.

P50 build: ~70% reduction vs rebuild-everything on a typical PR.

When: Essential at 10+ packages. Free with Nx and Turborepo.

# Nx affected
npx nx affected -t build test lint --base=origin/main

# Turborepo --filter
npx turbo run build test --filter=...[origin/main]

3. Cache-aware

Add remote cache on top of affected-only. Cache hits are returned in seconds instead of rebuild.

P50 with high cache hit rate: 80-95% reduction vs rebuild-everything.

When: Essential at 30+ packages or when CI run times exceed 10 minutes.

# Nx Cloud
export NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_TOKEN }}
npx nx affected -t build test lint --base=origin/main

# Turbo + Vercel Remote Cache
export TURBO_TOKEN=${{ secrets.VERCEL_TOKEN }}
export TURBO_TEAM=my-team
npx turbo run build test --filter=...[origin/main]

Benchmark Numbers (Cited to Source)

18% CI build time reduction post-migration

Source: developers.dev aggregate of migration reports

Average across teams that migrated from polyrepo to monorepo with caching enabled. Individual results vary widely based on pre-migration CI setup.

19h (mono) vs 2h (poly) median PR cycle time

Source: Faros AI, 320 engineering teams

PR cycle time measures from first commit to merge. Monorepo PRs are typically larger (touch more packages), which drives the 9x delta. Build time and PR size are confounded.

50-90% build time reduction with remote caching at high hit rates

Source: Nx.dev case studies (vendor-sourced)

Vendor-sourced. Cache hit rates vary by codebase structure, PR size, and how well outputs are defined. Best case: stable packages with deterministic builds. Worst case: large packages with environment-sensitive outputs.

Tool-Specific CI Commands

ToolAffected-only commandRemote cacheDistributed builds
Nxnpx nx affected -t buildNx Cloud (env: NX_CLOUD_ACCESS_TOKEN)Yes (Nx Agents)
Turboreponpx turbo run build --filter=...[origin/main]Vercel Remote Cache (env: TURBO_TOKEN)No
Bazelbazel build //... --build_event_stream=...BuildBuddy / EngFlow remote cacheYes (BuildFarm)
Rushrush build --to-except-point <sha>Azure Blob / S3 (custom setup)Partial
Pantspants --changed-since=main build ::Toolchain remote cacheYes (remote execution)

Common CI Failure Modes in Monorepos

Cache poisoning

High

Non-deterministic build writes wrong artifact to cache. Subsequent runs use poisoned artifact. Fix: ensure build outputs are determined only by inputs (no timestamps, random seeds, env vars not hashed).

Untracked dependencies

High

A build task depends on a file not declared in its input set. Changes to that file don't invalidate the cache. Fix: declare all file inputs explicitly in nx.json inputs or turbo.json inputs.

Cache hit rate collapse

Medium

Large packages or packages with many dependents make almost every PR's cache stale. Fix: split large packages into smaller ones; be explicit about outputs.

Partial-clone issues

Medium

git clone --depth=1 in CI causes nx affected to fail (no full history to compare against main). Fix: use fetch-depth: 0 or fetch the base branch explicitly.

Flaky tests polluting cache

Medium

A flaky test passes and caches its result. Next run returns cached 'pass'. Flakiness hidden by cache. Fix: mark known-flaky tests as no-cache; track flakiness in CI observability.

CI/CD FAQ

How do you speed up monorepo builds in CI?+
Three levels: (1) Affected-only: run only tasks affected by changed files (Nx affected, Turbo --filter with --affected). (2) Local caching: cache build outputs by content hash; skip if unchanged. (3) Remote caching: share cache hits across CI workers (Nx Cloud, Vercel Remote Cache). Each level compounds the previous.
What is Nx affected?+
Nx affected is a set of commands that use the project dependency graph to determine which projects were affected by changes since a base commit (e.g., main). nx affected:build only runs build for affected projects and their dependents. nx affected:test only runs tests for affected projects. Combined with caching, this is the primary CI performance lever in Nx monorepos.
Does Turborepo have an equivalent to Nx affected?+
Yes. Turborepo's --filter=...[main] flag uses git diff against a base branch to filter which packages run. Combined with Turbo's pipeline dependency graph, it achieves similar results to Nx affected. Syntax: npx turbo run build --filter=...[origin/main].
What is cache poisoning in a monorepo CI pipeline?+
Cache poisoning happens when a non-deterministic build writes an incorrect artifact to the cache, and subsequent CI runs use the poisoned artifact instead of rebuilding. Common causes: environment variables baked into builds, timestamp-dependent outputs, non-hermetic tests that write to shared state. Bazel's hermetic builds prevent this by design. Nx and Turbo can be poisoned if build outputs depend on environment state that isn't hashed.
Tool comparison →Migration cost →CI/CD cost calculator →