On Wednesday, September 23, GitHub deletes the Node.js 20 interpreter from every hosted Actions runner on the planet, all at once, on a calendar you never voted on. If any workflow you own still pins a node20-era action — actions/checkout@v4, actions/setup-node@v4, and friends — that step will not warn, degrade, or limp along on a compatibility shim. The runner will look for an interpreter that no longer exists and fail the step before your action's code ever runs.
You have until Wednesday. The good news: the fix is a version-bump table, not a migration project. The bad news: this is the third time GitHub has run this exact playbook, and the structure of the situation guarantees a fourth. This post covers what breaks Wednesday, the exact majors to bump to, and the self-hosting moral — with the honest boundaries most "just self-host it" takes leave out.
What actually breaks on Wednesday, in three buckets
GitHub announced the Node 20 deprecation on September 19, 2025, a year ago. Node 20 "Iron" itself reached end of life on April 30, 2026. Since June 16, 2026, runners have defaulted every JavaScript action to Node 24, with one escape hatch: set ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true and your node20 actions kept running on the old interpreter. September 23 kills the interpreter, and the hatch dies with it. (The date slipped a week from the originally announced September 16 — check the editor's note on the changelog if your calendar still says the 16th.)
Bucket 1: pinned node20 actions hard-fail at launch. Any action whose action.yml declares runs.using: node20 stops launching. The mechanism is blunt: no interpreter binary, no launch, failed step. This hits tag pins (actions/checkout@v4), and it equally hits SHA pins that resolve to node20-era releases — a pinned SHA is a frozen runtime declaration, not protection from this. If your workflow logs have shown the "Node.js 20 actions are deprecated … will be forced to run on Node.js 24" annotation since June, that forcing was the grace period. Wednesday the grace period ends.
Bucket 2: the opt-out dies. If you set ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true anywhere — workflow env, runner machine environment, reusable workflows consumed by other repos — it becomes a no-op on Wednesday. There is no Node 20 left to opt into. Worse than useless, really: it signals "we handled the migration" while handling nothing. Grep for it and remove it once you've bumped, so nobody mistakes it for a safety net next time.
Bucket 3: two runner fleets have no forward path inside Actions at all. Node 24 ships no official ARM32 build and refuses macOS 13.4 and lower, per GitHub's own changelog. Self-hosted runners on ARM32 — the Raspberry Pi fleet in the closet, the cheap ARM boards doing nightly builds — lose support entirely once Node 20 is gone. Same for Mac minis pinned to an old macOS for hardware-compatibility reasons. These aren't "bump a version" problems; they're "replace the machine or leave Actions" problems, and the affected teams have until Wednesday to notice.
The fix: a version-bump table, not a migration project
For everyone not in bucket 3, the fix is mechanical. Bump each JavaScript action to a major whose action.yml declares using: node24. The first-party mapping that the ecosystem has converged on:
| Action | node20-era (breaks) | node24-native (safe) |
|---|---|---|
actions/checkout | v4 | v6 (v5+ already declares node24) |
actions/setup-node | v4 | v6 (v5+ already declares node24) |
actions/setup-python | v5 | v6 |
actions/upload-artifact | v4 | v7 |
actions/deploy-pages | v4 | v5 |
actions/cache | v4 | v5 |
softprops/action-gh-release | v2 | v3 |
For third-party actions, the rule is universal: open the action's repo, read action.yml, and confirm runs.using: node24 at the ref you pin. No node24 release from the maintainer? That action is abandonware as of Wednesday — fork it, replace it, or vendor the 30 lines it wraps.
Three gotchas that have already bitten people in this migration:
SHA pins must be re-pinned, not just admired. Pinning to a full commit SHA is the right security posture, and it changes nothing about this deadline. A SHA pointing at a node20 release fails exactly like the tag would. Re-pin to the SHA of the new major and keep the # v6 trailing comment convention so the next human can see the intent.
The action's runtime is not your toolchain. actions/setup-node@v6 running on Node 24 says nothing about which Node version it installs for your build — that's the separate node-version: input, and your app can keep building on Node 20 (or 22, or whatever your Dockerfile says) regardless. Multiple teams burned hours "migrating to Node 24" by changing their app's toolchain and never touching the action version. The outage Wednesday is about the runner's interpreter, not your code's.
Self-hosted runners have a floor. Node 24 actions require Actions Runner v2.327.1 or newer; anything older can't launch them. Check your fleet with the runner version in the self-hosted runners settings page before Wednesday, not after. And note the ratchet only moves one way: since March 2026 GitHub has blocked registering runners older than v2.329.0, so a stale runner image doesn't just miss the new runtime — it can't even re-register.
Finding the stragglers takes one pass over your workflow logs: every run since June has printed the deprecation annotation naming each offending action. No recent runs on a repo? grep -rn "uses:" .github/workflows and audit every JavaScript action against the table above. Dormant repos with scheduled workflows are the classic surprise outage here — the cron job nobody watched has been warning into the void for three months.
Third verse of the same song
None of this is novel, which is precisely the point. The Node 16 to Node 20 migration ran the identical playbook: announced September 2023, warnings starting October 2023, enforcement on June 30, 2024 — with the same ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION opt-out variable, the same "forced to run on" grace period, the same community-wide scramble to bump checkout@v3 to v4. Before that, Node 12 got the same treatment on the same rhythm.
The rhythm isn't GitHub being capricious; it's the Node.js release clock. Node majors go end of life on a fixed schedule — 16 in September 2023, 18 in April 2025, 20 in April 2026 — and GitHub trails each EOL by roughly a year of deprecation plus a forced cutover. Node 24 "Krypton" is Active LTS until April 2028, which means you can already pencil in the next one: a deprecation notice circa fall 2027, a forced default in mid-2028, and another Wednesday where unmaintained workflows die simultaneously. Any CI strategy that treats this week's scramble as a one-off is budgeting for surprise.
That predictability is what makes the vendor-calendar structure worth naming. The timeline is public, the grace periods are generous, the communication is genuinely good — and none of it changes the fact that the purge lands on every hosted runner at once, on a date set in someone else's changelog, with no lever you can pull to defer it by a week while you finish the thing you're actually shipping.
The own-metal moral, honestly bounded
Here is the take that fits on a sticker: a build pipeline on hosted runners inherits a vendor's runtime-removal calendar, while building on machines you own makes runtime currency your own scheduled decision. It's directionally true, and the honest version has three rungs, not two:
Rung 1 — hosted runners: zero levers. September 23 happens to everyone at once. No support ticket, no feature flag, no enterprise tier defers a runtime purge. The only move is to be migrated before the date, every cycle, forever.
Rung 2 — self-hosted runners: scheduling control, not exemption. This is the rung most "just self-host" advice oversells. Self-hosting the runner does buy you something real: the Node runtime ships inside the runner binary, and config.sh --disableupdate lets you pin the runner version and roll the upgrade forward on your own week — canary one runner, validate node24 actions against your actual workflows, then widen. But GitHub still sets the walls of the room: auto-update is on by default, new registrations already require v2.329.0+, and minimum-version enforcement means pinning buys you a scheduled migration, not a permanent freeze. And bucket 3 (ARM32, old macOS) applies to self-hosted runners hardest of all. Self-hosting the runner turns a vendor deadline into a planned maintenance window. That's genuinely valuable — it's just not immunity.
Rung 3 — your own build plane: the calendar is yours. Full ownership means the CI system itself runs on your fleet — your runners, your controller, your base images — so a runtime EOL becomes a ticket in your backlog with a severity you assign, not a guillotine with someone else's date. The price is also yours: patching, capacity, and the unglamorous work of keeping builders healthy. Whether that trade wins depends on how much Wednesday-style scrambles cost you; for a team whose deploys stop when CI stops, it's worth pricing honestly instead of rediscovering every two years.
The pattern to internalize: each rung up buys scheduling control, never exemption from maintenance. Node EOLs will keep arriving every April until the heat death of the ecosystem. The only question is whether they arrive as your plan or someone else's deadline.
Your pre-Wednesday checklist
- Find the stragglers today. Read the deprecation annotations in your recent workflow runs, or grep every
uses:line in every repo with a.githubdirectory — including the dormant ones with scheduled workflows. - Bump to
node24-native majors using the table above, re-pin any SHA locks to the new majors, and verify third-party actions declareusing: node24at your pinned ref. - Upgrade self-hosted runners to v2.327.1 at minimum (v2.329.0+ to stay registrable), and retire or re-platform any ARM32 or macOS ≤13.4 runners before Wednesday.
- Delete the opt-out. Remove every
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=trueso it can't masquerade as protection, and decide which rung of the ladder your next build-plane investment climbs.
See you at the next one, circa 2028. It'll be Node 24's turn, the playbook will be identical, and the teams who used this week to buy scheduling control instead of just bumping versions will watch it the way you watch weather from inside a house.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



