volta 2.0.2 vs fnm 1.39.0 switching Node 20.19.2 ↔ 22.14.0: session A-then-B median 13.9 ms vs 17.0 ms; steady node -v 7.4 ms vs 6.4 ms (direct 4.7 ms).
5 min read
On two Node pins (20.19.2 and 22.14.0), a volta 2.0.2 session that cds project A then B and runs node -v took a median 13.9 ms. The same A-then-B path with fnm 1.39.0 (fnm use in each directory) took 17.0 ms (~1.22x). Steady node -v overhead was 7.4 ms for the volta shim and 6.4 ms for fnm exec, against 4.7 ms for the direct Node 20 binary.
Short answer: pick volta if you want package.json pins and automatic shims with no shell hook. Pick fnm if you prefer .node-version files, a lighter Rust binary, and fnm env in your shell. Speed is not a reason to migrate either way on this box. No affiliate links in this post.
/usr/bin/node v20.19.2 so Volta's _VOLTA_TOOL_RECURSION session pin could not skew child shells.package.json volta.node; fnm via .node-version.node -v. Wall clock via process.hrtime.cd 8.42 ms, fnm exec 7.39 ms, fnm use 10.56 ms, volta run --node 7.60 ms. Re-running install for an already-present 20.19.2: volta 3.3 ms, fnm 2.9 ms._VOLTA_TOOL_RECURSION=1 from a parent started through its shim, so a nested harness must use a non-Volta Node or the pin looks stuck. Real interactive shells do not hit that trap.| Variant | Median wall | Runs (ms) |
|---|---|---|
volta: bash cd A then B, node -v each | 13.9 ms | 14.5 / 13.5 / 14.3 / 14.7 / 13.9 / 13.8 / 15.3 / 13.5 / 13.0 |
fnm: fnm use A then B in one shell | 17.0 ms | 17.0 / 17.3 / 17.1 / 17.9 / 16.6 / 16.8 / 15.4 / 17.3 / 16.8 |
| volta: resolve B after A (fresh shell each) | 8.4 ms | 8.6 / 9.4 / 8.6 / 8.3 / 8.0 / 8.0 / 8.4 / 7.6 / 8.8 |
fnm: fnm exec --using B after A | 7.4 ms | 7.5 / 8.1 / 8.0 / 6.9 / 7.1 / 6.7 / 7.6 / 7.4 / 6.4 |
fnm: fnm use B after A | 10.6 ms | 9.7 / 11.3 / 11.6 / 10.5 / 10.4 / 10.0 / 11.0 / 10.6 / 10.6 |
Slightly. The realistic "open project A, then project B" session favored volta by about 3 ms. fnm exec alone was the fastest single switch, but the common fnm use shell-hook path was the slowest of the four. Absolute times are tiny next to install or test suites; if you also care about which Node ships with which package manager, read the pnpm vs npm vs Bun post.
| Path | Median wall | Runs (ms) |
|---|---|---|
| Direct Node 20.19.2 binary | 4.7 ms | 4.8 / 4.4 / 4.6 / 4.5 / 5.4 / 5.1 / 5.2 / 4.9 / 4.6 / 4.6 / 4.7 |
volta shim via cd + node -v | 7.4 ms | 7.4 / 8.1 / 7.2 / 7.3 / 7.4 / 7.6 / 7.2 / 7.0 / 7.7 / 7.4 / 7.0 |
fnm exec --using 20.19.2 -- node -v | 6.4 ms | 6.1 / 6.5 / 7.1 / 6.9 / 6.4 / 6.2 / 6.9 / 5.8 / 6.4 / 6.0 / 5.8 |
About 2–3 ms over a direct binary. Neither shim is a problem for interactive work. For long-running tools the shim cost is paid once at process start; see the tsx vs ts-node post if the next question is how you launch TypeScript, and the Node.js 26 LTS schedule post if the question is which version to pin.
| Situation | My pick |
|---|---|
| You want the pin inside package.json next to engines | volta |
You already standardize on .node-version / .nvmrc | fnm (or stay) |
You hate shell eval hooks | volta shims |
| You want the smallest Rust version manager | fnm |
| Sub-20 ms switch times are "fast enough" | Do not switch for speed |
| You are also changing TypeScript majors | Decide the Node pin first; then read TypeScript 7: Should You Upgrade? |
Bottom line: volta's A-then-B session was 13.9 ms vs fnm's 17.0 ms; steady shim overhead was 7.4 ms vs 6.4 ms. Who should not switch yet: anyone whose current manager already pins the right Node for every repo. Who should try volta: teams that want package.json-owned pins without a shell hook. Who should try fnm: teams that already live in .node-version land and want a fast Rust CLI.
How this was made: I installed both managers, pinned Node 20.19.2 and 22.14.0 in two fixture dirs, ran interleaved switch and overhead timings from a non-Volta harness Node, and kept the JSON; the write-up was drafted with AI help and checked against that output.
Lab evidence
Hands-on on ShopperCove box 5 Oct 2026 ~23:20-23:26 IST (8 vCPU Intel Xeon / 15 GB shared Linux). Harness runner: system /usr/bin/node v20.19.2 (avoids Volta _VOLTA_TOOL_RECURSION in children). Fixture: proj-a Node 20.19.2, proj-b Node 22.14.0 (volta package.json pin; fnm .node-version). Versions: volta 2.0.2, fnm 1.39.0; both Node versions pre-installed. 2 warmups; 9 interleaved switch/session rounds; 11 steady node -v rounds. Session A-then-B medians: volta 13.93 ms; fnm use 16.97 ms. Switch B after A: volta cd 8.42 ms; fnm exec 7.39 ms; fnm use 10.56 ms; volta run 7.60 ms. Steady node -v: direct 4.72 ms; volta shim 7.38 ms; fnm exec 6.36 ms. Reinstall existing 20.19.2: volta 3.3 ms; fnm 2.9 ms. Raw: /workspace/bench-volta-fnm/res-volta-fnm.json. Not tested: cold Node downloads, nvm/asdf, Windows/macOS, Corepack-only. No affiliate.
Hands-on6 Oct 20266 min read
sharp 0.35.5 vs Jimp 1.6.1 on one 1920×1080 JPEG → width 800 JPEG q80. Warm medians (7 rounds after 1 warm-up): sharp 112 ms / 142,435 B vs Jimp 366 ms / 269,590 B (~3.3×). Fixture /workspace/bench-sharp-jimp/.
Hands-on6 Oct 20264 min read
Summary1 Oct 20263 min read