1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["31461"],{61991(e,t,s){s.r(t),s.d(t,{metadata:()=>o,default:()=>h,frontMatter:()=>l,contentTitle:()=>a,toc:()=>d,assets:()=>i});var o=JSON.parse('{"id":"global-virtual-store","title":"Global Virtual Store","description":"By default, pnpm creates a .pnpm directory inside each project\'s node_modules \u2014 this is the \\"virtual store\\". It contains hardlinks to files in the content-addressable store. Every project gets its own projection of this virtual store \u2014 pnpm hardlinks files from the content-addressable store into the .pnpm directory structure. The actual file contents exist only once on disk, but the directory structure is recreated for each project so that Node.js\'s module resolution algorithm can find the right dependencies for each package.","source":"@site/versioned_docs/version-11.x/global-virtual-store.md","sourceDirName":".","slug":"/global-virtual-store","permalink":"/11.x/global-virtual-store","draft":false,"unlisted":false,"editUrl":"https://github.com/pnpm/pnpm.io/edit/main/versioned_docs/version-11.x/global-virtual-store.md","tags":[],"version":"11.x","lastUpdatedBy":"Zoltan Kochan","lastUpdatedAt":1788556519000,"frontMatter":{"id":"global-virtual-store","title":"Global Virtual Store"},"sidebar":"docs","previous":{"title":"Global Packages","permalink":"/11.x/global-packages"},"next":{"title":"Release management","permalink":"/11.x/versioning"}}'),n=s(91987),r=s(67008);let l={id:"global-virtual-store",title:"Global Virtual Store"},a,i={},d=[{value:"Default behavior vs global virtual store",id:"default-behavior-vs-global-virtual-store",level:2},{value:"Default (per-project virtual store)",id:"default-per-project-virtual-store",level:3},{value:"With global virtual store",id:"with-global-virtual-store",level:3},{value:"How package identity works",id:"how-package-identity-works",level:2},{value:"When to use it",id:"when-to-use-it",level:2},{value:"Limitations",id:"limitations",level:2},{value:"Global packages",id:"global-packages",level:2},{value:"Configuration",id:"configuration",level:2}];function c(e){let t={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(t.p,{children:["By default, pnpm creates a ",(0,n.jsx)(t.code,{children:".pnpm"})," directory inside each project's ",(0,n.jsx)(t.code,{children:"node_modules"}),' \u2014 this is the "virtual store". It contains hardlinks to files in the ',(0,n.jsx)(t.a,{href:"/11.x/settings/store#storedir",children:"content-addressable store"}),". Every project gets its own projection of this virtual store \u2014 pnpm hardlinks files from the content-addressable store into the ",(0,n.jsx)(t.code,{children:".pnpm"})," directory structure. The actual file contents exist only once on disk, but the directory structure is recreated for each project so that Node.js's module resolution algorithm can find the right dependencies for each package."]}),"\n",(0,n.jsxs)(t.p,{children:["The ",(0,n.jsx)(t.strong,{children:"global virtual store"})," (",(0,n.jsx)(t.code,{children:"virtualStoreType: global"}),", spelled ",(0,n.jsx)(t.code,{children:"enableGlobalVirtualStore: true"})," before v11.23.0) changes this. Instead of each project having its own ",(0,n.jsx)(t.code,{children:"node_modules/.pnpm"})," directory, pnpm maintains a single shared virtual store (located at ",(0,n.jsx)(t.code,{children:"<store-path>/links/"}),", run ",(0,n.jsx)(t.code,{children:"pnpm store path"})," to find ",(0,n.jsx)(t.code,{children:"<store-path>"}),"). Each project's ",(0,n.jsx)(t.code,{children:"node_modules"})," contains only symlinks pointing into this shared location."]}),"\n",(0,n.jsx)(t.h2,{id:"default-behavior-vs-global-virtual-store",children:"Default behavior vs global virtual store"}),"\n",(0,n.jsx)(t.h3,{id:"default-per-project-virtual-store",children:"Default (per-project virtual store)"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{children:"project-a/\n\u2514\u2500\u2500 node_modules/\n \u251C\u2500\u2500 lodash \u2192 .pnpm/[email protected]/node_modules/lodash\n \u2514\u2500\u2500 .pnpm/\n \u2514\u2500\u2500 [email protected]/\n \u2514\u2500\u2500 node_modules/\n \u2514\u2500\u2500 lodash/ \u2190 hardlinks to content-addressable store\nproject-b/\n\u2514\u2500\u2500 node_modules/\n \u251C\u2500\u2500 lodash \u2192 .pnpm/[email protected]/node_modules/lodash\n \u2514\u2500\u2500 .pnpm/\n \u2514\u2500\u2500 [email protected]/\n \u2514\u2500\u2500 node_modules/\n \u2514\u2500\u2500 lodash/ \u2190 same hardlinks, duplicated directory structure\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Each project has its own ",(0,n.jsx)(t.code,{children:".pnpm"})," with hardlinks. The file contents aren't duplicated on disk (hardlinks share inodes), but the directory structure is. With large monorepos or many parallel checkouts, the time spent creating thousands of hardlinks during ",(0,n.jsx)(t.code,{children:"pnpm install"})," adds up."]}),"\n",(0,n.jsx)(t.h3,{id:"with-global-virtual-store",children:"With global virtual store"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{children:"project-a/\n\u2514\u2500\u2500 node_modules/\n \u2514\u2500\u2500 lodash \u2192 <global-store>/links/@/lodash/4.17.21/<hash>/node_modules/lodash\nproject-b/\n\u2514\u2500\u2500 node_modules/\n \u2514\u2500\u2500 lodash \u2192 <global-store>/links/@/lodash/4.17.21/<hash>/node_modules/lodash \u2190 same target\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Both projects symlink directly to the same location in the global virtual store. There's no per-project ",(0,n.jsx)(t.code,{children:".pnpm"})," directory. The global virtual store itself contains the hardlinks to the content-addressable store \u2014 but that happens only once per dependency graph (more on that below), not per project."]}),"\n",(0,n.jsx)(t.h2,{id:"how-package-identity-works",children:"How package identity works"}),"\n",(0,n.jsxs)(t.p,{children:["In the global virtual store, each package directory is named by the hash of its dependency graph. Two projects that use ",(0,n.jsx)(t.code,{children:"[email protected]"})," with the same transitive dependency tree will point to the exact same directory. If the dependency trees differ (e.g., different peer dependencies), pnpm creates separate entr
1ies. This is conceptually similar to how ",(0,n.jsx)(t.a,{href:"https://nixos.org/guides/how-nix-works/",children:"NixOS manages packages"})," using dependency graph hashes."]}),"\n",(0,n.jsx)(t.h2,{id:"when-to-use-it",children:"When to use it"}),"\n",(0,n.jsxs)(t.p,{children:["The global virtual store is most useful when you have multiple checkouts of the same project on disk \u2014 for example, when using ",(0,n.jsx)(t.a,{href:"/11.x/git-worktrees",children:"git worktrees for multi-agent development"}),". In that scenario, each worktree gets a nearly free ",(0,n.jsx)(t.code,{children:"node_modules"})," because all the real package content already exists in the shared store."]}),"\n",(0,n.jsx)(t.p,{children:"It also speeds up installations across unrelated projects on the same machine, since any package version that's already been installed by any project is available instantly."}),"\n",(0,n.jsx)(t.h2,{id:"limitations",children:"Limitations"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"CI environments"}),": In CI, caches are typically absent, so there's no warm global store to benefit from. The global virtual store is generally not useful in CI."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"Shared trust domain"}),": The global virtual store and the content-addressable store are shared writable state. Use them only for projects, users, and jobs that trust each other, and protect the store path with filesystem permissions."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.strong,{children:"ESM hoisting"}),": pnpm uses the ",(0,n.jsx)(t.code,{children:"NODE_PATH"})," environment variable to support hoisted dependencies with the global virtual store, and Node.js does not respect ",(0,n.jsx)(t.code,{children:"NODE_PATH"})," for ESM imports. Since v11.23.0, every process pnpm spawns for the project \u2014 ",(0,n.jsx)(t.code,{children:"pnpm run"}),", ",(0,n.jsx)(t.code,{children:"pnpm exec"}),", lifecycle scripts, and the tools ",(0,n.jsx)(t.code,{children:"pnpm dlx"})," runs \u2014 also gets a ",(0,n.jsx)(t.code,{children:"NODE_OPTIONS"})," ",(0,n.jsx)(t.code,{children:"--import"})," flag registering a resolve hook that restores those lookups, so a dependency importing a package it does not declare resolves under ESM too (",(0,n.jsx)(t.a,{href:"https://github.com/pnpm/pnpm/issues/9618",children:"#9618"}),"). A ",(0,n.jsx)(t.code,{children:"node"})," process started outside pnpm does not get that environment, and setting ",(0,n.jsx)(t.a,{href:"/11.x/settings/other#extendnodepath",children:(0,n.jsx)(t.code,{children:"extendNodePath"})})," to ",(0,n.jsx)(t.code,{children:"false"})," turns the whole ",(0,n.jsx)(t.code,{children:"NODE_PATH"})," mechanism off, resolve hook included; declare the missing dependencies with ",(0,n.jsx)(t.a,{href:"/11.x/settings/dependency-resolution#packageextensions",children:"packageExtensions"})," if you need them to resolve in either case."]}),"\n"]}),"\n",(0,n.jsx)(t.admonition,{type:"note",children:(0,n.jsxs)(t.p,{children:["The global virtual store is currently disabled by default for project installs and marked as experimental, as some tools may not work correctly with symlinked ",(0,n.jsx)(t.code,{children:"node_modules"}),". You need to explicitly set ",(0,n.jsx)(t.code,{children:"virtualStoreType: global"})," in ",(0,n.jsx)(t.code,{children:"pnpm-workspace.yaml"})," to use it for project installs. In pnpm v11, the global virtual store is enabled by default for packages installed via ",(0,n.jsx)(t.code,{children:"pnpm dlx"})," (",(0,n.jsx)(t.code,{children:"pnpx"}),") and globally installed packages. The goal is to enable it by default for all installations in a future version."]})}),"\n",(0,n.jsx)(t.h2,{id:"global-packages",children:"Global packages"}),"\n",(0,n.jsxs)(t.p,{children:["In pnpm v11, global installs (",(0,n.jsx)(t.code,{children:"pnpm add -g"}),") and ",(0,n.jsx)(t.code,{children:"pnpm dlx"})," use the global virtual store by default. See ",(0,n.jsx)(t.a,{href:"/11.x/global-packages",children:"Global Packages"})," for the full guide on how global package management works in v11, including isolated installations and the new binaries location."]}),"\n",(0,n.jsx)(t.h2,{id:"configuration",children:"Configuration"}),"\n",(0,n.jsxs)(t.p,{children:["See the ",(0,n.jsx)(t.a,{href:"/11.x/settings/node-modules#virtualstoretype",children:(0,n.jsx)(t.code,{children:"virtualStoreType"})}
1)," setting reference for all configuration details. ",(0,n.jsx)(t.code,{children:"virtualStoreType"})," was added in v11.23.0 as the canonical spelling of ",(0,n.jsx)(t.a,{href:"/11.x/settings/node-modules#enableglobalvirtualstore",children:(0,n.jsx)(t.code,{children:"enableGlobalVirtualStore"})}),", which keeps working."]})]})}function h(e={}){let{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(c,{...e})}):c(e)}},67008(e,t,s){s.d(t,{R:()=>l,x:()=>a});var o=s(71763);let n={},r=o.createContext(n);function l(e){let t=o.useContext(r);return o.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:l(e.components),o.createElement(r.Provider,{value:t},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.