Last verified April 2026 · 8 min read
CI cost in a monorepo: Turborepo vs Nx vs Bazel
Without affected-only logic, every PR in a 10-package monorepo runs CI for every package. That is 10x the CI cost of the same codebase in separate repositories. Monorepo CI cost is quadratic by default and linear with the right tooling.
Strategy A: DIY only-changed-packages
The zero-vendor approach using git diff and workflow paths filters. Works well for small monorepos (under 10 packages) where the dependency graph is simple enough to manage manually.
# Detect changed packages with git diff
- id: changes
uses: dorny/paths-filter@v3
with:
filters: |
api:
- 'packages/api/**'
web:
- 'packages/web/**'
# Run only affected jobs
jobs:
api-tests:
if: needs.changes.outputs.api == 'true'
# ...
web-tests:
if: needs.changes.outputs.web == 'true'
# ...Limitation: does not handle transitive dependencies. If package A depends on package B and you change B, this approach does not know to re-run A's tests without explicit configuration.
Strategy B: Turborepo
Turborepo understands your package dependency graph and runs only the tasks affected by a change, including transitive dependencies. Remote cache stores task outputs: on a cache hit, the task is skipped entirely and the output replayed.
# Run only affected packages since origin/main
- run: npx turbo run build test --filter='...[origin/main]'
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: your-orgPricing: Vercel hosts Turborepo remote cache free for small teams. Self-host on S3/R2 with turborepo-remote-cache package for free. No per-minute charge from Turborepo itself.
Cost impact: 30-60% CI time reduction on a typical 10-package monorepo with a warm remote cache.
Strategy C: Nx Cloud
Nx provides nx affected commands that scope tasks to changed packages, plus Nx Cloud remote cache and distributed task execution.
Nx Cloud pricing (2026): Free tier: 500 CI pipeline hours/month. Pro: $300/month unlimited. Enterprise: custom pricing.
For large monorepos, Nx Cloud's distributed execution (running individual tasks across multiple agents) can reduce wall-time by 60-80% independently of the cost reduction from running only affected tasks.
Strategy D: Bazel
Bazel is the maximum-correctness option. It builds a precise dependency graph at the file level (not package level), stores all outputs in a content-addressed remote cache, and only rebuilds targets whose inputs have changed.
At scale (100+ engineers, deep C++/Java/Python polyglot codebases), Bazel's precision pays dividends that no other tool matches. Dropbox's published migration showed 10x build speed improvement and ~50% CI compute reduction at their scale. See case studies.
The honest counterpoint: Bazel setup cost is high. Plan 2-6 months of migration for a large existing codebase. Maintenance requires dedicated build-engineering capacity. Below 100 engineers, Turborepo or Nx almost always gives better ROI per engineering hour invested.
Side-by-side cost table
| Strategy | CI minutes/mo | CI cost/mo | Tooling cost/mo | Total | Setup effort |
|---|---|---|---|---|---|
| No strategy (all packages, every PR) | 45,000 | $360 | $0 | $360 | None |
| DIY paths filter | 27,000 | $216 | $0 | $216 | 1-2 days |
| Turborepo + Vercel remote cache | 18,000 | $144 | $0 (free tier) | $144 | 2-3 days |
| Nx Cloud Pro | 14,000 | $112 | $300 | $412 | 3-5 days |
| Bazel + remote cache | 8,000 | $64 | $0 + 0.5 FTE | $64 + ~$5k/mo FTE | 2-6 months |
Scenario: 25-dev team, 10-package monorepo, 50 PRs/day, GH Actions hosted Linux. April 2026 rates.