pnpm 11.0
pnpm 11 is here! This release tightens the security defaults introduced throughout the v10 cycle, drops the npm CLI fallback for publishing in favor of a native implementation, replaces the JSON-per-package store index with a single SQLite database, and isolates global installs so they no longer interfere with each other.
It also requires Node.js 22 or newer — pnpm itself is now pure ESM.
Upgrading from v10? See the Migrating from v10 to v11 guide. Most config changes are mechanical and can be applied by the pnpm-v10-to-v11 codemod.
Highlights
- Node.js 22+ required. Support for Node 18, 19, 20, and 21 is dropped. The standalone executable requires glibc 2.27 or newer.
- Supply-chain protection on by default.
minimumReleaseAgedefaults to1440(1 day) andblockExoticSubdepsdefaults totrue. allowBuildsreplaces the legacy build-dependency settings.onlyBuiltDependencies,onlyBuiltDependenciesFile,neverBuiltDependencies,ignoredBuiltDependencies, andignoreDepScriptsare gone.- Global installs are isolated and use the global virtual store by default. Each
pnpm add -ggets its own directory with its ownpackage.json,node_modules, and lockfile. - New SQLite-backed store index (store v11), with bundled manifests and hex digests for fewer syscalls and faster installs.
- Native publish flow.
pnpm publish,login,logout,view,deprecate,unpublish,dist-tag, andversionno longer delegate to the npm CLI. .npmrcis auth/registry only. Other settings must live inpnpm-workspace.yamlor the new globalconfig.yaml. Environment variables use thepnpm_config_*prefix.
Breaking changes
Requirements
- Drops Node.js 18, 19, 20, and 21.
- pnpm is now distributed as pure ESM.
- The standalone exe requires glibc 2.27+.
Security & build defaults
Several defaults have flipped to safer values:
| Setting | New default |
|---|---|
minimumReleaseAge | 1440 (1 day) |
minimumReleaseAgeStrict | false |
blockExoticSubdeps | true |
strictDepBuilds | true |
optimisticRepeatInstall | true |
verifyDepsBeforeRun | install |
Newly published packages won't be resolved until they're at least 1 day old. To opt out, set minimumReleaseAge: 0 in pnpm-workspace.yaml.
allowBuilds replaces the old build settings
onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts have all been removed. Use allowBuilds instead — a map from package name patterns to booleans:
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Also removed: allowNonAppliedPatches (use allowUnusedPatches) and ignorePatchFailures (patch failures now throw).
.npmrc is auth/registry only
pnpm no longer reads non-auth settings from .npmrc. Configuration is split into two categories:
- Registry and auth settings — INI files (
.npmrc, the globalrcfile,~/.config/pnpm/auth.ini). - pnpm-specific settings — YAML files (
pnpm-workspace.yaml, the new global~/.config/pnpm/config.yaml).
Other related changes:
-
pnpm no longer reads
npm_config_*environment variables. Usepnpm_config_*instead (e.g.pnpm_config_registry). -
pnpm no longer reads the
pnpmfield inpackage.json. -
pnpm no longer reads npm's global config at
$PREFIX/etc/npmrc. -
Network settings (
httpProxy,httpsProxy,noProxy,localAddress,strictSsl,gitShallowHosts) are now written toconfig.yaml/pnpm-workspace.yaml(still readable from.npmrcto ease migration). -
A new
registriessetting inpnpm-workspace.yamlreplaces@scope:registry=lines:registries:
default: https://registry.npmjs.org/
"@my-org": https://private.example.com/
"@internal": https://nexus.corp.com/ -
Per-project
.npmrcis replaced bypackageConfigsinpnpm-workspace.yaml:packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Native publish flow, no more npm CLI fallback
Commands previously implemented by passing through to the npm CLI have either been reimplemented natively or removed.
Reimplemented: publish, view (info, show, v), login (adduser), logout, deprecate, unpublish, dist-tag, version, search, star/unstar/stars, whoami, ping, docs/home.
Removed (now throw "not implemented"): access, bugs, edit, issues, owner, prefix, profile, pkg, repo, set-script, team, token, xmas.
A few notes on the new native pnpm publish:
- The OTP environment variable is now
PNPM_CONFIG_OTP(wasNPM_CONFIG_OTP). - If the registry asks for OTP and none is provided, pnpm prompts for it interactively.
- Web-based authentication shows a scannable QR code and URL.
pnpm audit uses the bulk advisories endpoint
The legacy /-/npm/v1/security/audits{,/quick} endpoints have been retired by the registry. pnpm audit now calls /-/npm/v1/security/advisories/bulk, which doesn't return CVE identifiers — so CVE-based filtering has been replaced with GHSA-based filtering:
auditConfig.ignoreCves→auditConfig.ignoreGhsaspnpm audit --ignore <id>and--ignore-unfixableread and write GHSAs
To migrate, replace each CVE-YYYY-NNNNN entry under ignoreCves with the matching GHSA-xxxx-xxxx-xxxx (visible in the More info column of pnpm audit) and move it to ignoreGhsas.
Isolated, global-virtual-store global installs
pnpm add -g <pkg> and pnx now use the global virtual store, and each install gets its own isolated directory at {pnpmHomeDir}/global/v11/{hash}/ with its own package.json, node_modules/, and lockfile. This stops global packages from interfering with each other through peer-dependency conflicts, hoisting changes, or version drift.
pnpm remove -g <pkg>removes the entire installation group containing the package.pnpm update -g [pkg]re-installs into a new isolated directory.pnpm list -gscans isolated directories.pnpm install -g(no args) is no longer supported — usepnpm add -g <pkg>.
Globally installed binaries now live in a bin subdirectory of PNPM_HOME (rather than directly in PNPM_HOME), so internal directories like global/ and store/ don't pollute shell autocompletion. Run pnpm setup after upgrading to update your shell configuration.
pnpm link has been tightened too: only relative or absolute paths are accepted, --global is removed (use pnpm add -g .), and pnpm link with no arguments is removed.
Other removals
-
pnpm server. -
useNodeVersionandexecutionEnv.nodeVersion— usedevEngines.runtime/engines.runtime. -
hooks.fetchers— replaced by the newfetchersfield in pnpmfile. -
managePackageManagerVersions,packageManagerStrict, andpackageManagerStrictVersion. These all derived theonFailbehavior of the legacypackageManagerfield; the newpmOnFailsetting subsumes them:Removed Replace with managePackageManagerVersions: truepmOnFail: download(default)managePackageManagerVersions: falsepmOnFail: ignorepackageManagerStrict: falsepmOnFail: warnpackageManagerStrictVersion: truepmOnFail: errorCOREPACK_ENABLE_STRICT=0pmOnFail: warn
New commands
| Komut | What it does |
|---|---|
pnpm ci | Runs pnpm clean followed by pnpm install --frozen-lockfile. Aliases: clean-install, ic, install-clean. |
pnpm clean | Removes node_modules from all workspace projects. --lockfile also removes pnpm-lock.yaml. |
pnpm sbom | Generates a Software Bill of Materials in CycloneDX 1.7 or SPDX 2.3 JSON. |
pnpm peers check | Reports unmet/missing peers from the lockfile. |
pnpm runtime set | Installs a runtime; deprecates pnpm env use. |
pnpm docs / home | Opens the package homepage. |
pnpm ping | Pings the registry. |
pnpm with | Runs pnpm at a specific (or current) version for a single invocation, bypassing packageManager pins. |
pnpm pack-app | Packs a CommonJS entry into a standalone executable for one or more target platforms via Node.js SEA. |
Plus the natively reimplemented commands listed under "Native publish flow" above, and short aliases pn for pnpm and pnx for pnpx/pnpm dlx.
pnpm audit --fix=update
Fix vulnerabilities by updating packages in the lockfile instead of adding overrides. For more granular control, the new --interactive / -i flag lets you select which advisories to fix:
pnpm audit --fix=update --interactive
pnpm audit --fix also adds the minimum patched version for each advisory to minimumReleaseAgeExclude in pnpm-workspace.yaml, so security fixes can be installed without waiting for minimumReleaseAge.
ESM pnpmfiles
Pnpmfiles can now be written in ESM, using the .mjs extension. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs, and only one is loaded.
Store v11
The store has been rebuilt around two ideas: read less, do fewer syscalls.
- The package index is now a single SQLite database at
$STORE/index.db(with MessagePack values, WAL mode for concurrent access), instead of millions of JSON files under$STORE/index/. Packages missing from the new index are re-fetched on demand. - The bundled manifest (name, version, bin, engines, scripts, etc.) is stored directly in the package index, eliminating the need to read
package.jsonfrom the CAS during resolution and installation. - Index entries store hex digests instead of full integrity strings (
<algo>-<digest>), and the hash algorithm is recorded once per file instead of per entry. This avoids base64 → hex conversion on every CAS path lookup. - When the global virtual store is enabled, packages that aren't allowed to build (and don't transitively depend on packages that are) get hashes that don't include the engine name (platform, arch, Node.js major). ~95% of GVS packages now survive Node.js upgrades and architecture changes without re-import.
Performance
A lot of small wins add up to a noticeably faster install:
undicireplacesnode-fetchfor all HTTP, with Happy Eyeballs (dual-stack), better keep-alive, and an optimized global dispatcher.- Tarball downloads with known size pre-allocate memory to avoid double-copy overhead.
- The metadata cache is now NDJSON, with
If-Modified-Sincefor conditional fetches. minimumReleaseAgechecks use the abbreviated metadata endpoint to fetch less.- CAS files are written directly to their content-addressed path instead of via a temp file + rename — saving ~30k rename syscalls per cold install.
- The staging directory is gone when importing into
node_modules. - Hot-path string operations in the CAS were tightened, and
gunzipSyncruns with a larger chunk size for fewer buffer allocations during tarball decompression. - During GVS warm reinstalls, redundant internal linking is skipped when no packages were added.
Slimmer runtime installs
Installing a Node.js runtime via node@runtime:<version> (including pnpm env use and pnpm runtime set node) no longer extracts the bundled npm, npx, and corepack from the Node.js archive. That cuts roughly half of the files pnpm has to hash, write to the CAS, and link. If you still need npm, install it as a separate package.
Other notable changes
-
Cleaner script output.
pnpmnow prints$ command(to stderr, so stdout stays pipe-friendly) instead of> pkg@version stage path\n> command. Project name and path are shown only when running in a different directory. -
Peer dependency issues are no longer rendered as a tree during install — pnpm suggests running
pnpm peers checkto view them. -
Lifecycle scripts no longer get
npm_config_*env vars from the pnpm config; only well-knownnpm_*env vars are set, matching Yarn. -
pnpm initwritesdevEngines.packageManagerinstead ofpackageManagerwheninit-package-manageris enabled, and the defaulttypeis now"module". -
devEngines.packageManagernow supports version ranges; the resolved version is stored inpnpm-lock.yamland reused if it still satisfies the range. -
dedupePeersis a new setting that uses version-only suffixes (name@version) instead of full dep paths, eliminating nested suffixes like(foo@1.0.0(bar@2.0.0))for projects with many recursive peers. -
pnpm approve-buildsnow accepts positional arguments for non-interactive use; prefix a name with!to deny it. -
Hidden scripts — scripts starting with
.can only be called from other scripts and don't show up inpnpm run. -
-Fis a new short alias for--filter. -
pnpm addshort flags —-dis now--save-dev,-pis--save-prod,-ois--save-optional,-eis--save-exact(only insidepnpm add). -
virtualStoreOnlypopulates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. Useful for pre-populating a store in Nix builds.pnpm fetchnow uses this internally. -
nodeDownloadMirrorsinpnpm-workspace.yamlreplaces thenode-mirror:<channel>.npmrcsetting:nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/ -
Config dependencies are now installed into
{storeDir}/links/and symlinked intonode_modules/.pnpm-config/, so they're shared across projects using the same store. Resolved versions and integrity hashes have moved frompnpm-workspace.yamlto a separate document inpnpm-lock.yaml; old inline-hash projects are migrated automatically.
Upgrading
See the full Migrating from v10 to v11 guide for the codemod and manual follow-ups. The short version:
- Bump your CI and dev environments to Node.js 22+ before upgrading.
- Move pnpm settings out of
.npmrcintopnpm-workspace.yaml(or the global~/.config/pnpm/config.yaml). - Migrate
onlyBuiltDependenciesand friends toallowBuilds. - Migrate
auditConfig.ignoreCvestoauditConfig.ignoreGhsas. - After installing v11, run
pnpm setupto update your shell so the newbinsubdirectory is onPATH.
The full list of changes is in the changelog. If something you relied on is missing or broken, please open an issue at github.com/pnpm/pnpm/issues.
