Skip to content
vxvx
GitHubBlueskydev.toRSS

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.

Cold build: total time
vx3 min 40 s
Nx3 min 49 svx 4% faster
Cold build: CPU burned
vx17 s
Nx52 svx 3× faster
Fully cached run (restored)
vx650 ms
Nx6.25 svx 9.6× faster
Fully cached run (up-to-date)
vx393 ms
Nx6.45 svx 16× faster
Secondary: time the runner adds to a cold build
vx2 s
Nx11 s
The table
1,090 packages, 3,270 tasksvxNx
Cold build: total time3 min 40 s3 min 49 s (vx 4% faster)
Cold build: CPU burned17 s52 s (vx 3× faster)
Fully cached run (restored)650 ms6.25 s (vx 9.6× faster)
Fully cached run (up-to-date)393 ms6.45 s (vx 16× faster)
Secondary: time the runner adds to a cold build2 s11 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.

vxNx
ConfigTypeScript, evaluated into the cache keyJSON (project.json, nx.json), plus inferred targets
DaemonNone; nothing boots per taskOn by default locally
Outputs on run and restoreWiped first: no stale file survives a hitAdditive
Task sandboxFree and local, opt-in per task (Linux, macOS)An Nx Cloud feature
Remote cacheAny wire through a plugin: Bazel REAPI, Turbo’s, Nx’s self-hosted specNx Cloud or a plugin
Flaky task detectionLocal: the same key failed after it passedNx Cloud
Why a task missedvx why diffs two keys: the file, env var or upstream that movedShows hashes, not the diff
Tasks on other machines@vzn/vx-reapi over Bazel’s REAPI, on servers you run or rentNx Agents on Nx Cloud
PluginsEvery pipeline stage, 14 hooks; core applies noneInferred tasks, graph data, generators, executors
Non-JS projectsNoGradle, Maven and .NET
One task, many modes (build:prod)No: a TypeScript function per mode, two tasksconfigurations and -c
Terminal outputStreamed blocks you can scroll, copy and pipeTerminal UI
WindowsWSL2Native
InstallOne binary; no Bun, Node optionalnpm and Node

Every row is held in vx vs Turborepo vs Nx, which links each claim to its source.

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 last replays any recorded run; vx info and vx why read 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).

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 configurations keep build:prod as one target.
  • A stable release. vx is pre-alpha: 0.0.x on npm, no support policy yet.
Terminal window
vx init
vx run build --all

vx 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.

Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.