actions/setup-node: version files, the cache that turns itself on, and publishing
actions/setup-node installs a Node.js version, puts it on PATH, can restore your package manager's global cache, and can write an .npmrc for publishing. Since v6 it caches npm on its own when package.json names npm as the package manager, and since v7 (v7.1.0 is current) it no longer exports a placeholder NODE_AUTH_TOKEN when you set registry-url.
Most workflows carry a setup-node step copied from somewhere else, with a cache: npm line nobody has checked. Then a major version lands and something shifts: caching turns itself on, a pnpm project fails before pnpm is installed, or an OIDC publish breaks on a token nobody set.
Two of those shifts came from caching, whose rules moved twice in two months between v5 and v6, and one from publishing, where v7 stopped exporting a placeholder token. If your workflow predates v6, read the caching section before you bump the pin.

Inputs, and the two new cache outputs
The table is action.yml at the v7.1.0 tag. Four outputs come back: node-version, cache-hit, and, new in v7, cache-primary-key and cache-matched-key, which let a later step see exactly which entry was restored. The cache is saved in a post step that action.yml limits to success(), so a failed job saves nothing for the next run.
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm test| Input | Default | What it does |
|---|---|---|
node-version | None | A version spec: 24, 22.17.1, lts/*, latest. Wins over node-version-file |
node-version-file | None | .nvmrc, .node-version, .tool-versions, mise.toml or package.json |
architecture | The runner's | x86, x64 and so on; ignored with a warning unless node-version is set |
check-latest | false | Look for a newer version than the cached one that still satisfies the spec |
registry-url | None | Writes an .npmrc whose auth line reads NODE_AUTH_TOKEN |
scope | None; the repository owner for npm.pkg.github.com | Scope for a scoped registry |
token | github.token on github.com | Downloads Node builds from actions/node-versions |
cache | None | npm, yarn or pnpm |
package-manager-cache | true | Set false to stop automatic npm caching |
cache-dependency-path | None | Lockfile path, glob or list used for the key |
mirror | None | Alternative source for Node binaries |
mirror-token | None | Sent as the Authorization header to the mirror |
node-version or node-version-file
Set one. If you set both, the action warns "Both node-version and node-version-file inputs are specified, only node-version will be used" and ignores the file. With neither, it uses whatever Node is already on PATH, which changes when the runner image does.
A relative node-version-file is resolved against GITHUB_WORKSPACE, not the step's working directory, and v7.1.0 accepts an absolute path too, so a composite action can pass its own file. For package.json the action reads volta.node first, then a devEngines.runtime entry named node, then engines.node, then follows volta.extends. A file it can read but not parse logs a warning and carries on with no version, which lands you on the PATH Node again; the node version file page shows how to turn that into a failure.
Resolution checks the runner's tool cache for a semver match first, then downloads from actions/node-versions, then from nodejs.org. check-latest: true skips the cached answer when a newer match exists, at the price of a download.
Built-in caching, and when it runs
Three questions decide it. If cache is set, the action caches that package manager, which must already be installed: cache: pnpm before pnpm is on PATH fails the step. If not, and package-manager-cache is left at true, it caches npm only when package.json names npm in devEngines.packageManager or the top-level packageManager field. Otherwise nothing is cached.
What it saves is the package manager's global cache directory, never node_modules, so npm ci still runs and still builds the tree. The key is node-cache-<platform>-<arch>-<manager>-<hash>, built from your lockfile, and the restore is exact: change one dependency and the next run starts cold. The only fallback is for Yarn Berry projects whose dependencies are all managed locally, which may restore an older entry by prefix.
For several lockfiles, point cache-dependency-path at a glob or a list. For what the cache is worth on a real project, speeding up npm install has measured numbers.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: pnpm/action-setup@73e46992e05b19abd31f2f3c20e0e7370d762115 # v6.1.1
with:
version: 10
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: |
apps/web/pnpm-lock.yaml
apps/api/pnpm-lock.yamlCaching rules moved twice in two months
The caching rules moved twice in two months. v5 turned caching on for any packageManager field, and projects that declared pnpm and installed it later with corepack failed in Set up Node.js before corepack ran (issue 1357). v6 narrowed automatic caching to npm.
| Version | Released | What changed | What to do |
|---|---|---|---|
| v4.0.0 | 2023-10-23 | Runtime moved from node16 to node20 | Nothing on hosted runners |
| v5.0.0 | 2025-09-04 | Runtime moved to node24; caching on by default when packageManager is set | Runner v2.327.1 or later on self-hosted machines |
| v6.0.0 | 2025-10-14 | Automatic caching limited to npm; always-auth input removed | Add cache: pnpm or cache: yarn explicitly; delete always-auth |
| v7.0.0 | 2026-07-14 | Placeholder NODE_AUTH_TOKEN removed; cache-primary-key and cache-matched-key outputs; ESM internals | Set NODE_AUTH_TOKEN yourself where a registry needs it |
| v7.1.0 | 2026-10-08 | mise.toml support; absolute node-version-file paths; version validation and manifest fetch retry | Nothing |
registry-url, NODE_AUTH_TOKEN and publishing
action.yml says registry-url writes a project-level .npmrc. The v7.1.0 source writes it to $RUNNER_TEMP/.npmrc and exports NPM_CONFIG_USERCONFIG pointing at it, so your repository's own .npmrc is left alone. The auth line it writes is //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}: the token is read at publish time from the step's environment, so put NODE_AUTH_TOKEN on the step that publishes.
Until v7 the action exported a placeholder NODE_AUTH_TOKEN of XXXXX-XXXXX-XXXXX-XXXXX when you had not set one. v7 removed it, and the README warns that Yarn Classic and older npm may now fail with registry-url set and no token. npm trusted publishing needs no token at all: npm 11.5.1 or newer, id-token: write, and a trusted publisher on npm that matches the repository and workflow exactly, or the publish returns E404.
The setup-node docs set package-manager-cache: false in every publishing example, because a poisoned cache in a job that holds a publish credential can expose it. Do the same.
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
registry-url: https://registry.npmjs.org
package-manager-cache: false
- run: npm ci
- run: npm publishMissing files, missing lockfiles and missing pnpm
The first two have full pages. The rest are one-line fixes. Failures after the step, in the install itself, are a different family: npm ci lockfile out of sync and no pnpm version is specified are the common two.
| Message | Cause | Fix |
|---|---|---|
The specified node version file at: <path> does not exist | The input, joined to GITHUB_WORKSPACE, names no file | Node version file does not exist |
Dependencies lock file is not found in <workspace>. Supported file patterns: package-lock.json,npm-shrinkwrap.json,yarn.lock | cache is set and no lockfile is at the root | Dependencies lock file is not found |
Some specified paths were not resolved, unable to cache dependencies. | cache-dependency-path matched nothing | Fix the glob; it is relative to the workspace |
Unable to find Node version '<spec>' for platform <os> and architecture <arch>. | No published build matches the spec on that platform | Check the spec and the architecture |
Unable to locate executable file: pnpm. | cache: pnpm ran before pnpm was installed | Move the pnpm setup step above setup-node |
Key takeaways
- Set exactly one of
node-versionandnode-version-file; a relative file path resolves againstGITHUB_WORKSPACE. - Automatic caching covers npm only, and only when
package.jsonnames npm; pnpm and yarn needcache:and must be installed first. - The cache key is exact and saved only on success, and it holds the global cache, not
node_modules. - v7 stopped exporting a placeholder
NODE_AUTH_TOKEN: set it on the publish step, or use trusted publishing.
Frequently asked questions
Does actions/setup-node cache node_modules?
~/.npm for npm, keyed on a hash of the lockfile, so npm ci still runs and still builds node_modules. The setup-node docs give the reason: the global cache can be reused across Node versions, so a matrix of three versions can share one entry.Why does setup-node v5 fail immediately when using pnpm?
package.json had a packageManager field, so a project declaring pnpm made the action look for pnpm before any later step had installed it (issue 1357). v6 limited automatic caching to npm. Upgrade, or set package-manager-cache: false on v5.Why does an npm OIDC publish fail after setup-node sets registry-url?
NODE_AUTH_TOKEN=XXXXX-XXXXX-XXXXX-XXXXX when you had not set one, and the issue asking to drop that default (issue 1440) describes having to blank it to get OIDC publishing working. v7.0.0 removed the placeholder. On v7, check the npm version is 11.5.1 or newer and that the trusted publisher matches the workflow file.Why can GITHUB_TOKEN not install private packages from another repository?
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} works for packages in the same organization.Related guides
References
- actions/setup-node action.yml at v7.1.0 (read 2026-11-12)
- actions/setup-node advanced usage: caching, version files, publishing
- actions/setup-node releases
- actions/setup-node issue 1357: v5 fails immediately when using pnpm
- npm docs: trusted publishers
- Node.js documentation
- npm CLI documentation
- GitHub Actions documentation