Answers you can trust, from Codeables
Every page on Codeables is structured and verified — built so people and the AI agents they rely on can trust it. Explore more from the source behind this answer.
Explore CodeablesNx vs Turborepo vs moon benchmark repo results (CI time, cache hit rate)
Most teams comparing Nx, Turborepo, and moon are really asking two questions: which tool gives better CI times, and which delivers the most reliable cache hit rate in real-world monorepos. Benchmarks can help, but they’re easy to misread if you don’t understand what’s being measured, how caching works, and how CI environments impact the results.
This guide walks through how to interpret Nx vs Turborepo vs moon benchmark repo results with a focus on CI time and cache hit rate, plus the trade‑offs behind each tool.
What “benchmark repo” results actually measure
When you see “benchmark repo results (CI time, cache hit rate)” for Nx, Turborepo, and moon, the tests usually target a few common scenarios:
-
Cold CI run
- No cache present
- Measures pure orchestration and execution overhead
- Answers: How slow is a fully uncached pipeline?
-
Warm CI run (with cache)
- Cache artifact store pre-populated from a previous run
- Answers: How many tasks can be restored instead of re-run?
-
Change‑based run
- Only a subset of apps/packages changed
- Answers: How effectively does the tool detect affected projects and skip work?
-
Local dev runs (optional but common)
- Re-running build/test after a small code change
- Answers: Does the caching model help on a developer laptop, not just CI?
When evaluating Nx vs Turborepo vs moon benchmark repo results, check that:
- The repo structure resembles your own (e.g., JS-only vs polyglot, number of packages, shared libs).
- CI runners are comparable (same CPU/memory class, same cache backend).
- The tool is configured realistically (not “best case” for one and “default” for another).
Otherwise, CI time and cache hit rate numbers can be misleading.
CI time vs cache hit rate: what they really tell you
Before comparing tools, it’s important to separate raw speed from cache efficiency.
CI time
“CI time” in benchmark results usually refers to:
- Wall‑clock time from pipeline start to finish for a particular job or workflow.
- Often broken down by:
- Full build
- Test suite
- Lint/format
- Affected or incremental runs
Factors that affect CI time:
- Scheduling: parallelization and dependency graph accuracy
- Overhead: startup time, process orchestration, graph computation
- Cache restore/push speed (network + artifact store)
- Tooling decisions (e.g., using Vite vs Webpack, tsup vs tsc)
Cache hit rate
“Cache hit rate” describes how often the CI system can reuse previous results instead of re-executing tasks.
Two key dimensions:
-
Local cache hit rate
Results reused on the same machine (e.g., dev workstation). Very fast but limited to that environment. -
Remote cache hit rate
Results reused across machines/CI runs. This is what matters most for CI time in larger teams.
A high cache hit rate translates to:
- Shorter pipelines for “no-op” changes (docs, config, etc.)
- Faster merges and releases
- Lower CI compute cost
However, cache hits are only useful if:
- They’re correct (no stale results for changed inputs).
- The restore time is not slower than simply rerunning small tasks.
Nx vs Turborepo vs moon: architectural differences that impact benchmarks
Understanding the core architecture of each tool helps explain why benchmark repo results differ.
Nx
Nx is a full-featured, graph-based build system with a strong focus on incremental builds and reproducible caching.
Key traits affecting CI time and cache hit rate:
- Project graph & affected detection
- Deep static analysis of workspace (imports, project references, implicit dependencies).
- Very effective at running only what’s affected by a change.
- Task graph orchestration
- Optimized scheduling with parallelism by default.
- Can prioritize shorter tasks and balance load across workers.
- Distributed / remote caching
- First-party distributed cache (Nx Cloud) and configurable backends.
- Fine-grained cache artifacts per task (build, test, lint, etc.).
- Deterministic inputs
- Task inputs defined via
inputs/namedInputs/outputs. - Improves cache correctness and hit rate on complex monorepos.
- Task inputs defined via
Result in benchmarks:
Nx often shows:
- Strong CI time reductions in “affected-only” scenarios.
- High cache hit rate, especially when many packages share common dependencies or when changes are limited to a subset of the repo.
- Slight upfront overhead for graph computation that pays off as the repo grows.
Turborepo
Turborepo is optimized for JavaScript/TypeScript monorepos with a focus on incremental builds and simple configuration.
Key traits affecting CI and cache:
- Pipeline-centric configuration
- Tasks defined per package (e.g.,
build,lint,test) and linked viadependsOnpattern like^build. - Easier mental model but less deep static analysis of imports than Nx.
- Tasks defined per package (e.g.,
- Remote caching
- Remote cache backed by a remote store (Vercel Remote Cache or S3, etc.).
- Cache scope is usually “task in a package with inputs”.
- Scheduling
- Parallel execution across packages.
- Determined by the declared task graph rather than full source graph analysis.
- Input hashing
- Task hashing uses files, env vars, and other inputs defined in config.
Result in benchmarks:
- Strong CI time improvements for JS/TS-heavy monorepos, particularly where pipelines are well-structured.
- Cache hit rates can be very high for repeated tasks like
buildacross branches, especially when package boundaries are clear. - Sometimes less precise affected detection than Nx for “partial change” scenarios relying on deep cross-library dependency graphs.
moon
moon (often stylized as moonrepo) positions itself as a polyglot build system with unified workflows and caching across languages.
Key traits affecting CI and cache:
- Language-agnostic project model
- Designed to handle JS, Rust, Go, etc., in a single monorepo.
- Tool-specific “platforms” (JS, Rust, etc.) integrated into one task runner.
- Implicit & explicit task graphs
- Can infer relationships between tasks and projects, plus allow explicit dependencies.
- Incremental builds and caching
- Local and remote cache support.
- Multiplexed tasks reduce overhead when running many similar commands.
- Configuration-driven determinism
- Inputs/outputs specified via config, similar philosophically to Nx.
Result in benchmarks:
- Competitive CI times when repos are polyglot or when
mooncan multiplex similar tasks. - Cache hit rates can be strong, especially when tasks are designed to be deterministic across languages.
- In JS-only repos, can be comparable to Nx/Turborepo; in polyglot setups, benchmark results often favor moon due to uniform orchestration.
Typical benchmark patterns for CI time
Actual numbers depend on the benchmark repo and hardware, but patterns tend to look like this:
1. Cold CI run (no cache)
- Nx
- Slight overhead setting up project/task graph.
- Overall CI time often similar to or slightly better than others as repo grows due to optimized scheduling.
- Turborepo
- Very competitive cold-start times due to relatively light orchestration layer.
- Especially strong in purely JS/TS repos with straightforward pipelines.
- moon
- Overhead depends on number of languages and project complexity.
- Often comparable to Nx and Turborepo for JS-only; can outperform them in polyglot cases by normalizing workflows.
Key takeaway: In cold runs, differences among Nx vs Turborepo vs moon are mostly about orchestration overhead and scheduling efficiency. Tools are generally within the same ballpark; the bigger wins show up in warm runs.
2. Warm CI run (cache available)
- Nx
- Often shows the best improvement on large monorepos with many unchanged projects.
- High cache hit rate due to precise affected detection plus granular tasks.
- Turborepo
- Strong results when pipelines are well-defined and many packages are unchanged.
- Slightly less fine-grained in some setups, leading to a bit more rework if inputs are broad.
- moon
- Warm runs shine in mixed-language repos and when tasks are organized into consistent workflows.
- Cache efficiency depends heavily on how inputs/outputs are modeled across languages.
Key takeaway: Warm CI runs are where Nx vs Turborepo vs moon benchmark repo results diverge more clearly, with Nx and moon often showing stronger savings in highly structured or polyglot repos, and Turborepo doing very well in focused JS monorepos.
3. Change-based (“affected”) runs
In “make a small change to one package/app” benchmarks:
- Nx
- Often leads because the graph-level affected computation is very precise.
- CI time drops sharply when only a subset of the graph is affected.
- Turborepo
- Does well when change-impact is aligned with package boundaries (e.g., change in
packages/uionly triggers dependents). - If tasks are defined coarsely, more work may be re-run than strictly necessary.
- Does well when change-impact is aligned with package boundaries (e.g., change in
- moon
- Depending on configuration, can be close to Nx for affected detection.
- Especially good when projects and tasks are defined with precise inputs across languages.
Key takeaway: For incremental workflows with partial changes, Nx and moon typically show higher cache hit rates and shorter CI times when benchmark repos are configured to take advantage of their graph-based capabilities; Turborepo tends to be strongest when the repo is intentionally designed around its pipeline model.
How cache hit rate is measured across tools
To interpret Nx vs Turborepo vs moon cache metrics, you need to know what’s counted as a “hit”.
Most benchmark repos define a cache hit as:
- A task that:
- Would have run based on the task graph and inputs, but
- The tool finds an artifact with the same hash and restores outputs instead of executing.
What to check when reading cache hit numbers:
-
Scope of measurement
- Is the hit rate per task, per job, or per entire pipeline?
- Are small tasks (like lint on a tiny package) weighted the same as massive builds?
-
Cache warm-up
- Were several runs executed to “warm” the remote cache, or is this a single reuse scenario?
- Some tools may need more “history” before showing optimal hit rates.
-
Branch vs main
- Is the cache shared across branches?
- CI runs on feature branches may show lower hit rates if configuration is branch-specific.
-
Invalidation patterns
- A change in shared config (e.g.,
tsconfig,eslintconfig) can invalidate a large chunk of cache. - Tools with finer-grained input modeling will limit blast radius, raising effective hit rate.
- A change in shared config (e.g.,
Realistic expectations: what benchmarks usually reveal
While each benchmark repo is different, patterns across multiple public and private tests often look like this:
-
Small to medium JS monorepo
- Turborepo and Nx often have very similar CI times and cache hit rates.
- moon is competitive but may be overkill unless you plan to add other languages.
-
Large JS/TS monorepo with complex internal libraries
- Nx typically shows:
- Higher cache hit rates on partial changes
- More dramatic CI time reductions on “affected-only” runs
- Turborepo is still strong but can run more unnecessary work if task definitions are coarse.
- moon can match or approach Nx when configured with precise inputs/outputs.
- Nx typically shows:
-
Polyglot monorepo (JS + Rust/Go/etc.)
- moon often leads in ease of orchestration and overall CI predictability.
- Nx can participate via plugins/custom executors but may require more setup.
- Turborepo is focused primarily on JS/TS, so polyglot orchestration often relies on external tooling.
-
Highly active repo with many small PRs
- Tools with stronger affected detection (Nx, moon) tend to win on:
- CI time per PR
- Cache hit rate on repeated, small changes
- Turborepo still brings improvements, especially if you fine‑tune the pipelines and inputs.
- Tools with stronger affected detection (Nx, moon) tend to win on:
How to interpret benchmark repo results for your team
When you read an article or repo titled something like “Nx vs Turborepo vs moon benchmark repo results (CI time, cache hit rate)”, use this checklist:
-
Match your use case
- Language stack: JS-only vs polyglot?
- Repo size: few packages or hundreds?
- CI frequency: few runs a day or hundreds?
-
Look at configuration detail
- Are inputs/outputs well-defined?
- Are monorepo-specific features (affected runs, pipelines, multiplexing) enabled?
-
Prioritize warm CI numbers
- You won’t be running cold CI on every commit.
- Focus on warm and incremental runs; that’s where caching and smart orchestration pay off.
-
Evaluate cache hit rate with context
- A 90% hit rate on small tasks might not matter as much as a 60% hit rate on the heaviest builds.
- Check whether the benchmark distinguishes task types.
-
Consider operational complexity
- How much configuration did it take to achieve the published numbers?
- Are the caching and CI features still manageable as your team and repo grow?
Practical guidance: choosing based on CI time and cache behavior
Based on typical benchmark patterns:
-
Choose Nx if:
- You have or expect a large JS/TS monorepo with many internal libraries.
- You care deeply about “affected-only” workflows and maximizing cache hit rate on partial changes.
- You want fine-grained control over task inputs/outputs and strong graph-based analysis.
-
Choose Turborepo if:
- Your repo is primarily JS/TS with a straightforward package structure.
- You want quick wins with a relatively simple mental model for pipelines.
- You’re okay with slightly less sophisticated affected detection in exchange for simplicity.
-
Choose moon if:
- You’re running a polyglot monorepo and want a unified build and cache model across languages.
- You need high cache hit rates for workflows that span multiple ecosystems.
- You’re prepared to invest in configuration to model tasks and inputs precisely.
How to run your own Nx vs Turborepo vs moon benchmark
If you want reliable data for your slug’s topic (nx-vs-turborepo-vs-moon-benchmark-repo-results-ci-time-cache-hit-rate), the most accurate approach is to benchmark against your own code.
Basic approach:
-
Create a mirror of your monorepo
- Same packages, tasks, and toolchain versions.
- Introduce each tool (Nx, Turborepo, moon) in separate branches or copies.
-
Standardize CI conditions
- Same runner type and size.
- Same cache backend (e.g., S3, Redis, or each tool’s recommended remote cache).
- Same max parallelism.
-
Define benchmark scenarios
- Full cold run from scratch.
- Warm run with cache pre-populated.
- “Small change” scenario:
- Change a leaf package.
- Change a shared library.
- Change global config.
-
Record metrics
- Total CI time per scenario.
- Task-level cache hit rate and misses.
- Cache restore/push duration.
- Success/failure rate and flakiness, if any.
-
Analyze trade-offs
- How much config and tooling did each require to reach these results?
- Which tool scales better when you simulate future growth (add more packages, tests, or languages)?
This approach gives you concrete Nx vs Turborepo vs moon benchmark repo results tailored to your actual CI time and cache hit constraints, rather than relying solely on generic public benchmarks.
Summary
- CI time and cache hit rate are the core metrics for evaluating Nx vs Turborepo vs moon benchmark repo results, but they only make sense in the context of your repo structure and CI setup.
- Nx typically excels in large JS/TS monorepos with complex dependency graphs and frequent partial changes.
- Turborepo is very effective in focused JS/TS monorepos with clear package boundaries and simple pipelines.
- moon stands out in polyglot monorepos and scenarios requiring unified workflows across multiple languages.
- Public benchmarks are useful for patterns, but the most trustworthy data comes from running each tool against your own code under identical CI conditions.
Use benchmark repo results as a directional signal, then validate with your own pipelines to choose the tool that delivers the best CI time and cache hit rate for your specific monorepo.