PageSourceSearch

https://pnpm.io/assets/js/e6d1b2f9.6aba4381.js

js pnpm.io collected 2026-09-24 07:23:38 UTC 6,620 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["23349"],{27763(e,n,o){o.r(n),o.d(n,{metadata:()=>i,default:()=>d,frontMatter:()=>c,contentTitle:()=>s,toc:()=>p,assets:()=>l});var i=JSON.parse('{"id":"cli/fetch","title":"pnpm fetch","description":"Fetch packages from a lockfile into virtual store, package manifest is ignored.","source":"@site/versioned_docs/version-12.x/cli/fetch.md","sourceDirName":"cli","slug":"/cli/fetch","permalink":"/cli/fetch","draft":false,"unlisted":false,"editUrl":"https://github.com/pnpm/pnpm.io/edit/main/docs/cli/fetch.md","tags":[],"version":"12.x","lastUpdatedBy":null,"lastUpdatedAt":null,"frontMatter":{"id":"fetch","title":"pnpm fetch"},"sidebar":"docs","previous":{"title":"pnpm ci","permalink":"/cli/ci"},"next":{"title":"pnpm install-test","permalink":"/cli/install-test"}}'),t=o(91987),a=o(67008);let c={id:"fetch",title:"pnpm fetch"},s,l={},p=[{value:"Usage scenario",id:"usage-scenario",level:2},{value:"Options",id:"options",level:2},{value:"--dev, -D",id:"--dev--d",level:3},{value:"--prod, -P",id:"--prod--p",level:3}];function r(e){let n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"Fetch packages from a lockfile into virtual store, package manifest is ignored."}),"\n",(0,t.jsx)(n.h2,{id:"usage-scenario",children:"Usage scenario"}),"\n",(0,t.jsx)(n.p,{children:"This command is specifically designed to improve building a docker image."}),"\n",(0,t.jsxs)(n.p,{children:["You may have read the ",(0,t.jsx)(n.a,{href:"https://github.com/nodejs/docker-node#readme",children:"official guide"})," to writing a Dockerfile for a Node.js\napp, if you haven't read it yet, you may want to read it first."]}),"\n",(0,t.jsx)(n.p,{children:"From that guide, we learn to write an optimized Dockerfile for projects using\npnpm, which looks like"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-Dockerfile",children:'FROM ghcr.io/pnpm/pnpm:12\n\nRUN pnpm runtime set node 22 -g\n\nWORKDIR /app\n\n# Files required by pnpm install\nCOPY package.json pnpm-lock.yaml pnpm-workspace.yaml .pnpmfile.mjs ./\n\n# If you patched any package, include patches before install too\nCOPY patches patches\n\nRUN pnpm install --frozen-lockfile --prod\n\n# Bundle app source\nCOPY . .\n\nEXPOSE 8080\nCMD [ "node", "server.js" ]\n'})}),"\n",(0,t.jsxs)(n.p,{children:["As long as there are no changes to ",(0,t.jsx)(n.code,{children:"package.json"}),", ",(0,t.jsx)(n.code,{children:"pnpm-lock.yaml"}),", ",(0,t.jsx)(n.code,{children:"pnpm-workspace.yaml"}),",\n",(0,t.jsx)(n.code,{children:".pnpmfile.mjs"}),", docker build cache is still valid up to the layer of\n",(0,t.jsx)(n.code,{children:"RUN pnpm install --frozen-lockfile --prod"}),", which cost most of the time\nwhen building a docker image."]}),"\n",(0,t.jsxs)(n.p,{children:["However, modification to ",(0,t.jsx)(n.code,{children:"package.json"})," may happen much more frequently than\nwe expect, because it does not only contain dependencies, but may also\ncontain the version number, scripts, and arbitrary configuration for any other\ntool."]}),"\n",(0,t.jsx)(n.p,{children:"It's also hard to maintain a Dockerfile that builds a monorepo project, it may\nlook like"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-Dockerfile",children:'FROM ghcr.io/pnpm/pnpm:12\n\nRUN pnpm runtime set node 22 -g\n\nWORKDIR /app\n\n# Files required by pnpm install\nCOPY package.json pnpm-lock.yaml pnpm-workspace.yaml .pnpmfile.mjs ./\n\n# If you patched any package, include patches before install too\nCOPY patches patches\n\n# for each sub-package, we have to add one extra step to copy its manifest\n# to the right place, as docker have no way to filter out only package.json with\n# single instruction\nCOPY packages/foo/package.json packages/foo/\nCOPY packages/bar/package.json packages/bar/\n\nRUN pnpm install --frozen-lockfile --prod\n\n# Bundle app source\nCOPY . .\n\nEXPOSE 8080\nCMD [ "node", "server.js" ]\n\n'})}),"\n",(0,t.jsx)(n.p,{children:"As you can see, the Dockerfile has to be updated when you add or remove\nsub-packages."}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:"pnpm fetch"})," solves the above problem perfectly by providing the ability\nto load packages into the virtual store using only information from a lockfile and a configuration file (",(0,t.jsx)(n.code,{children:"pnpm-workspace.yaml"}),")."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-Dockerfile",children:'FROM ghcr.io/pnpm/pnpm:12\n\nRUN pnpm runtime set node 22 -g\n\nWORKDIR /app\n\n# pnpm fetch does require only lockfile\nCOPY pnpm-lock.yaml pnpm-workspace.yaml ./\n\n# If you patched any package, include patches before running pn
1pm fetch\nCOPY patches patches\n\nRUN pnpm fetch --prod\n\n\nCOPY . ./\nRUN pnpm install -r --offline --prod\n\n\nEXPOSE 8080\nCMD [ "node", "server.js" ]\n'})}),"\n",(0,t.jsxs)(n.p,{children:["It works for both simple and monorepo projects, ",(0,t.jsx)(n.code,{children:"--offline"})," enforces\npnpm not to communicate with the package registry as all needed packages are\nalready present in the virtual store."]}),"\n",(0,t.jsxs)(n.p,{children:["As long as the lockfile is not changed, the build cache is valid up to the\nlayer, so ",(0,t.jsx)(n.code,{children:"RUN pnpm install -r --offline --prod"}),", will save you much\ntime."]}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["Local ",(0,t.jsx)(n.code,{children:"file:"})," protocol dependencies are skipped during ",(0,t.jsx)(n.code,{children:"pnpm fetch"}),", since they reference directories that may not be available at fetch time (e.g. in Docker builds)."]})}),"\n",(0,t.jsx)(n.h2,{id:"options",children:"Options"}),"\n",(0,t.jsx)(n.h3,{id:"--dev--d",children:"--dev, -D"}),"\n",(0,t.jsx)(n.p,{children:"Only development packages will be fetched"}),"\n",(0,t.jsx)(n.h3,{id:"--prod--p",children:"--prod, -P"}),"\n",(0,t.jsx)(n.p,{children:"Development packages will not be fetched"})]})}function d(e={}){let{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(r,{...e})}):r(e)}},67008(e,n,o){o.d(n,{R:()=>c,x:()=>s});var i=o(71763);let t={},a=i.createContext(t);function c(e){let n=i.useContext(a);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:c(e.components),i.createElement(a.Provider,{value:n},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.