GitHub Actions workflows depend on more than the commands written in a YAML file. Actions themselves can depend on specific Node.js runtimes, and changes to those runtimes can affect CI pipelines even when a repository's application code has not changed.

This becomes especially important when an action moves away from an older Node.js runtime.

If a workflow still depends on an action running on Node 20, developers may eventually see warnings, compatibility problems, or failures as GitHub Actions moves its runtime support forward.

The important question is not simply whether your application uses Node.js 20.

The real question is:

Which Node.js runtime do the actions inside your workflow require?

This distinction matters because a .NET, Java, Python, or C++ repository can still use JavaScript-based GitHub Actions internally.

GitHub Actions and Node.js Runtimes

GitHub Actions supports reusable actions written in different technologies. JavaScript actions run using a Node.js runtime supplied by the GitHub Actions runner.

A workflow might therefore contain:

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
  - uses: some-org/some-action@v2

Your application may be written entirely in C#, but actions/checkout or another third-party action can still depend on Node.js internally.

This creates two separate runtime questions:

Application runtime
        +
GitHub Actions action runtime

They should not be treated as the same thing.

Why a Node Runtime Change Matters

GitHub periodically updates the Node.js versions used by its JavaScript-based actions.

This is necessary because older Node.js versions eventually reach the end of their supported lifecycle.

For workflow maintainers, the important consequence is that an action can require an updated runtime even when the workflow YAML looks unchanged.

For example:

- uses: example/action@v1

may continue to look valid while the implementation behind v1 depends on an older runtime.

A runtime migration can therefore affect the workflow indirectly.

Node 20 in Your Application vs Node 20 in Actions

This is one of the most common sources of confusion.

Consider a .NET workflow:

name: Build

on:
  push:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '10.x'

      - name: Build
        run: dotnet build

There is no application-level Node.js requirement here.

However, some actions used by the workflow may internally use Node.js.

That means changing the Node runtime used by GitHub's action infrastructure does not necessarily mean you need to add:

node-version: '20'

to the workflow.

Do not add application runtime configuration simply because an action uses Node internally.

How to Find Node-Based Actions

The first step is to inspect your workflow files.

Look under:

.github/workflows/

For example:

.github/
└── workflows/
    ├── build.yml
    ├── test.yml
    ├── release.yml
    └── deployment.yml

Then identify every uses: entry:

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
  - uses: docker/setup-buildx-action@v3
  - uses: org/custom-action@v1

The important question for each action is:

Which runtime does this version of the action require?

Do not determine this only from the action name.

An action can change its internal runtime between major or minor releases.

Why Third-Party Actions Need Special Attention

Official actions are usually easier to track because their repositories and release notes are widely documented.

Third-party actions can be more difficult.

For example:

- uses: company/internal-deployment-action@v1

If that action has not been maintained recently, it may continue using an older Node runtime.

The workflow may then encounter warnings or compatibility problems even though the repository itself is actively maintained.

This is why action maintenance should be part of CI maintenance.

Finding Old Action Versions

Search your workflow files for uses:.

On Linux or macOS:

grep -R "uses:" .github/workflows

On PowerShell:

Get-ChildItem .github/workflows -Recurse |
    Select-String "uses:"

This gives you a starting list.

For example:

.github/workflows/build.yml
.github/workflows/test.yml
.github/workflows/release.yml

You can then inspect each referenced action.

Check the Action's Runtime

For JavaScript actions, the repository commonly contains an action.yml or action.yaml file.

A JavaScript action can contain information similar to:

runs:
  using: node20
  main: dist/index.js

The important field is:

using: node20

This indicates which Node.js runtime the action expects.

An older action might contain:

runs:
  using: node16
  main: dist/index.js

That is an action-runtime dependency, not necessarily an application dependency.

What Should You Use Instead?

The correct replacement depends on the action.

If an action has a newer maintained release that supports the required Node runtime, update the action.

For example:

- uses: example/action@v1

might become:

- uses: example/action@v2

The exact version should come from the action's documented release and compatibility information.

Do not blindly replace every action version with the newest major version.

Major releases can contain behavior changes.

Example - Updating a Workflow

Suppose an existing workflow contains:

steps:
  - uses: actions/checkout@v3

  - name: Run tests
    run: dotnet test

If the repository is still using an old version of an action, the correct maintenance process is:

Identify action
      ↓
Check current release
      ↓
Check runtime requirement
      ↓
Review breaking changes
      ↓
Update action
      ↓
Run CI
      ↓
Verify results

Do not treat a runtime migration as a simple search-and-replace operation.

Do You Need setup-node?

Another common misunderstanding is that every workflow affected by a Node runtime change needs actions/setup-node.

For example:

- uses: actions/setup-node@v4
  with:
    node-version: 20

This is appropriate when your application or build process needs Node.js 20.

For example:

- uses: actions/setup-node@v4
  with:
    node-version: 20

- run: npm ci
- run: npm test

But if your repository is a .NET application and Node.js is only used internally by GitHub Actions, adding setup-node does not solve an action-runtime migration.

These are different layers.

Example - .NET Application With a Node-Based Action

Consider:

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 test

There is no reason to add setup-node simply because GitHub's internal action runtime changes.

The .NET SDK is the application build runtime.

The JavaScript runtime used by the action is an implementation detail of the action.

What About Node Projects?

Node applications are different.

Consider:

steps:
  - uses: actions/checkout@v4

  - uses: actions/setup-node@v4
    with:
      node-version: 20

  - run: npm ci
  - run: npm test

Here Node 20 is explicitly part of the application's CI environment.

If your application requires Node 22, for example, you can configure:

- uses: actions/setup-node@v4
  with:
    node-version: 22

This controls the Node version available to your run: commands.

Again, this is separate from the Node runtime used internally by JavaScript actions.

How to Find Node 20 References

Search both workflow configuration and project configuration.

Search Workflows

grep -R "node20" .github/workflows

Also search for:

grep -R "node-version" .github/workflows

Search the Repository

Depending on your project:

grep -R "\"node\": \"20" .

or inspect:

package.json
.nvmrc
.tool-versions

For a mixed repository, search for references to Node 20 across CI scripts, build scripts, containers, and documentation.

The goal is to determine whether Node 20 is:

Application dependency
        or
Action implementation dependency

Check Container-Based Workflows Too

Some workflows explicitly use Node images.

For example:

container:
  image: node:20

This is different from a JavaScript action using Node 20 internally.

The container is part of your workflow environment.

If the application requires a newer Node version, the container image may need to change:

container:
  image: node:22

Test the application after making such a change.

Docker Builds Need Separate Review

Node versions can also appear inside Dockerfiles.

For example:

FROM node:20

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

If your application still depends on Node 20, an Actions runtime migration does not automatically require changing this Dockerfile.

Again, separate the concerns:

GitHub Actions runtime
        ≠
Docker runtime
        ≠
Application runtime

Each needs its own compatibility check.

Common Mistakes

Changing Every Node Version to the Same Value

Do not replace every 20 with another Node version without checking what the value represents.

Node 20 can appear in:

They may have different requirements.

Adding setup-node Without Understanding the Problem

setup-node changes the Node version available to commands executed by the job.

It does not necessarily change the runtime embedded in another JavaScript action.

Ignoring Third-Party Actions

A third-party action may remain on an old runtime long after the rest of the workflow has been updated.

Updating Actions Without Reading Release Notes

A major action upgrade can introduce behavior changes.

Test the workflow after upgrading.

Checking Only One Workflow

Repositories commonly have multiple workflow files:

build.yml
test.yml
release.yml
deploy.yml
security.yml

An old action can remain in only one of them.

Troubleshooting Runtime Migration Problems

Workflow Shows a Node Runtime Warning

Identify which action produced the warning.

Do not assume the warning comes from your application.

Inspect the workflow step associated with the message and check the action version.

Action Fails After an Upgrade

Compare the old and new action versions.

Look for:

Node Application Fails After Updating Node

Run the application's tests separately.

Check:

node --version
npm --version
npm ci
npm test

Also inspect package compatibility.

.NET Workflow Fails After an Action Update

Check whether the updated action changed its inputs or outputs.

Then run:

dotnet restore
dotnet build
dotnet test

The action runtime and .NET runtime should be diagnosed separately.

Best Practices for GitHub Actions Runtime Changes

Keep Actions Updated

Do not leave workflow actions untouched for years.

Regular updates reduce the chance of being forced into an emergency migration.

Pin Action Versions Carefully

Use intentional action versions instead of relying on an unreviewed moving target.

For higher-security environments, commit SHA pinning can provide stronger supply-chain control, although it increases maintenance overhead.

Test Changes in Pull Requests

Action updates should go through the same review process as application changes.

Maintain a Workflow Inventory

Keep track of:

Action
Version
Purpose
Runtime
Owner
Update frequency

For example:

Action

Purpose

Runtime Concern

Checkout action

Fetch repository

JavaScript runtime

Setup Node

Configure application Node

Explicit Node version

Setup .NET

Configure SDK

Action runtime separate from .NET

Deployment action

Deploy application

Check action implementation

Custom action

Internal automation

Verify maintained runtime

Separate Application and Action Runtime Changes

When troubleshooting, identify which layer changed.

This makes failures much easier to diagnose.

Advantages of Keeping Workflows Current

  1. Fewer runtime compatibility surprises

  2. Better support for current runner environments

  3. Improved security maintenance

  4. Easier troubleshooting

  5. Less risk of emergency migration work

Disadvantages and Risks of Updating Too Quickly

  1. Major action upgrades can introduce breaking changes.

  2. Old workflows may depend on undocumented behavior.

  3. Third-party actions may not have compatible releases.

  4. Testing is required before production deployment.

  5. Updating multiple actions simultaneously makes troubleshooting harder.

A Practical Migration Checklist

Before changing a workflow, check:

[ ] List every action used by the workflow
[ ] Identify actions that execute JavaScript
[ ] Check their current runtime requirements
[ ] Identify Node versions used by application commands
[ ] Check Docker and container images
[ ] Check package configuration
[ ] Review action release notes
[ ] Update one logical dependency at a time
[ ] Run the workflow in a pull request
[ ] Verify build and tests
[ ] Verify deployment jobs separately
[ ] Remove obsolete configuration

Conclusion

When GitHub Actions moves away from an older Node.js runtime, the first step is not to change every Node version in your repository.

Instead, determine where Node.js is being used.

A repository can use Node.js in several independent places: GitHub Actions themselves, application builds, Docker containers, local development, and package tooling.

The Node runtime used internally by a JavaScript GitHub Action is different from the Node runtime configured with actions/setup-node.

That distinction makes migrations much easier to understand.

The safest approach is to inventory your workflows, identify the actions that depend on older runtimes, update those actions to supported releases, test the complete workflow, and separately maintain any Node.js version required by the application itself.

Keeping GitHub Actions dependencies current is ongoing maintenance rather than a one-time migration task. A small amount of regular review can prevent an old action runtime from becoming a CI failure later.