# How I moved two GitHub Actions from Node.js 20 to Node.js 24

> In March 2026, I moved two GitHub Actions, chrnorm/deployment-action and chrnorm/deployment-status, from Node.js 20 to Node.js 24, because GitHub deprecated Node.js 20 on its runners. The maintainer merged both pull requests on 22 March 2026. This note gives each change, so you can do the same for your action.

- URL: https://uttamgelot.com/notes/github-actions-node-24/
- Updated: 2026-10-19
- Author: Uttam Gelot
- Published: 2026-10-19

- **Topics:** GitHub Actions, Node.js, TypeScript, CI/CD

## Why the change is necessary

A JavaScript action sets its Node.js version in `action.yml`, in `runs.using`. The only valid values are `node20` and `node24` ([GitHub docs](https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#runsusing-for-javascript-actions)). GitHub deprecated Node.js 20 on its runners. It said that Node.js 20 actions will be forced to run on Node.js 24 from 2 June 2026 ([GitHub changelog, 19 September 2025](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/)).

Two popular actions still had `node20`:

- [chrnorm/deployment-action](https://github.com/chrnorm/deployment-action) creates a GitHub deployment.
- [chrnorm/deployment-status](https://github.com/chrnorm/deployment-status) updates the status of a deployment.

Each pull request fixes an open issue: [#93](https://github.com/chrnorm/deployment-action/issues/93) and [#53](https://github.com/chrnorm/deployment-status/issues/53).

## The 5 changes

### 1. Set the runtime in action.yml

```diff
 runs:
-  using: node20
+  using: node24
   main: 'dist/index.js'
```

### 2. Update the Node.js types

```diff
-    "@types/node": "^20.11.16",
+    "@types/node": "^24.0.0",
```

In deployment-status, the old version was `^12.0.4`. In that repository, I also added `"skipLibCheck": true` to `tsconfig.json`.

### 3. Update the workflows of the repository

The repository has its own CI workflows. Run them on Node.js 24, and use the current versions of the actions that they use:

```diff
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v6

-      - name: Set Node.js 20.x
-        uses: actions/setup-node@v4
+      - name: Set Node.js 24.x
+        uses: actions/setup-node@v6
         with:
-          node-version: 20.x
+          node-version: 24.x
```

deployment-status also used `actions/upload-artifact@v2`. I changed it to `actions/upload-artifact@v4`.

### 4. Build dist/ again

The action runs `dist/index.js`, not the TypeScript source. The `check-dist` workflow builds the bundle again, and it fails if the result is not the same as the committed file. So build the bundle and commit `dist/`. In deployment-status, the new bundle had 26,293 added lines and 1,412 removed lines in `dist/index.js`.

### 5. Run all checks

`npm run all` runs the build, format, lint, package and test steps. Before I opened the deployment-action pull request, it passed on my machine.

## The result

| Action | Pull request | Files | Opened | Merged |
|---|---|---|---|---|
| deployment-action | [#94](https://github.com/chrnorm/deployment-action/pull/94) | 6 | 2026-03-17 | 2026-03-22 |
| deployment-status | [#54](https://github.com/chrnorm/deployment-status/pull/54) | 10 | 2026-03-17 | 2026-03-22 |

Neither pull request changed the source code of the action. All my merged pull requests are on the [open-source page](https://uttamgelot.com/open-source/).

## Checklist

1. In `action.yml`, set `runs.using` to `node24`.
2. Set `@types/node` to `^24.0.0`.
3. In the workflows, use `actions/checkout@v6`, `actions/setup-node@v6` and `node-version: 24.x`.
4. Build `dist/` again and commit it.
5. Run the build, the linter and the tests.
