GitHub Actions workflows can depend on Node.js even when the application itself is not written in JavaScript or TypeScript.
A .NET, Java, Python, Go, or C++ repository can use JavaScript-based GitHub Actions internally. Those actions may depend on a particular Node.js runtime, which means a workflow can have a Node dependency that is easy to miss during normal application development.
This becomes important when GitHub changes the Node.js runtime used by its action infrastructure or when an action version requires a newer runtime.
The first step in handling such a change is finding where Node 20 is actually being used.
This article shows how to identify Node 20 dependencies in GitHub Actions workflows, distinguish action runtimes from application runtimes, troubleshoot old actions, and build a practical migration checklist.
Why Node 20 Dependencies Can Be Hard to Find
A simple search for node-version: 20 is not enough.
Consider this workflow:
name: Build
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- run: dotnet restore
- run: dotnet test
There is no explicit Node.js version in the YAML.
However, some of the actions used by the workflow are JavaScript actions and have their own runtime requirements.
This creates two different types of Node dependency:
Node.js used by application commands
+
Node.js used internally by GitHub Actions
These dependencies need to be investigated separately.
The Three Places to Look
When searching for Node 20, check at least these areas:
GitHub Actions workflow files
Action implementations and versions
Application and build configuration
Your repository may contain:
.github/workflows/
package.json
.nvmrc
.tool-versions
Dockerfile
docker-compose.yml
build scripts
A complete search should cover all of them.
Step 1 - Find All Workflow Files
GitHub Actions workflows are normally stored in:
.github/workflows/
A repository may contain several files:
.github/
└── workflows/
├── build.yml
├── test.yml
├── release.yml
├── deployment.yml
└── security.yml
Do not inspect only the main build workflow.
A deployment or release workflow may contain an older action that is not used during normal development.
On Linux or macOS, you can list workflow files with:
find .github/workflows -type f
On PowerShell:
Get-ChildItem .github/workflows -Recurse
Step 2 - Search for Explicit Node Versions
Start with the obvious references.
On Linux or macOS:
grep -Rni "node-version" .github/workflows
You can also search for:
grep -Rni "node20" .github/workflows
On PowerShell:
Get-ChildItem .github/workflows -Recurse |
Select-String -Pattern "node-version|node20"
You might find:
- uses: actions/setup-node@v4
with:
node-version: 20
This is an explicit application-level Node dependency.
It means commands such as:
- run: npm ci
- run: npm test
will use the configured Node environment.
Step 3 - Search for setup-node
Search for:
grep -Rni "setup-node" .github/workflows
A workflow might contain:
- uses: actions/setup-node@v4
with:
node-version: 20
This is straightforward to identify.
But setup-node is not the only place where Node.js can appear.
A workflow can use Node through an action without explicitly configuring a Node version.
Step 4 - Find Every uses: Entry
Search all workflows for:
uses:
For example:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/setup-dotnet@v4
- uses: docker/setup-buildx-action@v3
Create an inventory:
Action | Version | Purpose | Node Dependency |
|---|---|---|---|
| v4 | Repository checkout | JavaScript action |
| v4 | Configure Node | Explicit |
| v4 | Configure .NET | JavaScript action |
Buildx action | v3 | Docker build setup | Check implementation |
The exact runtime requirement should be verified against the action version currently used.
Do not infer the runtime only from the action name.
Step 5 - Understand JavaScript Actions
A JavaScript GitHub Action commonly contains an action.yml file.
A simplified example looks like:
name: Example Action
runs:
using: node20
main: dist/index.js
The important value is:
using: node20
This means the action expects the Node 20 runtime provided for GitHub Actions.
An older action could instead contain:
runs:
using: node16
main: dist/index.js
This is an action-level runtime dependency.
It is different from:
- uses: actions/setup-node@v4
with:
node-version: 20
The second example configures Node for commands that run later in the job.
Step 6 - Inspect Third-Party Actions
Third-party actions deserve special attention.
Consider:
- uses: company/deployment-action@v1
You should check the referenced action's implementation or documentation to determine which runtime it uses.
If it is a JavaScript action, inspect its action.yml or action.yaml.
For example:
runs:
using: node20
main: dist/index.js
If the action still uses an older runtime, check whether a newer maintained version exists.
Do not update it blindly.
A major version can change inputs, outputs, behavior, permissions, or other requirements.
Step 7 - Search the Whole Repository
After checking workflows, search the entire repository for Node 20 references.
On Linux or macOS:
grep -Rni --exclude-dir=.git "node:20\|node-version.*20\|node20" .
On PowerShell:
Get-ChildItem -Recurse -File |
Select-String -Pattern "node:20|node-version.*20|node20"
You may discover references in:
package.json
Dockerfile
.nvmrc
.tool-versions
CI scripts
Build scripts
Documentation
Development containers
Not every match requires a change.
The purpose of the search is to understand where Node 20 is being used.
Step 8 - Check package.json
For Node-based projects, inspect:
package.json
You may find an engines declaration:
{
"engines": {
"node": ">=20"
}
}
Or a project may use dependencies that require a particular Node version.
You can also check the package manager configuration.
For example:
.npmrc
package-lock.json
yarn.lock
pnpm-lock.yaml
These files can provide additional information about the build environment.
Step 9 - Check .nvmrc
Some projects specify their development Node version using:
.nvmrc
For example:
20
This usually controls the local development environment rather than the internal runtime of GitHub Actions.
A workflow may still use:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
In that case, the workflow explicitly inherits the Node version from the repository configuration.
This creates a dependency between the workflow and .nvmrc.
Step 10 - Check Dockerfiles
Node versions are frequently hidden inside container definitions.
For example:
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
This is an application build dependency.
Changing the Node runtime used by GitHub Actions does not automatically mean this Docker image should change.
The correct question is:
Does the application support another Node version?
That needs to be answered independently.
Distinguish Action Runtime From Application Runtime
This is the most important concept in this entire process.
Consider:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm test
There are two different Node concepts here.
The first is the runtime used internally by actions/checkout.
The second is the Node runtime configured for:
npm test
Changing one does not necessarily change the other.
A useful mental model is:
GitHub Actions infrastructure
|
+-- Action runtime
|
+-- Runner
|
+-- Shell commands
|
+-- Application runtime
Treat each layer independently.
Example - A .NET Repository
Suppose your repository contains:
src/
tests/
.github/workflows/
Dockerfile
Your workflow is:
name: .NET CI
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x'
- run: dotnet restore
- run: dotnet build --no-restore
- run: dotnet test --no-build
There is no application-level Node dependency.
However, actions/checkout and actions/setup-dotnet can still use JavaScript internally.
Therefore, a search for:
node-version: 20
will not necessarily reveal every Node-related workflow dependency.
Example - A Node.js Repository
Now consider:
name: Node CI
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm test
Here Node 20 is explicitly part of the application's CI environment.
If the project moves to another supported Node release, the workflow needs to be updated and tested.
This is separate from any Node runtime used internally by actions/checkout or actions/setup-node.
Search for Node Commands
A workflow can also invoke Node directly.
Search for:
node
npm
npx
yarn
pnpm
For example:
- run: node scripts/generate.js
- run: npm run build
These commands depend on the Node runtime installed in the job.
A workflow may therefore have an implicit Node dependency even without setup-node.
For example:
- run: npm ci
If the runner provides Node, the workflow can use it.
For production CI, however, explicitly defining the required runtime is generally easier to understand and reproduce.
Build a Dependency Inventory
Once you have searched the repository, create a simple inventory.
Location | Example | Dependency Type | Action |
|---|---|---|---|
Workflow |
| Application runtime | Review |
Workflow |
| Action runtime | Check action |
|
| Developer runtime | Review |
|
| Application requirement | Review |
Dockerfile |
| Container runtime | Review |
Build script |
| Application runtime | Review |
Third-party action |
| Action runtime | Check release |
This prevents unrelated Node references from being mixed together.
Common Mistakes
Searching Only for node-version
This misses Node runtime dependencies inside JavaScript actions.
Updating Every Node 20 Reference
A Node 20 Docker image and a GitHub Action runtime are different dependencies.
Ignoring Third-Party Actions
Internal and third-party actions may contain the most important compatibility issues.
Updating Major Versions Without Testing
Changing:
some-action@v1
to:
some-action@v2
can introduce breaking changes.
Checking Only the Main Workflow
Release, deployment, security, and scheduled workflows may use different actions.
Troubleshooting
The Workflow Uses Node 20 but You Cannot Find setup-node
Search for:
node
npm
npx
uses:
The Node dependency may come from a command or an action.
An Action Reports a Node Runtime Warning
Identify the action that produced the warning.
Then inspect its current version and implementation.
Updating an Action Breaks the Workflow
Check:
Inputs
Outputs
Permissions
Runner compatibility
Runtime requirements
Release notes
Node Application Tests Fail After a Runtime Update
Check the actual runtime:
node --version
npm --version
Then run:
npm ci
npm test
Look for dependency compatibility problems.
Docker Builds Still Use Node 20
Check whether the Node version is defined in:
Dockerfile
docker-compose.yml
container configuration
An Actions runtime migration does not automatically modify container images.
Best Practices
Maintain an Action Inventory
Keep track of the actions used across your repository.
Review Runtime Dependencies Regularly
Do not wait until a workflow starts failing.
Pin Versions Intentionally
Use explicit action versions and update them through normal code review.
Separate Runtime Layers
Document the difference between:
Application Node
Action Node
Docker Node
Local Development Node
Test CI Changes
After updating an action or application runtime, verify:
Build
Unit tests
Integration tests
Packaging
Deployment
Remove Obsolete Configuration
Once a migration is complete, remove unused Node versions and old workflow settings.
Advantages of a Proper Dependency Inventory
Makes migrations easier - You know exactly where Node is being used.
Reduces unexpected CI failures - Hidden dependencies become visible.
Improves maintenance - Action and application dependencies can be updated independently.
Makes troubleshooting faster - Runtime problems can be isolated by layer.
Improves documentation - New developers can understand the CI environment more quickly.
Disadvantages and Limitations
Repositories can contain many runtime references.
Third-party actions may hide implementation details.
A text search cannot always determine the actual runtime dependency.
Different workflows can use different versions.
Action behavior can change between releases.
Practical Node 20 Audit Checklist
Use this checklist before making a runtime migration:
[ ] Search every .github/workflows file
[ ] Find every uses: action
[ ] Search for setup-node
[ ] Search for node-version
[ ] Search for node20
[ ] Search for npm, npx, yarn, and pnpm
[ ] Check third-party JavaScript actions
[ ] Inspect action.yml when necessary
[ ] Check package.json
[ ] Check .nvmrc
[ ] Check Dockerfiles
[ ] Check development container configuration
[ ] Check build scripts
[ ] Separate application runtime from action runtime
[ ] Review action release notes
[ ] Test the complete workflow
Conclusion
Finding Node 20 dependencies in GitHub Actions is more involved than searching for node-version: 20.
Node.js can appear in several layers of a repository. It can be the runtime for application commands, a container runtime, a local development requirement, or the runtime used internally by a JavaScript GitHub Action.
The most important step is to classify each dependency before changing it.
Start by auditing every workflow, inventorying all uses: actions, searching for explicit Node configuration, checking third-party action implementations, and then reviewing application and container configuration separately.
Once the dependencies are mapped, runtime migrations become much more controlled. Instead of changing every Node 20 reference and hoping CI continues to work, you can update each dependency according to what it actually does and verify the result with targeted tests.

Join the conversation! Your thoughts help the community grow.