vx vs Nx
Both tools run the tasks of a JavaScript monorepo and cache their results. This page puts them side by side on one benchmark, feature by feature, and says when Nx is the better choice. The design choices behind both are on vx, Turborepo, Nx, Bazel.
The numbers
Section titled “The numbers”The table
| 1,090 packages, 3,270 tasks | vx | Nx |
|---|---|---|
| Cold build: total time | 3 min 40 s | 3 min 49 s (vx 4% faster) |
| Cold build: CPU burned | 17 s | 52 s (vx 3× faster) |
| Fully cached run (restored) | 650 ms | 6.25 s (vx 9.6× faster) |
| Fully cached run (up-to-date) | 393 ms | 6.45 s (vx 16× faster) |
| Secondary: time the runner adds to a cold build | 2 s | 11 s |
vx N% or N× faster: that tool takes N% longer or N times as long as vx.
Benchmark workload: a synthetic monorepo of 1,090 packages and 3,270 tasks in 100 dependency layers, every build and test taking 1 s; real repos with uneven task times will differ. Run 2026-10-04 on linux x64, 4 cores: vx from source, Turborepo 2.11.7, Nx 23.2.1, Vite Task (vite-plus) 1.0.0.
Same graph, commands and concurrency, each tool in its own native config: how it is measured.
Every Nx task is an nx:run-commands target running the same command vx
runs, with Nx’s daemon off as in CI.
Feature by feature
Section titled “Feature by feature”| vx | Nx | |
|---|---|---|
| Config | TypeScript, evaluated into the cache key | JSON (project.json, nx.json), plus inferred targets |
| Daemon | None; nothing boots per task | On by default locally |
| Outputs on run and restore | Wiped first: no stale file survives a hit | Additive |
| Task sandbox | Free and local, opt-in per task (Linux, macOS) | An Nx Cloud feature |
| Remote cache | Any wire through a plugin: Bazel REAPI, Turbo’s, Nx’s self-hosted spec | Nx Cloud or a plugin |
| Flaky task detection | Local: the same key failed after it passed | Nx Cloud |
| Why a task missed | vx why diffs two keys: the file, env var or upstream that moved | Shows hashes, not the diff |
| Tasks on other machines | @vzn/vx-reapi over Bazel’s REAPI, on servers you run or rent | Nx Agents on Nx Cloud |
| Plugins | Every pipeline stage, 14 hooks; core applies none | Inferred tasks, graph data, generators, executors |
| Non-JS projects | No | Gradle, Maven and .NET |
One task, many modes (build:prod) | No: a TypeScript function per mode, two tasks | configurations and -c |
| Terminal output | Streamed blocks you can scroll, copy and pipe | Terminal UI |
| Windows | WSL2 | Native |
| Install | One binary; no Bun, Node optional | npm and Node |
Every row is held in vx vs Turborepo vs Nx, which links each claim to its source.
Free on vx, Nx Cloud on Nx
Section titled “Free on vx, Nx Cloud on Nx”Nx offers these through Nx Cloud. With vx they run on your machine, at no cost:
- The sandbox. Opt a task in, and a workspace file it did not declare is out of its reach. A violation fails the task, and a failed task is never cached. Nx sandboxes tasks on Nx Cloud (task sandboxing).
- Flaky detection. vx flags a task whose same key failed after it passed, from its local run history.
- Run history.
vx lastreplays any recorded run;vx infoandvx whyread the same local data.
A shared cache a pull request cannot poison
Section titled “A shared cache a pull request cannot poison”A pull request that can write the cache your default branch reads can plant bytes that main later replays. That attack is CREEP (CVE-2025-36852); Nx’s advisory names its bucket-based self-hosted cache packages as affected.
vx gives each run a cache scope. A pull request reads the trusted keys and
writes only its own, so it never writes what main reads. On GitHub Actions,
github() from @vzn/vx-ci sets the scope from the ref. The scope is the
client half: the real boundary is a token your cache server limits, so give
pull request jobs one that cannot write the trusted keys
(security model).
Where Nx wins
Section titled “Where Nx wins”Pick Nx if one of these decides it for you:
- Plugins that already know your tools. Nx infers targets from your tool configs (Nx plugins). vx asks you to write each task.
- JVM or .NET in the same graph (multi-language support). vx runs JavaScript workspaces only.
- CI distribution run for you as a service (Nx Agents). vx ships the seam and a REAPI plugin; the servers are yours.
- One task, many modes. Nx’s
configurationskeepbuild:prodas one target. - A stable release. vx is pre-alpha: 0.0.x on npm, no support policy yet.
Switch in one command
Section titled “Switch in one command”vx initvx run build --allvx init reads the resolved Nx graph and writes one vx.config.ts per
package, plus the workspace file, through
@vzn/vx-migrate. Anything Nx can say that vx
cannot becomes a TODO(vx-migrate) comment, never a silent wrong value.
Executor targets (@nx/js:tsc, @nx/vite:build) run as themselves, so Nx
stays installed until you rewrite each one as the command it runs. Keep
your remote cache with nxCache(), which speaks Nx’s self-hosted
/v1/cache spec. The numbers above are native vx config.
Do my Nx flags still work?
vx run takes each Nx flag as it is, rewrites it to vx’s spelling, or
refuses it and names the vx way. None is dropped in silence.
vx run web:build is answered with vx run web#build.
Does vx have a daemon? No. vx spawns your command and nothing else. Each run does one git walk, and on a clean tree keys come from git’s index.
What does it cost? Nothing. vx is MIT, with no cloud and no account.
Do I need Bun? No. The npm package installs a prebuilt binary for Linux and macOS, x64 and arm64. Windows: use WSL2.