Skip to content

/flags/pnpm/pnpm-install-frozen-lockfile

pnpm install --frozen-lockfile: never update pnpm-lock.yaml (CI default)

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
pnpm
command
pnpm install
flag
--frozen-lockfile, --no-frozen-lockfile, --prefer-frozen-lockfile
default
true in CI environments, false otherwise

pnpm install --frozen-lockfile installs exactly what pnpm-lock.yaml describes and fails if the lockfile is missing or does not match package.json, instead of updating it. pnpm turns it on automatically when it detects CI (the CI environment variable), which is why a forgotten lockfile change shows up only in the pipeline. --no-frozen-lockfile turns it off.

Local install, CI install and the flag

package.json asks for a version that the lockfile does not contain:

bash
cat > package.json <<'EOF'
{ "name": "demo", "version": "1.0.0", "dependencies": { "is-number": "7.0.0" } }
EOF
npx --yes pnpm@9 install >/dev/null 2>&1
sed -i 's/"7.0.0"/"6.0.0"/' package.json
echo "--- --frozen-lockfile"
npx --yes pnpm@9 install --frozen-lockfile 2>&1 | grep -o "ERR_PNPM_[A-Z_]*.*up to date"
echo "--- CI=true (frozen by default)"
CI=true npx --yes pnpm@9 install 2>&1 | grep -o "ERR_PNPM_[A-Z_]*.*up to date"
echo "--- plain install updates the lockfile"
npx --yes pnpm@9 install >/dev/null 2>&1; echo "exit: $?"
grep -A1 "is-number:" pnpm-lock.yaml | head -2
npx --yes pnpm@9 install --frozen-lockfile >/dev/null 2>&1; echo "frozen again, exit: $?"

Output

--- --frozen-lockfile
ERR_PNPM_OUTDATED_LOCKFILE  Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date
--- CI=true (frozen by default)
ERR_PNPM_OUTDATED_LOCKFILE  Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date
--- plain install updates the lockfile
exit: 0
      is-number:
        specifier: 6.0.0
frozen again, exit: 0

The same error appears with the flag and with CI=true. After a normal install the lockfile records the new version and the frozen install succeeds. The error itself is explained, with every cause, in ERR_PNPM_OUTDATED_LOCKFILE.

OptionBehaviour
--frozen-lockfileNever write pnpm-lock.yaml; fail when it is absent or outdated
--no-frozen-lockfileAllow updates even in CI
--prefer-frozen-lockfileUse the lockfile as-is when it satisfies package.json, otherwise resolve again (the local default)
--fix-lockfileRepair broken entries in the lockfile without resolving everything again
--lockfile-onlyUpdate pnpm-lock.yaml without touching node_modules
frozen-lockfile=true in .npmrcMake frozen installs the default everywhere

A missing lockfile is a separate error with the same root idea: ERR_PNPM_NO_LOCKFILE.

GitHub Actions

yaml
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
  with:
    node-version: 24
    cache: pnpm
- run: pnpm install --frozen-lockfile

The flag is redundant in GitHub Actions, where CI is already set, but writing it makes the intent visible and keeps the behaviour on CI systems that pnpm does not detect.

Pitfalls

  • Different pnpm major versions write different lockfile formats. If CI uses another major version than developers, the lockfile looks outdated; pin pnpm with the packageManager field in package.json.
  • Changing pnpm.overrides, patched dependencies or workspace settings also invalidates the lockfile, not only dependencies.
  • --frozen-lockfile does not check that installed packages are up to date on the registry; it only enforces what was committed.

Yarn, Bun and Deno have the same safety switch: see yarn install --frozen-lockfile and deno install --frozen.

FAQ

Why does pnpm install fail in CI but work on my machine?

In CI, frozen-lockfile is on by default, so pnpm refuses to change pnpm-lock.yaml and reports ERR_PNPM_OUTDATED_LOCKFILE. Locally it is off and pnpm silently updates the lockfile. Run pnpm install locally and commit pnpm-lock.yaml.

How do I turn off frozen-lockfile in CI?

Pass --no-frozen-lockfile. It is rarely the right fix: the build then resolves versions that were never committed. Use it only for jobs that are meant to update the lockfile, such as a dependency update bot.

What is the difference between --frozen-lockfile and npm ci?

Both refuse to change the lockfile. npm ci also deletes node_modules first; pnpm keeps node_modules and only links what is missing, which is why it is faster on warm CI caches.

Sources