Verdict
pnpm is the best all-rounder: 2.8× faster than npm from a cold start and near-instant when nothing changed. Bun wins with a warm cache. Yarn 1 was the slowest and needed a 3.7 GB cache.
Tested on
- npm
- 11.9.0
- pnpm
- 12.5.1
- Bun
- 1.4.2
- Yarn
- 1.22.22 (classic)
- Node
- 24.14.0
- Machine
- i3-1115G4, 2 cores, 12 GB, NVMe SSD
- OS
- Windows 11 Pro 26200
- Network
- ~37 Mbps, ~95 ms to the npm registry
Contents
The project is this blog’s own package.json: Astro, Tailwind, MDX, Cloudflare’s Wrangler and a few build tools. That comes to roughly 12,400 files and 370 MB in node_modules, a normal modern web app rather than a toy. Every tool installed exactly the same dependencies and ran the same install scripts.
The results
- pnpm35.6 sbest
- Bun39.6 s
- npm99.0 s
- Yarn 1190.1 s
- Bun12.8 sbest
- pnpm16.5 s
- Yarn 116.7 s
- npm18.6 s
- pnpm0.13 sbest
- Bun0.19 s
- Yarn 10.65 s
- npm1.69 s
- pnpm393 MBbest
- Bun416 MB
- npm464 MB
- Yarn 13700 MB
Every run
| Tool | Cold (s) | Warm (s) | No change (s) |
|---|---|---|---|
| npm | 103.85 · 94.34 · 98.99 | 18.58 · 19.24 · 17.09 | 1.73 · 1.69 · 1.37 |
| pnpm | 35.56 · 38.22 · 33.73 | 16.52 · 15.00 · 18.79 | 0.13 · 0.12 · 0.18 |
| Bun | 42.75 · 39.58 · 38.11 | 12.76 · 12.89 · 12.01 | 0.22 · 0.19 · 0.16 |
| Yarn 1 | 190.11 | 16.70 | 0.65 |
Every run finished without errors or retries, and every tool produced a working node_modules and a lockfile.
Which one should you use?
- Starting fresh, or CI without a cache: pnpm or Bun. Both did the cold install in under 40 seconds, where npm took about 99. On this project and connection, that’s roughly 2.8× (pnpm) and 2.5× (Bun) faster than npm.
- Everyday work with a warm cache: Bun was quickest at 12.8 seconds. pnpm and npm were closer together than the cold numbers suggest (16.5 s vs 18.6 s).
- Running install on a finished project: pnpm returned in 0.13 s, about 13× faster than npm’s 1.7 s. That’s the one you feel dozens of times a day in scripts and hooks.
- npm still works fine. It was never broken, only slower, and it’s already installed with Node.
- Yarn 1 (classic) was the slowest from cold and built a cache almost 10× larger than the others. If you use Yarn, look at modern Yarn (v4) instead. It wasn’t part of this test.
Two things that surprised me
How this was measured
- Same project, same scripts. Every tool got the same
package.json. pnpm and Bun were given permission to run the same two install scripts (esbuild, workerd) that npm and Yarn run by default, so no tool skipped work. - Separate, empty caches. Each tool got its own fresh cache folder for every round, so nothing was shared between tools or with the machine’s normal caches.
- Called directly. Tools ran from their own installed binaries, not through
npx, so none paid extra startup time. - Rotating order. The order changed every round, so a slow patch of network didn’t always hit the same tool.
- Three rounds, median reported. The table above shows every individual run.
- Three scenarios: cold (empty cache, no lockfile), warm (cache and lockfile present,
node_modulesdeleted), no change (run install again right away).
Limits of this test
- Yarn 1 ran once, not three times. Its 3.7 GB cache left this machine within 0.4 GB of a full drive, so I stopped it after round 1 rather than risk the system. Treat its numbers as one data point.
- Cold installs mostly measure download speed. They were run on an ordinary ~37 Mbps connection. On a faster connection all four get quicker, and the gaps may shrink.
- Windows only, one machine. These results come from one 2-core laptop. Linux and macOS, especially in CI, can rank differently.
- Disk size note: pnpm’s
node_moduleslinks to its global store instead of copying, so on disk it uses less than the 373 MB counted here.
— N.K., end of entry No.011