Skip to content
Fran Gonzalez
← Back to blog
(updated Jul 17, 2026)·Clanker·12 min read

What Changed in pnpm Since 11.7 Until 11.13.1

Seven pnpm releases shipped between 11.7 and 11.13.1, and the one that maps to my config is a peak-memory fix for the minimumReleaseAge and trustPolicy resolution path.

Some matmuls wrote this slop, sorry. My goal with this content is to document some work I (a real human bean) do while poking the Clanker, and try to learn something along the way.

The last pnpm catch-up post stopped at 11.7.0. Since then, six minor releases shipped (11.8 through 11.13), plus the 11.13.1 patch. Three of them landed inside a single week: 11.11 on July 9, 11.12 on July 11, 11.13 on July 13. The project had drifted up to 11.10.0 between posts, so this one surveys the full 11.7-to-11.13.1 arc and catches it up to the current latest.

This batch was quieter than the last one. There was no settings move out of package.json, no silently dropped override. The change that maps directly to my config is a peak-memory reduction in 11.11 for the resolution path that minimumReleaseAge and trustPolicy force. The rest was security hardening that applied by default, a pile of install and audit determinism fixes, and new commands I mostly skipped because I don’t publish npm packages.

Note

Fact-checked against the pnpm 11.8 through 11.13.1 release notes and the GitHub release pages on 2026-07-17. I did not reproduce every patch locally; the adoption claims are grounded in the project’s pnpm-workspace.yaml and the Dagger CI module.

Upgrade baseline

The project’s supply-chain config was already in place from the last round: minimumReleaseAge: 1440, trustPolicy: no-downgrade, trustLockfile: true, strictPeerDependencies, allowBuilds, and the rest. CI already ran pnpm audit --prod --audit-level high, pnpm audit --audit-level high, pnpm audit signatures, and pnpm peers check. The mise.toml pin had moved from 11.7.0 to 11.10.0 between posts.

Two things were worth attention this round. First, the v11 audit rewrite had a known performance regression on cyclic lockfiles, fixed in 11.9. Second, the resolution path that minimumReleaseAge and trustPolicy turn on fetches full registry metadata for every package, and 11.11 cut its peak memory back to the pnpm 10 baseline. Both apply to my config. The first I had already benefited from at 11.10.0; the second was the reason to move past 11.10.

Release-by-release changes

pnpm 11.8: dry-run installs and SBOM

pnpm install --dry-run runs full dependency resolution and reports what a real install would change, then writes nothing: no lockfile, no manifests, no node_modules. It exits 0 on success, matching npm install --dry-run. I reach for it before a large Renovate batch to preview lockfile churn without touching the tree.

pnpm sbom (added in 11.8.0) generates a CycloneDX Software Bill of Materials. In 11.8 it gained --out (write to a file), --split (one SBOM per selected workspace package), and root-component selection from --filter. 11.9 added --exclude-peers, which drops peers and any subtree reachable only through them.

The release also closed a path-traversal hole in config-dependency handling (GHSA-qrv3-253h-g69c). Config dependency names and versions from the env lockfile are validated before they build filesystem paths, so a committed lockfile can no longer write outside node_modules/.pnpm-config using traversal-shaped names.

One fix I was glad to see: pnpm strips the com.apple.quarantine extended attribute from native binaries imported from the store, so macOS Gatekeeper no longer blocks them (#11056). This project lists better-sqlite3 and sharp in allowBuilds, so native binaries matter here, and the fix removes a class of macOS friction.

pnpm 11.9: audit speed and generated-tarball integrity

The v11 audit rewrite had a regression. On lockfiles with dependency cycles, pnpm audit could take minutes and could not be cancelled (#12086). 11.9 recomputes reachability once per node using Tarjan’s strongly-connected-components algorithm, with a non-recursive path walk, so time and memory stay linear in cyclic graphs (#12212). My tree is small, so I never measured the regression, but the fix is the right one and it is now in the baseline.

11.9 also handles registries that generate tarballs on demand and cannot publish a checksum in package metadata. pnpm computes the integrity from the downloaded tarball and stores it in the lockfile. If an entry is still missing integrity, lockfile verification rejects it with ERR_PNPM_MISSING_TARBALL_INTEGRITY instead of silently refetching. This closes the gap the 11.4 tarball-integrity hardening left for checksum-less registries.

One behavioral note for anyone running pnpm audit --fix: it now writes one combined minimumReleaseAgeExclude entry per package (axios@0.18.1 || 0.21.1) instead of one per version, and multiple exact-version entries behave like a single || disjunction. My existing excludes are single-package, so nothing merged.

pnpm 11.10: CI-friendly auth and the Rust port preview

The standout is the _auth setting. It configures registry authentication as a single structured, URL-keyed value, and it can be set from the pnpm_config__auth environment variable. That matters in CI because environment variable names containing /, :, or . are silently dropped by GitHub Actions, bash, and zsh, which broke the older pnpm_config_//host/:_authToken=… form. _auth has an ordinary name, so it survives. It is honored only from the environment and the global config, never from project files, so a repo-controlled pnpm-workspace.yaml cannot redirect the token to a different host.

I use the public npm registry exclusively, so _auth stays unused here. If this project ever pulled from GitHub Packages or an internal Artifactory, it would be the first thing I wired into the Dagger pipeline.

11.10 also lets pnpm self-update install pnpm v12, the Rust port, on the next-12 dist-tag (pnpm self-update next-12). The native binaries ship as @pnpm/exe.<platform>-<arch> packages and link with no Node.js launcher. I cover why I am not switching in The Rust port.

Smaller additions: pnpm prefix prints the package or global prefix directory; pnpm issues aliases pnpm bugs; and a prototype-pollution hazard from a dependency named __proto__ was closed during preferred-version seeding.

pnpm 11.11: the memory fix that maps to my config

This is the release that justified leaving 11.10. The resolution phase memoizes each package’s registry metadata, and it had been retaining the raw response body (used only to mirror the response to disk) for the whole phase. 11.11 keeps a body-less copy in the memoized cache, so the raw body only lives as long as the call that writes the disk mirror.

The release notes quantify it for graphs that fetch full metadata, which is exactly the path minimumReleaseAge and trustPolicy turn on: peak RSS drops by roughly 30%, back in line with pnpm 10. My tree is moderate (about 890 resolved packages), and I did not benchmark the change myself, so the 30% figure is from the release notes rather than my own measurement. The relevant point is that v11 had regressed memory on the exact configuration I run, and 11.11 closed that regression.

11.11 also hardened the lockfile against path traversal. A dependency path key that reconstructs to a traversal sequence (../../../tmp/x@1.0.0) is rejected by the isolated linker, the Plug’n’Play resolver map, and the global virtual store, and a manifest name shaped like a scoped traversal (@x/../../../<path>) can no longer write outside node_modules during install. These apply by default; I changed nothing.

Other 11.11 items: pnpm access manages package visibility on the registry; allowBuilds entries for git-hosted packages can match by repository URL without pinning a commit; pnpm self-update now honors trustPolicy: no-downgrade; pnpm publish redacts inline user:pass@ credentials. None of these apply to me; they assume you publish packages, and I don’t.

pnpm 11.12: peer-resolution deadlock and determinism fixes

11.12 is almost entirely patches. The one that would have caught me if I had hit it: pnpm install could hang forever when a peer-dependency cycle spanned a project’s own dependencies and auto-installed peers, for example installing electron-builder@26.15.3 (#12921). A second peer fix stops auto-install from picking a version the declared range rejects: a package peer-depending on ^1.0.0 could previously pick up a 2.0.0 resolved for another workspace project. Peers are now deduplicated onto the highest preferred version that satisfies the declared range.

The only minor addition is a pnpmfile fetcher delegation envelope. A custom fetcher can return { delegate: <resolution> } and pnpm rewrites the resolution and runs its built-in fetcher. That is portable across pnpm and its new resolver (pacquet). I do not ship a pnpmfile, so it does not apply.

pnpm 11.13: convergence overrides and workspace release management

11.13 adds a new override selector form with an empty range:

# pnpm-workspace.yaml
overrides:
  "form-data@": 4.0.6

This is a convergence override. It rewrites a dependency edge only when its exact version satisfies the edge’s declared range, so compatible consumers converge on one version while incompatible consumers keep their own resolution. The value must be an exact version, and pnpm warns when the override goes stale and names the version to converge on (#12794). I keep a targeted esbuild override ("esbuild@0.28.0": "0.28.1"), so I do not need the convergence form yet. It is the right tool the next time a transitive version fan-out needs collapsing.

The rest of 11.13 is workspace release management, which I skip: versioning.epics ties member package majors to a lead package; native pnpm change records changesets-compatible intents; bare pnpm version -r consumes them with dependent propagation, fixed groups, changelogs, and release lanes; and pnpm team manages registry organization teams. All of these assume you publish packages, and I don’t.

One patch worth knowing. The dependency status check no longer reports “up to date” when a package.json, .pnpmfile.cjs, or patch file was edited in the same second as the previous install on whole-second-resolution filesystems such as ext4. The optimistic fast path treated a same-second edit as unchanged and skipped re-resolution. CI runs on Linux, so this is the class of bug I would have hit eventually.

pnpm 11.13.1

Three patches: pnpm pack no longer applies workspace-root ignore rules when a workspace package has its own .npmignore; the interactive minimumReleaseAge approval prompt stays visible during install instead of being overwritten by progress redraws (#13019); and pnpm self-update links native platform binaries stored in sibling global virtual store slots.

The Rust port

11.10 can install pnpm v12, the Rust port, via pnpm self-update next-12. The native binary ships under @pnpm/exe.<platform>-<arch> and links with no Node.js launcher, so there is no Node.js startup cost. I am not switching this project to it. It is on next-12, a prerelease dist-tag, and my CI depends on pnpm’s stable behavior for audit signatures, peers check, minimumReleaseAge, and the workspace settings. The right time to evaluate it is after it reaches a stable dist-tag and the features I rely on are confirmed equivalent. For now I note it exists.

What I skipped

  • pnpm sbom: I don’t publish packages, so there is no consumable artifact to attach an SBOM to. I left it out of CI.
  • _auth: I use the public npm registry exclusively.
  • pnpm access, pnpm team, pnpm stage, pnpm change, pnpm version -r, versioning.epics, release lanes: all assume package publishing.
  • pnpm runtime set: I manage Node.js through mise.toml, not pnpm’s runtime manager.
  • pnpmfile fetcher delegation, convergence overrides: no pnpmfile, and no version fan-out that needs collapsing yet.

Verification

The version source is a single line in mise.toml. The Dagger CI module bootstraps pnpm through mise install pnpm, so there were no workflow files to edit. mise.lock regenerated from it and pinned the 11.13.1 checksums and GitHub provenance attestation; nothing else in the lock moved.

# mise.toml
- pnpm = "11.10.0"
+ pnpm = "11.13.1"

package.json keeps engines.pnpm at >=11.7.0 <12. I left the floor where it was; nothing in 11.11 through 11.13.1 raised the minimum this project needs, and the floor documents the security baseline from the last post rather than the current pin.

The four supply-chain gates pass when run locally with pnpm 11.13.1, using the exact commands the Dagger module runs in CI: pnpm audit --prod --audit-level high, pnpm audit --audit-level high, pnpm audit signatures, and pnpm peers check all exit 0. I did not run the full Dagger module for this post; the four gates above are what I verified. pnpm audit signatures verified all 887 registry packages as carrying a valid ECDSA signature, the expected result for an npm-registry-only project.

A bare pnpm audit at the default threshold still reports one moderate advisory, GHSA-48c2-rrv3-qjmp, in a dev-only path through @astrojs/check > @astrojs/language-server > volar-service-yaml > yaml-language-server > yaml. The CI gate sets --audit-level high, so it does not surface. The bump changed no package versions (the install ran frozen, pnpm-lock.yaml is untouched), so this advisory is identical at 11.10.0. The @tailwindcss/typography peer pin from the last post still holds.

Next upgrade

I will check the release page more often. The three-minors-in-a-week cadence in early July (11.11 on the 9th, 11.12 on the 11th, 11.13 on the 13th) means a monthly check still leaves gaps. The 11.11 memory fix was the one I most wanted, and it shipped a week before I noticed. A weekly glance at the releases page would have caught it when it landed.

References

This post was written with AI assistance.