
pnpm 11.7 landed on June 15 with three features that solve specific, long-standing friction points: read-only store installs for Nix and OCI users, atomic batch publishing for monorepos, and per-scope auth tokens for teams managing packages across multiple GitHub organizations. None of these make the keynote highlights reel. All of them unblock real workflows that have had ugly workarounds for years.
frozenStore: pnpm Finally Works with Nix and Read-Only OCI Layers
The new --frozen-store flag lets pnpm install from a package store that lives on a read-only filesystem. That means a Nix store, a read-only OCI image layer, or a bind-mounted CI artifact cache — environments where pnpm previously failed because it always tried to write to the store, even on a fully cached run.
The implementation is cleaner than a workaround: pnpm opens the store’s SQLite index.db with an immutable=1 URI parameter, bypassing the WAL and shm sidecar files that would fail to create on a read-only mount. Every code path that writes to the store is then suppressed for the duration of the install.
A few constraints to know before you reach for it:
- Requires Node.js >=22.15.0, >=23.11.0, or >=24.0.0. Older runtimes get a clear
ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODEerror. - Incompatible with
--forceand with a running pnpr server — both need to write to the store. - The side-effects cache is not written under
--frozen-store. - If a build step is missing from the virtual store, you get
ERR_PNPM_FROZEN_STORE_NEEDS_BUILDupfront rather than a crash mid-install.
The recommended pattern for CI with a pre-populated store:
pnpm install --frozen-store --offline --frozen-lockfile
For Nix users specifically, this closes a gap that has existed since pnpm switched to the SQLite store format in 11.0. The Nix community has been asking for this for years — pnpm’s store outputs could not be used as proper Nix derivation outputs because writes at install time violated Nix’s immutability contract. That argument is over now.
Batch Publish: One Request, All-or-Nothing
Monorepo teams have a new flag to try: pnpm publish --recursive --batch. Instead of sending one HTTP request per package, it sends everything to the registry in a single PUT /-/pnpm/v1/publish call. Fifty packages in your workspace means one network round trip instead of fifty.
The real value is not the speed. It is the atomicity: if any package in the batch fails registry validation, none of them publish. Partial monorepo releases — where package A’s v2.0.0 lands but packages B and C do not — have been a quiet source of pain for teams that automate their release pipelines. --batch makes that failure mode disappear.
The limitation to know: the target registry must implement the batch endpoint. pnpm’s own registry (pnpr) supports it. Public registries that do not will return ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED. Whether npmjs.com has added the endpoint was not confirmed at the 11.7 release, so test before wiring it into your release CI.
Scope-Specific Auth Tokens: Finally
pnpm can now use different auth tokens for different package scopes even when those scopes point at the same registry URL. This is primarily a GitHub Packages fix.
GitHub Packages hosts multiple organizations at the same hostname. Teams that maintain packages for several clients — a common setup in agencies and enterprises — need to authenticate as different identities for @org-a and @org-b simultaneously. Previously, pnpm selected auth by registry URL only, which forced both scopes to share a single token or required CI environment tricks.
The configuration is straightforward:
@org-a:registry=https://npm.pkg.github.com/
@org-b:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:@org-a:_authToken=ORG_A_TOKEN
//npm.pkg.github.com/:@org-b:_authToken=ORG_B_TOKEN
Packages without a matching scope fall back to the registry-wide token. The pnpm login command also accepts --scope to write the right key: pnpm login --registry=https://npm.pkg.github.com --scope=@org-a.
Under the Hood: pacquet Now Resolves, Not Just Materializes
The pnpm team has been migrating the hot path of installs to pacquet, a Rust reimplementation. In 11.2, pacquet handled the fetch-and-link phase only — pnpm still resolved the dependency graph in TypeScript and handed pacquet a finished lockfile. In 11.7, when pacquet >=0.11.7 is declared in configDependencies, pnpm delegates the entire install — resolution, lockfile write, and materialization — to the Rust backend in a single pass.
The fetch-and-link phase accounts for 60 to 70 percent of cold install time in most workloads. Fewer handoffs between the TypeScript and Rust layers means faster cold installs. pnpm add, update, and remove still resolve in TypeScript for now.
Security: Lockfile Alias Hardening
11.7 closes a lockfile-based supply chain attack vector. The lockfile verification gate now rejects path-traversal aliases and reserved names — ../../../escape, .bin, .pnpm, node_modules — before any fetch or filesystem work begins. A crafted lockfile alias could otherwise write files outside the install root.
This is part of a broader hardening arc in pnpm 11.x. The major release shipped with minimumReleaseAge defaulting to 24 hours (a hold on newly published packages to catch typosquatting), blockExoticSubdeps on by default, and the 11.6 fix for token leakage via expansion in repository-controlled .npmrc files. Given the supply chain incidents that hit the JS ecosystem earlier this year, pnpm 11’s security posture looks prescient. If your team is still on npm without pnpm 11’s security defaults, you are accepting unnecessary risk.
How to Upgrade
From any pnpm 11.x release, upgrading is a single command:
pnpm self-update
If you are still on pnpm 10 or earlier, check the official migration guide first — pnpm 11 requires Node.js 22+ and moves configuration out of package.json into pnpm-workspace.yaml. The pnpx codemod run pnpm-v10-to-v11 codemod handles most of the mechanical changes.
If you plan to use --frozen-store, verify your Node.js version. Node 22.15.0 is the minimum, and anything below that will fail loudly on the first run.













