Inside pnpm's Rust Rewrite
Explore pnpm 12’s Rust rewrite, benchmark results, compatibility with pnpm 11, and upgrade risks, including CI changes that may break installs.
pnpm 12 replaces pnpm’s TypeScript codebase with a native Rust rewrite and keeps pnpm 11’s commands, flags, settings and lockfile format, so most projects can upgrade without editing any config.
If you run pnpm across a monorepo and a busy CI fleet, you’ve probably seen “90% faster” in the headlines and wanted to know what the catch is. A new major version of the tool that writes your lockfile deserves more scrutiny than a benchmark chart.
This article covers why a JavaScript package manager is slow in the first place, what the Rust engine changed and what it cost, who measured each number, and which behavioural changes can reach your pipeline.
Key Takeaways
- pnpm 12.0.0 went stable on August 26, 2026, and keeps pnpm 11’s commands, flags, settings and lockfile format, apart from a short list of documented differences.
- On pnpm’s own benchmark page (pnpm 11.27.1 vs 12.7.0), a warm repeat install drops from 563 ms to 18 ms, while a clean install only drops from 8.4 s to 4.4 s, because network transfer and unpacking dominate cold installs.
- Vercel measured its 1,670-package workspace installing in 64.4% to 90.5% less time on pnpm 12 than on pnpm 10.28, but uncached Corepack startup got 11.1% slower because the native download is larger.
- The change most likely to break CI is the removal of
pnpm install --resolution-only. Usepnpm peers checkinstead.
The Constraint: Same pnpm, Different Engine
pnpm 12 was built so that upgrading would not feel like a migration. The pnpm 12.0 release post sets that as the goal, and the compatibility guide confirms that, apart from a short list of differences, pnpm 12 keeps pnpm 11’s commands, flags, settings and lockfile format. pnpm 12 also keeps the content-addressable store, which lets projects share package files instead of copying them. InfoQ lists the node_modules layout as unchanged too.
Most rewrites treat the new codebase as a chance to fix old design decisions. pnpm made compatibility the goal, to the point that pnpm says its documentation applies to both versions. Almost nothing changed on the surface. The work was replacing everything underneath it.
Why Is a JavaScript Package Manager Slow?
A package manager written in JavaScript pays two costs on every large install. It boots the Node.js runtime each time you invoke it, and it pushes thousands of filesystem operations through a single JavaScript runtime.
An install works through roughly these phases:
- Fetch package metadata from the registry.
- Resolve the dependency graph.
- Download tarballs.
- Unpack them into the store.
- Link packages into
node_modules.
The first cost is fixed. Before pnpm 11 did any real work, Node.js had to start. pnpm 12 is published as native binaries, one @pnpm/exe.<platform>-<arch> package per platform. The self-update documentation confirms that nothing launches Node.js first, so that startup cost is gone.
The second cost grows with the work. Phases 4 and 5 involve unpacking tarballs and hard-linking thousands of files, and every one of those operations passes through the JavaScript runtime.
The principle carries over to any tool: removing a fixed overhead helps most when there’s little other work left. When an install has almost nothing to do, startup is most of the wall-clock time. When it has to download hundreds of megabytes, startup barely registers.
How Much Faster Is pnpm 12?
pnpm 12’s speed gains are real but uneven: a warm repeat install drops from 563 ms to 18 ms, while a clean install only drops from 8.4 s to 4.4 s. The gains come from two separate measurement sets with different baselines. Don’t merge them into one number.
| Scenario | Before | pnpm 12 | Measured by | Baseline |
|---|---|---|---|---|
| Warm repeat install | 563 ms | 18 ms | pnpm benchmark page (pnpm 12.7.0) | pnpm 11.27.1 |
| Clean install | 8.43 s | 4.42 s | pnpm benchmark page (pnpm 12.7.0) | pnpm 11.27.1 |
| 1,670-package workspace, six scenarios (median) | n/a | 64.4–90.5% less time | Vercel (pnpm 12.0.0) | pnpm 10.28.0 |
| Uncached Corepack startup | n/a | 11.1% slower | Vercel (pnpm 12.0.0) | pnpm 10.28.0 |
| Cached Corepack startup | n/a | 74.7% faster | Vercel (pnpm 12.0.0) | pnpm 10.28.0 |
The pnpm figures are for the alotta-files project on the pnpm benchmark page, comparing pnpm 11.27.1 with pnpm 12.7.0. The page re-runs regularly and always shows the newest version of each tool, so expect those numbers to change. The gap between the two pnpm rows is the previous section in action. The warm install improves about 30x, most likely because fixed costs such as startup were a large share of its runtime. The clean install improves about 1.9x because network transfer and tarball unpacking take most of the time, whatever language the engine is written in.
Vercel’s own measurements show the same pattern. With node_modules already present, a warm store and scripts turned off, installs fell from 1.476 s to 142 ms. A fully cold install with lifecycle scripts enabled went from 9.850 s to 3.472 s. Each median comes from 20 runs per version on one Linux machine, in a 21-project Turborepo workspace.
pnpm 12’s native build has a cost. Vercel put the pnpm 12 Corepack download at 47.3 MB, up from 17.5 MB for pnpm 10.28.0, and ties the slower uncached startup to that bigger download. CI runners that don’t cache Corepack will probably pay that on every job.
The deterministic cycle handling that ships alongside the engine is a separate gain. pnpm’s compatibility guide ties about 25% lower memory use and 2–3x faster peer resolution on cycle-heavy workspaces to that change, rather than to the Rust engine itself.
pnpm’s public benchmark page now compares pnpm 12 only with npm and pnpm 11. InfoQ reported that pnpm dropped Bun and Yarn from the comparison after problems with the benchmark setup made those rankings unreliable.
Why Did the pnpm Lockfile Format Have to Stay Identical?
pnpm 12’s lockfile format had to stay the same because a lockfile break would split a team partway through an upgrade. If pnpm 12 wrote a new format, every laptop and CI runner would have to switch on the same day. Otherwise, two lockfile dialects would compete in one repository and every pull request would carry noise from whichever version last ran.
Keeping the format is meant to let a team upgrade gradually. It doesn’t mean nothing in the file will ever change. Format and contents are different things:
- Existing lockfiles keep working. The guide notes that pnpm doesn’t touch existing entries until something makes it re-resolve them.
- Re-resolution can change entries. pnpm 12 records dependencies from GitHub, GitLab and Bitbucket under their HTTPS URL, never an SSH one. It also cuts dependency cycles in the same place every time, which makes lockfiles smaller in workspaces with many cycles.
Review the diff from your first re-resolving install on pnpm 12 as its own commit.
What Breaks When You Upgrade to pnpm 12?
If a pnpm 12 upgrade breaks a CI job, the most likely cause is a script that still calls pnpm install --resolution-only. v12 rejects that flag. Its peer-dependency reporting role now belongs to pnpm peers check:
# pnpm 11
pnpm install --resolution-only
# pnpm 12
pnpm peers check
v12 also rejects pnpm install --frozen-lockfile false. Use --no-frozen-lockfile to turn frozen-lockfile mode off, and plain --frozen-lockfile to turn it on.
Other changes mostly affect results, but three of them can also stop a command:
| Change | Who it affects | What to do |
|---|---|---|
| Git deps on GitHub/GitLab/Bitbucket resolve via HTTPS | Private repos accessed over SSH | Configure Git URL rewriting on the machine |
Unknown pnpm-workspace.yaml keys are reported, and fail the command with ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS when the project pins a pnpm version the running pnpm satisfies | Configs with typos | Fix or remove the key |
Commands that change the global installation fail under sudo with ERR_PNPM_SUDO_NOT_SUPPORTED | sudo pnpm self-update and similar | Run them without sudo |
With engineStrict on, an incompatible engine in regular dependencies fails the install, even under an optionalDependencies entry | Projects that use engineStrict | Expect installs that used to warn to fail |
Linux packageImportMethod: auto tries hardlinks before reflinks | Linux users | Usually nothing |
Global node, deno or bun follows the project’s pin | Machines with global runtimes | Expect the pinned version |
The v12.0.0 release notes explain why the workspace check matters. In pnpm 11, a typo in a setting such as minimumReleaseAge meant pnpm skipped the key without a word, so the rule it was meant to set never applied:
# pnpm-workspace.yaml
packages:
- "apps/*"
minimumReleseAge: # typo: pnpm 12 reports this key
The compatibility guide lists eight differences in total. Six change a result, and two reject command-line syntax that pnpm 11 accepted: --resolution-only and --frozen-lockfile false. The table above doesn’t cover all of them. It leaves out how pnpm 12 treats package-manager names such as yarn in pnpm add, and the workspace-key and sudo rows come from the release notes rather than the guide. Read the full guide before upgrading.
Should You Upgrade to pnpm 12?
Usually yes, and the upgrade is uneventful. That was the design goal. From pnpm 11.10 or newer, run:
pnpm self-update
In a project that pins pnpm through packageManager, self-update just bumps the version in that field rather than installing pnpm globally. pnpm then fetches the new version the next time you run a command. Commit the change so CI uses the same version:
{
"packageManager": "pnpm@12.8.1"
}
Use the latest 12.x release. If your install depends on less common features such as pnpm deploy or specific linker modes, try the upgrade on a branch first. Teams still on npm can read whether switching from npm to pnpm makes sense before they take on both changes at once.
Conclusion
pnpm 12 kept the parts you use every day, including the commands, the lockfile format and the store model, and rebuilt the engine behind them. The rewrite paid off because two costs were limiting the JavaScript version: Node.js startup on every invocation and file I/O funnelled through a single runtime. Before you upgrade, search your CI config for --resolution-only and --frozen-lockfile false, check for private Git dependencies accessed over SSH, and commit the first re-resolved lockfile on its own so you can review the diff.
FAQs
Does pnpm 12 need Node.js installed to run?
No. Once installed, pnpm 12 runs as a native program, so Node.js isn't needed. The standalone install script doesn't need Node.js either, even at install time. The one exception is installing pnpm 12 through npm, where the installer needs Node.js 22.13 or newer. If your platform has no prebuilt pnpm 12 binary, the pnpm docs suggest using the JavaScript pnpm 11 instead.
How do I keep using SSH for private Git dependencies in pnpm 12?
pnpm 12 fetches GitHub, GitLab and Bitbucket dependencies over each host's HTTPS URL. To keep using SSH, set up a Git URL rewrite, for example with git config --global url.'git@github.com:'.insteadOf https://github.com/. pnpm calls git under the hood, so that rule covers every Git command pnpm runs. Hosts pnpm doesn't recognise, and URLs that include credentials, are left exactly as you wrote them, SSH included.
Why does pnpm 12 fail on an unrecognized pnpm-workspace.yaml setting?
It only fails when the project pins a pnpm version and the pnpm you're running matches that pin. In that case pnpm treats the unknown key as a mistake and stops with ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS. Without a matching pin you get a warning and the command carries on. If the key looks like a typo, pnpm names the setting you probably meant. The pnpm config commands still run on a file with a bad key, so you can use them to find and fix it.
Which pnpm 12 commands fail when run with sudo?
Under sudo, pnpm setup, pnpm self-update and every command that changes the global installation, such as pnpm add --global, stop with ERR_PNPM_SUDO_NOT_SUPPORTED. Earlier versions quietly wrote to root's home directory instead. Global packages and settings live in your own home directory, so none of these commands needs root. Read-only commands such as pnpm bin --global still run under sudo.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k