23 min read

Node 24 End of Life: What October 20, 2026 Actually Changes for Laravel and Vapor Teams

Node 24 end of life is not October 20, 2026 — that is the Maintenance LTS date. Here is what actually changes for Laravel, Vapor, and AWS Lambda teams.

By Richard Joseph Porter

  • nodejs
  • laravel
  • vapor
  • aws
  • lambda
  • devops

Let me start by correcting the thing people are searching for. Node 24 end of life is not October 20, 2026. On that date — about five weeks from now — Node.js 24 "Krypton" moves from Active LTS into Maintenance LTS. Node.js 26 takes over as the Active LTS line eight days later, on October 28. Node 24's actual end of life is April 30, 2028, nineteen months later.

That distinction is not pedantry. It is the difference between "we need an emergency runtime migration in Q4" and "we need a scheduled cutover sometime in the next year." Plenty of teams are about to get the first answer from a compliance dashboard when the second answer is the correct one.

This post covers what October 20 genuinely changes, what it does not, where AWS Lambda's deprecation clock actually sits for nodejs24.x, and the specific places Node touches a Laravel Vapor deployment — which is a shorter list than most people assume, and includes one item that is far more urgent than Node 24.

TL;DR: What to Do in the Next Five Weeks

If you read nothing else:

  • Do not panic-migrate production Lambdas to nodejs26.x. As of publication it is a public preview runtime, explicitly not covered by the Lambda SLA or AWS Support. AWS lists general availability as targeted for November 2026.
  • nodejs24.x on Lambda is supported until April 30, 2028. Nothing about your Lambda functions changes on October 20.
  • Do move your build machines to Node 26 once it hits Active LTS. CI images, Docker build stages, and developer machines should track Active LTS. That is where Node 24 will start creating friction first.
  • Go find your real problem instead. If you still have functions on nodejs20.x or Vapor runtimes sitting on provided.al2, AWS begins blocking function updates on March 3, 2027. That is a hard wall six months out, and it is the item that belongs in your Q4 plan.
  • Run the audit in the section below. Thirty minutes, and it tells you whether any of this applies to you at all.

The Node.js Release Schedule, With Dates

Node.js majors get six months as Current, twelve months as Active LTS, and eighteen months as Maintenance LTS — roughly 30 months of guaranteed critical-bug coverage in total, which is the number the Release Working Group publishes.

Version Codename Current from Active LTS from Maintenance LTS from End of life
20 Iron Apr 18, 2023 Oct 24, 2023 Oct 22, 2024 Apr 30, 2026 (EOL)
22 Jod Apr 24, 2024 Oct 29, 2024 Oct 21, 2025 Apr 30, 2027
24 Krypton May 6, 2025 Oct 28, 2025 Oct 20, 2026 Apr 30, 2028
26 May 5, 2026 Oct 28, 2026 Oct 20, 2027 Apr 30, 2029

Two transitions land eight days apart, and conflating them with each other — or either of them with end of life — is the source of most of the confusion:

  • October 20: Node 24 enters Maintenance LTS. It is still supported. It is not end of life.
  • October 28: Node 26 becomes Active LTS. It becomes the version you should be building against.

Node 20 is the one that is genuinely dead — it reached end of life on April 30, 2026. If your CI pins Node 20 because someone copied it out of a tutorial in 2024, that is today's problem, not October's.

Every date in that table comes from the Node.js Release Working Group schedule, and the Node 24 EOL date is independently corroborated by AWS, which sets its nodejs24.x deprecation to April 30, 2028 on exactly that basis. The Working Group notes that dates are subject to change, so re-verify before you build a plan on them.

One structural rule worth keeping in mind while you plan: odd-numbered Node release lines are never promoted to LTS. They go from Current straight to end of life, which is why your upgrade path runs 22 → 24 → 26 and skips everything in between.

What Maintenance LTS Actually Means Day to Day

What you still get

Security releases and critical bug fixes, backported to the v24.x line and published through the normal release process, until April 30, 2028. Documentation updates continue. npm install keeps working. Your Docker base image keeps getting rebuilt. A CVE in the Node runtime between now and April 2028 gets a patch on Node 24.

This is a real guarantee, not a formality. Maintenance LTS is a supported state, and "we run Node 24" remains a defensible answer in a security questionnaire until April 2028.

What you stop getting

Everything else, and the "everything else" is more consequential than it sounds:

  • No new features. Whatever landed in 24 is what 24 has, permanently.
  • No non-critical bug fixes. A memory regression under a specific workload, a fs edge case on your particular filesystem, an inconsistency in the test runner — these get fixed on 26 and are not backported.
  • No semver-minor backports. New APIs that ship in 26 will not appear in 24, so any dependency that starts using them silently raises its floor above you.

The practical cost is the same as Laravel's security-only window: your escape hatch for an ordinary, non-security runtime bug becomes a major version upgrade, and you will be doing it under time pressure rather than on a schedule. That is the argument for moving, and it is a planning argument, not an emergency one.

What Actually Breaks (And What Doesn't)

What does not break on October 20

Nothing. Your Lambda functions keep running. Your Docker builds keep succeeding. Your npm ci keeps resolving. There is no flag day, no forced upgrade, and no AWS notification. If you do nothing on October 20, nothing observable happens.

I am being emphatic about this because the most expensive outcome here is a team burning two sprints on a runtime migration they did not need, in the same quarter they have real deadlines.

Real risk 1: Lambda's deprecation clock — which tracks EOL, not maintenance

AWS is explicit about its policy: Lambda deprecates a managed runtime "when any major component of the runtime reaches the end of community long-term support and security updates are no longer available." That is upstream end of life, not the maintenance transition. Here is where the Node.js runtimes actually sit, from the Lambda runtimes documentation:

Runtime Deprecation date Block function create Block function update
nodejs26.x (public preview) Not scheduled Not scheduled Not scheduled
nodejs24.x Apr 30, 2028 Jun 1, 2028 Jul 1, 2028
nodejs22.x Apr 30, 2027 Jun 1, 2027 Jul 1, 2027
nodejs20.x (deprecated) Apr 30, 2026 Feb 1, 2027 Mar 3, 2027
nodejs18.x (deprecated) Sep 1, 2025 Feb 1, 2027 Mar 3, 2027

Read the right-hand columns carefully, because they are the ones with teeth. After a runtime is deprecated you can keep invoking functions indefinitely, but once the block-update date passes, UpdateFunctionCode and UpdateFunctionConfiguration start failing. You cannot ship a hotfix. You cannot change an environment variable. Your only move is to bump the runtime identifier — and AWS warns that rolling back to the deprecated runtime may be blocked after you do.

So: nodejs24.x gives you until mid-2028. nodejs20.x and nodejs18.x give you until March 3, 2027. Those are wildly different situations and they live in the same account.

The other thing to note is that nodejs26.x is currently a public preview runtime. AWS's own policy is to ship managed runtimes only once a language version reaches Active LTS; the preview exists so the ecosystem can test ahead of that. Preview runtimes are outside the SLA and outside Support, and AWS targets November 2026 for the GA launch. Moving production traffic onto a preview runtime to escape a date that is not actually a deadline would be a strictly worse position than staying on 24.

Real risk 2: CI images and base images drop Node 24 before Node does

This is where you will feel it first, and it is not on anyone's calendar. GitHub Actions runner images, Docker Official Image tags, and hosted CI defaults track Active LTS. Within a quarter or two of the transition, node:lts starts resolving to 26, a runner image update changes the default node on the PATH, and a build that has been green for a year starts behaving differently.

The fix is not to chase it. The fix is to pin explicitly and fail loudly when the pin drifts, which is the CI gate further down this post.

Real risk 3: engines drift in your dependency graph

Package maintainers drop maintenance-mode Node versions from their support matrices well before upstream EOL. You will not experience this as a breakage. You will experience it as a slow freeze: a major release of Vite, or a lint tool, or a CSS pipeline, ships with "engines": { "node": ">=26" }, and your upgrade path quietly narrows. Then a security advisory lands on a package whose fix is only in that major.

Same dynamic as Composer dependency drift on an old Laravel version — the framework is the stable part, and the ecosystem around it is not. The lesson from Laravel 12's bug-fix cutoff applies verbatim here.

Real risk 4: native addons and prebuilt binaries

Anything that compiles against the V8 or Node ABI — sharp, better-sqlite3, node-sass if you are still carrying it, Playwright's browser bundles, esbuild's platform binaries — ships prebuilt artifacts per Node ABI version. Maintenance-mode Node majors get deprioritized in those build matrices, which turns an npm ci into a source compile on a machine without a toolchain. That is the failure mode: not a runtime error, a fifteen-minute CI job that suddenly takes forty and then fails on a missing python3.

Real risk 5: scanners and questionnaires that do not read the fine print

Plenty of SCA tooling and internal compliance dashboards classify anything not on Active LTS as "unsupported." That is wrong, but arguing with a scanner is not a good use of your quarter. If you are PCI- or SOC 2-exposed, be ready to document that Node 24 receives security fixes until April 30, 2028 and cite the release schedule. A supported-but-in-maintenance runtime with a dated migration plan is a conversation. An EOL runtime is a finding.

Where Node Actually Touches a Laravel Vapor Deployment

This is the part that gets overstated, so let me be precise about it.

Vapor's PHP runtime does not depend on the Node.js runtime at all. When you set runtime: php-8.4 in vapor.yml, Vapor deploys your application as a Lambda custom runtime with PHP layers — not as a nodejs* managed runtime. Node 24 entering Maintenance LTS has zero effect on how your Laravel application executes. If that is the only question you came here with, the answer is: you are fine, go do something else. The architecture is covered in more depth in my walkthrough of deploying serverless PHP with Laravel Vapor.

Node still matters in four specific places.

1. The build step, which runs on your machine — not on AWS. vapor deploy executes your build hooks locally (or on your CI runner) before it packages anything. Whatever node -v prints on that host is what compiles your assets. There is nothing in vapor.yml that selects a Node version, which means the pin has to live somewhere else and is very easy to leave unpinned.

# vapor.yml
id: 12345
name: my-app
environments:
    production:
        runtime: php-8.4
        build:
            - './scripts/assert-node-major.sh 26'
            - 'composer install --no-dev --classmap-authoritative'
            - 'php artisan event:cache'
            - 'npm ci && npm run build'
        deploy:
            - 'php artisan migrate --force'

The assert-node-major.sh line is the whole point. Without it, a deploy from a laptop running Node 20 and a deploy from CI running Node 26 produce different asset bundles from identical source, and you will spend an afternoon finding out why.

2. The asset pipeline itself. Vite, PostCSS, the TypeScript compiler, and whatever ships your Inertia or Vue frontend all run on Node. This is the surface where engines drift and native addon breakage actually land. If you run a typed frontend against a Laravel API — the setup in end-to-end type safety with TypeScript and Vue 3 — your build toolchain is a bigger Node dependency than your application is.

3. Other Lambdas in the same stack. This is the one people forget. Vapor's PHP functions are not the only things in your account. Queue consumers written in JavaScript, CloudFront Functions and Lambda@Edge handlers, CDK or SST sidecars, Slack webhook receivers, scheduled cleanup jobs someone wrote in 2021 — those are nodejs* managed runtimes and they are subject to the deprecation table above. The audit section exists because these are almost never in the same repository as your Laravel app.

4. The Vapor CLI itself. laravel/vapor-cli is a Composer package and runs on PHP, not Node. The Node dependency comes in through the laravel-vapor npm helper if you use Vapor.asset() in your frontend, and through everything your build hooks invoke. Verify against Vapor's current documentation rather than trusting a blog post, but the shape is: CLI is PHP, build is Node, runtime is PHP.

The item that is actually urgent

While you are in vapor.yml, check which runtime you named. Vapor's :al2 runtime family maps to Lambda's provided.al2 OS-only runtime, and AWS lists provided.al2 as deprecated since July 31, 2026, with function updates blocked from March 3, 2027. That is a real deadline, it is six months out, and it will stop you deploying. Confirm what your functions actually report rather than what your config file says:

aws lambda get-function-configuration \
  --function-name my-app-production \
  --query '{Runtime:Runtime,Handler:Handler,Modified:LastModified}'

The Audit: Thirty Minutes, Four Checks

Find every Node.js Lambda in the account

Start with the current region, grouped by runtime, so you get a count before you get a list:

# How many functions per Node.js runtime, in this region
aws lambda list-functions \
  --query "Functions[?starts_with(Runtime,'nodejs')].Runtime" \
  --output text \
  | tr '\t' '\n' | sort | uniq -c | sort -rn

Then the detail, including published versions — list-functions returns only $LATEST unless you ask for everything, and a pinned alias on an old version is exactly the thing that surprises you:

aws lambda list-functions \
  --function-version ALL \
  --query "Functions[?starts_with(Runtime,'nodejs')].[Runtime,FunctionName,LastModified]" \
  --output table

Then sweep every enabled region, because someone deployed something to ap-southeast-1 once:

#!/usr/bin/env bash
# scripts/audit-node-lambdas.sh - Node.js Lambdas across all enabled regions.
set -euo pipefail

for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
  aws lambda list-functions \
    --region "$region" \
    --function-version ALL \
    --query "Functions[?starts_with(Runtime,'nodejs')].[Runtime,FunctionName]" \
    --output text \
  | awk -v r="$region" 'NF {print r"\t"$0}'
done | tee /tmp/node-lambdas.tsv \
  | awk '{c[$2]++} END {for (k in c) printf "%-14s %d\n", k, c[k]}' | sort -rn -k2

Two notes so this actually works. AWS CLI v2 auto-paginates list-functions by default, but passing --max-items silently turns that off and truncates your results — do not add it. And --query is applied client-side after pagination, so the filtering above is complete, not a sample. Cross-check the total against the AWS Trusted Advisor "Lambda Functions Using Deprecated Runtimes" check, which tracks published versions too and is the same data source AWS uses for its 180-day deprecation emails.

Find where your Node version is pinned — and where the pins disagree

Three mechanisms, three different scopes, and they routinely contradict each other:

# What `nvm use` reads. Resolves to the newest locally installed 24.x.
cat .nvmrc
{
  "engines": {
    "node": ">=24.0.0 <27.0.0"
  },
  "volta": {
    "node": "26.0.0"
  }
}

engines is advisory by default — npm only enforces it when engine-strict is set. Volta, if installed, overrides whatever nvm would have chosen, silently. And .nvmrc is ignored entirely by CI unless something explicitly reads it. Make engines binding:

# .npmrc
engine-strict=true

Find the packages that will exclude you

# Every installed package that declares an engines.node range
jq -r 'select(.engines.node) | "\(.name)\t\(.engines.node)"' \
  node_modules/*/package.json node_modules/@*/*/package.json \
  2>/dev/null | sort -u

# Does the Node you are running satisfy every one of them?
node -v
npm ci --dry-run --engine-strict

The second command is the decisive one. A clean run means your current Node is acceptable to your whole tree. Run it again inside a Node 26 container before you cut over, and you have your migration estimate.

Migrating Node 24 to Node 26

The upgrade itself is small if you sequence it. The order below exists so that a failure tells you which change caused it.

Step 1 — Test both majors side by side before you change anything. Add Node 26 to the matrix without removing 24. You want two weeks of green builds on both before the cutover, not a big-bang switch.

# .github/workflows/assets.yml
name: assets

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        node: ['24', '26']

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: npm

      - run: npm ci --engine-strict
      - run: npm run build
      - run: npm test --if-present

Your deploy job should never use the matrix. Point it at the file that is the single source of truth:

      - uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
          cache: npm

Step 2 — Diff the build output, not just the exit code. A green build on a new Node major is necessary, not sufficient. Compare the emitted bundle from Node 24 and Node 26 on the same commit. If your Vite manifest hashes differ for unchanged source, something in the toolchain behaved differently, and you want to know that now rather than from a customer with a broken stylesheet.

Step 3 — Bump the pins together, in one commit. All three, or the drift starts immediately:

echo "26" > .nvmrc
npm pkg set engines.node=">=26.0.0 <27.0.0"
volta pin node@26   # only if your team actually uses Volta

Step 4 — Add the gate that fails the build on drift. This is the part that makes the pin real:

#!/usr/bin/env bash
# scripts/assert-node-major.sh - fail fast when the Node major is not what we expect.
set -euo pipefail

expected="${1:-$(tr -dc '0-9.' < .nvmrc | cut -d. -f1)}"
actual="$(node -p 'process.versions.node.split(".")[0]')"

if [ "$actual" != "$expected" ]; then
  echo "Node major mismatch: expected v${expected}, found $(node -v)" >&2
  exit 1
fi

echo "Node $(node -v) OK"

Or, if you would rather it fire on every npm install including on developer machines:

{
  "scripts": {
    "preinstall": "node -e \"if(+process.versions.node.split('.')[0]<26){console.error('Node 26+ required, found '+process.version);process.exit(1)}\"",
    "build": "vite build"
  }
}

Step 5 — Leave your Lambda runtimes alone until nodejs26.x is GA. Build tooling moves to 26 now. Managed runtimes move after November 2026, and only after you have soaked them. There is no reason to rush this: nodejs24.x is supported until April 2028.

Rollback and Safety

Node upgrades are reversible in a way that schema changes are not, provided you keep the escape route intact.

  • Keep Node 24 in the CI matrix through the soak. A green build on the version you are leaving is what turns a rollback into a redeploy instead of a dependency re-resolve under pressure.
  • Do not bundle npm update into the Node upgrade PR. If the same branch pulls packages that require Node 26, your lockfile no longer installs on 24 and your rollback has quietly stopped existing. Same rule I apply to PHP runtime upgrades, and for the same reason.
  • Commit the lockfile from the new Node major deliberately. npm's resolution can differ across majors. Regenerate it once, review the diff, and do not let it churn per-developer.
  • Use Lambda versions and aliases for any function runtime you do bump. AWS is explicit that rolling back to a deprecated runtime may be blocked after you update, so the alias shift is your rollback, not the runtime field.
  • Tag the pre-upgrade release. git tag pre-node-26 && git push --tags. Thirty seconds, and it is the difference between a revert and an archaeology session.

The Q4 Collision: Three Deadlines, One Upgrade Quarter

Here is the framing I would actually take into a planning meeting. Three dates land in the same window:

  • October 20, 2026 — Node 24 enters Maintenance LTS (Node 26 becomes Active LTS eight days later, on October 28).
  • December 31, 2026PHP 8.2 reaches end of life. This one is a real EOL with no further security fixes.
  • February 24, 2027 — Laravel 12's security fixes end, following the August 2026 bug-fix cutoff.

And, for a lot of Vapor accounts, a fourth: March 3, 2027, when Lambda blocks updates to nodejs20.x, nodejs18.x, and provided.al2 functions.

Treating those as four projects is how you lose two quarters. Treat them as one upgrade quarter with an ordering: runtime first (PHP, then Node), platform second (Lambda runtime identifiers), framework last. Each ships and soaks independently so failures stay attributable, and the sequencing logic is the same one in PHP 8.3 to 8.5 for Laravel teams.

The Node piece is the cheapest of the four and the only one without a hard deadline. Do it first precisely because it is cheap — it clears your CI, unblocks your dependency graph, and buys you a clean baseline for the expensive work. While you have the Lambda inventory open, it is also the natural moment to look at architecture and memory sizing against AWS cost optimization for PHP and Laravel apps, since you are already touching every function configuration in the account.

Frequently Asked Questions

Is Node 24 end of life? No. Node 24 enters Maintenance LTS on October 20, 2026 and reaches actual end of life on April 30, 2028. Between those dates it continues to receive security patches and critical bug fixes. It stops receiving new features and ordinary bug fixes.

What happens to Node 24 on October 20, 2026? It transitions from Active LTS to Maintenance LTS. Node 26 becomes the Active LTS release eight days later, on October 28. Nothing in your infrastructure changes. No AWS notification is sent, no build breaks, and no runtime is deprecated.

Is nodejs24.x being deprecated on AWS Lambda? Not for a long time. AWS lists the nodejs24.x deprecation date as April 30, 2028, with function creation blocked from June 1, 2028 and updates blocked from July 1, 2028. Lambda deprecates runtimes at upstream end of life, not at the maintenance transition.

What Node version should Laravel Vapor use? Vapor's PHP runtime does not use Node at all, so there is no Node version to set in vapor.yml. The version that matters is the one on the machine running vapor deploy — your CI runner or a developer laptop. Pin that to Active LTS: Node 24 today, Node 26 from October 28, 2026, enforced by .nvmrc, engines, and a build-time assertion.

Can I deploy to the nodejs26.x Lambda runtime now? Not for production. As of publication nodejs26.x is a public preview runtime, excluded from the Lambda SLA and AWS Support, with GA targeted for November 2026. Use it to test; do not move production traffic onto it.

Do I need to upgrade from Node 24 to Node 26 immediately? No. Move your build tooling to Node 26 once it reaches Active LTS, because that is where CI images and package engines constraints will pressure you first. Lambda runtimes can wait until nodejs26.x is generally available. The genuinely urgent migrations are nodejs20.x, nodejs18.x, and provided.al2, all of which block function updates from March 3, 2027.

What if we are still on Node 20? That one is real. Node 20 reached end of life on April 30, 2026, and the nodejs20.x Lambda runtime has been deprecated since that date. You have until March 3, 2027 before AWS blocks function updates, after which you cannot ship a hotfix without changing the runtime identifier first. Treat it as remediation with a named owner.

Key Takeaways

  • October 20, 2026 is Node 24's Maintenance LTS date, not its end of life. Actual EOL is April 30, 2028.
  • Maintenance LTS still ships security and critical fixes. What you lose is new features and ordinary bug fixes — which means your escape hatch for a routine runtime bug becomes a major upgrade.
  • Lambda deprecates on upstream EOL. nodejs24.x is supported until April 30, 2028; nodejs22.x until April 30, 2027; nodejs20.x and nodejs18.x block function updates on March 3, 2027.
  • nodejs26.x is a public preview runtime as of publication, with GA targeted for November 2026. Do not move production onto it yet.
  • Vapor's PHP runtime does not depend on Node. Node matters for the vapor deploy build step, the Vite/npm asset pipeline, and any JavaScript Lambdas sharing the account.
  • Pin in three places and gate on drift. .nvmrc, engines with engine-strict, and a build-time major-version assertion. Unpinned Node is how identical source produces different bundles.
  • Node 24, PHP 8.2, and Laravel 12 all expire in the same planning window. One upgrade quarter, sequenced — runtime, platform, framework — not three separate projects.

Verify every date here against the Node.js release schedule and the AWS Lambda runtimes documentation before you commit to a plan. Schedules shift, preview runtimes reach GA, and a blog post is a map, not the territory.

Your next action is the thirty-minute audit: run scripts/audit-node-lambdas.sh across your account and see what actually comes back. If the answer is "a handful of nodejs24.x functions and a CI pipeline on Node 24," you have no Q4 work here and you should spend the quarter on PHP 8.2 instead. If it comes back with nodejs20.x or provided.al2 rows, you have a March deadline nobody has told you about yet.

If you would rather have someone run that audit and the sequencing plan alongside your team than find the March date in February, that is the kind of work I do — send me the output of the region sweep and your vapor.yml, and that is usually enough for a useful first answer.

Richard Joseph Porter - Senior PHP and Laravel Developer, author of technical articles on web development

Written by

Richard Joseph Porter

Senior Laravel Developer with 14+ years of experience building scalable web applications. Specializing in PHP, Laravel, Vue.js, and AWS cloud infrastructure. Based in Cebu, Philippines, I help businesses modernize legacy systems and build high-performance APIs.

Get in touch

Need help upgrading your Laravel app?

I specialize in modernizing legacy Laravel applications with zero downtime. Get a free codebase audit and upgrade roadmap.

Related articles