Skip to content
vxvx
GitHubBlueskydev.toRSS

vx vs Turborepo

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 Turborepo is the better choice. The design choices behind both are on vx, Turborepo, Nx, Bazel.

Cold build: total time
vx3 min 40 s
Turborepo4 min 59 svx 36% faster
Cold build: CPU burned
vx17 s
Turborepo21 svx 22% faster
Fully cached run (restored)
vx650 ms
Turborepo997 msvx 53% faster
Fully cached run (up-to-date)
vx393 ms
Turborepo463 msvx 18% faster
Secondary: time the runner adds to a cold build
vx2 s
Turborepo1 min 21 s
The table
1,090 packages, 3,270 tasksvxTurborepo
Cold build: total time3 min 40 s4 min 59 s (vx 36% faster)
Cold build: CPU burned17 s21 s (vx 22% faster)
Fully cached run (restored)650 ms997 ms (vx 53% faster)
Fully cached run (up-to-date)393 ms463 ms (vx 18% faster)
Secondary: time the runner adds to a cold build2 s1 min 21 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.

vxTurborepo
ConfigTypeScript, evaluated into the cache keyJSON (turbo.json), static
Default inputsNone: you declare cache.inputs.filesEvery tracked file in the package
Outputs on run and restoreWiped first: no stale file survives a hitAdditive: stale files survive
Task sandboxFree and local, opt-in per task (Linux, macOS)Its docs describe no file sandbox
Clean-tree hashingNo source reads: git index OIDsgit OIDs
DaemonNoneNone for turbo run since 2.8.11
Remote cacheAny wire through a plugin; turboCache() speaks Turbo’s ownVercel’s /v8/artifacts, hosted or your own
PluginsEvery pipeline stage, 14 hooks; core applies noneIts docs describe no plugin surface
Why a task missedvx why diffs two keys: the file, env var or upstream that moved--summarize shows hashes, not the diff
Watch modevx watch <task>turbo watch
Prune for Dockervx prune, from @vzn/vx-lockfileturbo prune
Import boundariesNo: a lint’s jobboundaries
Non-JS projectsNoExperimental: Go, Cargo, uv
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.

  • Your config is in the key. vx evaluates each TypeScript config before hashing, so imports and computed values move the key.
  • Outputs are owned. Declared outputs are wiped before every run and every restore, so a file from last week’s build cannot ride along on a hit.
  • Tasks can prove their reads. Opt a task into the sandbox, 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.
  • Pull requests keep to their own scope. A pull request run reads the trusted keys and writes only its own, over any wire, Turbo’s included. The real boundary is a token your cache server limits; the scope keeps honest runs apart (security model).

Pick Turborepo if one of these decides it for you:

  • Caching before anyone writes an input list. Turborepo hashes every tracked file in a package by default (inputs). vx makes you declare inputs, and keeping them right is your job.
  • Native Windows. vx runs under WSL2.
  • A stable release with a support policy, today. vx is pre-alpha: 0.0.x on npm, no support policy yet.
  • A terminal UI and import boundaries. Both ship in Turborepo. vx streams plain blocks and leaves boundaries to your linter.
  • Go, Cargo or uv in the graph. Turborepo has experimental support. vx runs JavaScript workspaces only.
Terminal window
vx init
vx run build --all

vx init finds turbo.json and writes one vx.config.ts per package, plus the workspace file, through @vzn/vx-migrate. Anything Turbo can say that vx cannot becomes a TODO(vx-migrate) comment, never a silent wrong value. Your package.json scripts are never edited. Keep your remote cache with turboCache(): any server speaking Turbo’s /v8/artifacts API, Vercel’s hosted cache included. The numbers above are native vx config.

Do my Turbo flags still work? vx run takes each Turbo flag as it is (--filter, --affected, --concurrency), rewrites it to vx’s spelling, or refuses it and names the vx way. None is dropped in silence.

Can I keep Vercel Remote Cache? Yes. Set TURBO_TOKEN and TURBO_TEAM, declare turboCache(), and vx stores its own artifacts there. Turbo cannot read what vx stores, and vx does not read Turbo’s entries.

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.