Here’s the thing: the Node.js 18 end of life isn’t a theoretical risk; providers have already moved on. If your apps, functions, or pipelines still pin to 18, you’re one deploy away from a broken build or a stuck hotfix. This is your fast path to ship on Node 24 LTS without surprises—and yes, we’ll call out the common traps and how to test them in hours, not weeks.
Why this matters now: concrete dates and your exposure
If you only skim one section, make it this. Node 18 hit end of life on April 30, 2025. Major platforms followed with hard deprecation dates:
- Vercel disabled Node 18 for new builds on September 1, 2025 (existing deployments stayed live, but you can’t rely on redeploys).
- AWS Lambda marked Node 18 deprecated on September 1, 2025. As of early 2026, creation and updates of Node 18 functions are blocked on a rolling schedule, so every week you wait increases operational risk for rollbacks, patches, and incident response.
- GitHub Actions and other hosted CI images began removing Node 18 in November 2025, which is why workflows that used to “just work” suddenly fail on a fresh runner.
Zooming out: the ecosystem has standardized on Node 20+ for tooling and Node 24 for production LTS. That means your next build tool, framework minor, or security patch will assume a newer runtime. Waiting forces you into emergency upgrades—never the moment you want.
Primary decision: Node 22 or Node 24 LTS?
Pick Node 24 LTS unless you have a pinned platform requirement that explicitly states Node 22. Why:
- Node 24 is the current Active LTS branch as of late 2025 and will get the longest runway of fixes.
- Node 22 is in Maintenance LTS; it’s stable, but the update runway is shorter. New framework features are centering on 24.
Pragmatically, bumping to 24 now avoids another runtime jump next year. If your platform or vendor SDK insists on 22 for the moment, plan a second hop to 24 in Q1/Q2 2026.
Node.js 18 end of life: the gotchas teams actually hit
When we move clients off an end‑of‑life runtime, the breakage isn’t random; it clusters in a few predictable places. Expect—and preempt—these:
- Native modules and prebuilt binaries: node‑sass, sharp, canvas, sqlite bindings, bcrypt, and anything that downloads per‑version binaries. They’ll reinstall cleanly on a dev box but fail in CI or serverless if you’re cross‑compiling or packaging incorrectly. Use fully reproducible builds, pin exact versions, and rebuild in the target environment.
- OpenSSL behavior and crypto defaults: older code paths that relied on legacy ciphers or implicit defaults may error on newer Node. Run a targeted smoke on login, webhook signing, and any HMAC/PKI code.
- ESM/CJS edges: Node 24 tightens the screws in ways that expose sloppy import patterns. Mixed module graphs, conditional exports, and default import interop can break unexpectedly. Turn on verbose logging for module resolution in your test run to catch it early.
- Test runners and coverage tools: upgrade jest, vitest, or tap along with their transformers. Outdated Babel or ts-jest layers cause confusing syntax errors only on CI images that moved past 18.
- Serverless packaging: Lambda layers and esbuild/webpack targets must match the runtime. A layer built on 18 isn’t guaranteed to behave on 24—even if it “seems fine” in staging.
The Two‑Week Plan (that won’t torpedo your sprint)
This is the exact playbook we use when a client needs to move fast without breaking prod. Adapt day counts to your team’s cadence; the order matters more than the calendar.
Days 1–2: Inventory and align
- Identify every place a Node version is declared: .nvmrc, .node-version, package.json engines, Dockerfiles, CI workflows, Serverless/infra templates, Lambda Layers, runtime flags in platform dashboards. Make a short spreadsheet.
- Choose Node 24 LTS as the target (or 22 if your platform says so), and document it in the repo README so future you won’t wonder why.
- Upgrade your package manager (npm, pnpm, or yarn) to a version that supports Node 24. Clear lockfiles only if you must; otherwise, keep the diff small.
Days 3–4: Local dev and unit tests
- Switch your local runtime to Node 24. Run a clean install (no cache, no lockfile drift).
- Fix obvious type and import errors early. If you use TypeScript, ensure your TS version supports your framework’s Node targets. TS 5.6+ is usually table stakes by now.
- Rebuild native dependencies from scratch. On macOS with Apple Silicon, also test an x64 Linux container to simulate CI/serverless.
Days 5–7: CI and build parity
- Update GitHub Actions (or your runner) to use Node 24 explicitly via setup-node, not “default.” Old images are gone or shifting underfoot.
- Add a second CI job that still runs on Node 18 for one or two builds. Use it as a diff to prove there’s no hidden regression, then delete it.
- Bake NODE_OPTIONS and platform flags you previously set in the UI into code or IaC so they’re versioned. If something only lives in a dashboard, it will get lost.
Related reading on tightening CI: see our guide on getting pull_request_target right in GitHub Actions and our checklist for fixing npm tokens in CI—both cut upgrade noise.
Days 8–10: Staging with real traffic patterns
- For Lambda: deploy a canary alias on Node 24 and shift 5–10% traffic. Validate cold start, duration, memory, and error classes. Rebuild layers natively for Amazon Linux and confirm your bundler’s target matches.
- For Vercel/Next.js: move a non‑critical project or a preview branch first. Validate edge/runtime APIs, middleware behavior, and image optimization. We’ve covered the new caching and proxy behaviors in our Next.js 16 piece on cache components and proxy—those patterns benefit from Node 20+.
- For containerized apps: rebuild images FROM a Node 24 base, not a two‑year‑old distro image. Slim the image while you’re here; smaller images reduce roll risk.
Days 11–12: Data integrity and integration passes
- Re-run E2E tests that exercise your payment flows, OAuth, SSO, and webhooks. Pay special attention to timestamp parsing and signature verification across languages (Node server verifying a Python/Golang client, etc.).
- Smoke-test reporting jobs, queues, and workers. If you rely on ICU data for date/number formatting, confirm the same locale data is available in the new runtime.
Days 13–14: Rollout and clean-up
- Promote your canary to 100%, then remove Node 18 declarations from repos and dashboards. Don’t leave booby traps for the next deploy.
- Set a policy: new repos default to Node 24 LTS, and production functions must upgrade within 30 days of a new LTS going Active.
People also ask: quick answers you can share with stakeholders
Is Node.js 18 still safe to use if my app works?
Not really. “Works today” isn’t a security posture. End‑of‑life means no security fixes from the runtime project and increasing platform friction. You’re one runner refresh or serverless limit change away from a broken deploy.
Which version should I upgrade to: Node 22 or Node 24?
Choose Node 24 LTS for the longest supported runway and best ecosystem alignment. Opt into Node 22 only if your platform or framework specifically requires it, and plan a follow‑up to 24.
Will my AWS Lambda functions break immediately?
No—existing functions don’t vanish. But deprecation quickly becomes block lists for creating or updating on old runtimes. You don’t want to discover this during an incident. Move now, not during a Friday hotfix.
Test matrix that catches 90% of runtime regressions
Set this up in CI as a one‑week safety net:
- Node 24 LTS: full unit, lint, TypeScript, and E2E suites.
- Node 22 LTS: unit + build only (verifies transitive deps and bundlers).
- Linux x64 and the OS your devs actually use (macOS on Apple Silicon).
- Rebuild native modules rather than copying from cache; cache the toolchain (node-gyp, Python, compilers), not artefacts tied to a runtime.
If you ship a design system or SDK, add “created on 24, consumed on 22” and vice versa to catch module and type definition compatibility issues.
Dependency upgrade order that avoids churn
When the runtime moves, a neat sequence prevents death‑by‑PR:
1) Runtime bump (Node + package manager).
2) Bundler/transpiler (esbuild/webpack, swc, Babel).
3) Test stack (vitest/jest + transformers).
4) Framework (Next.js/Nuxt/SvelteKit/Angular SSR).
5) Observability (OpenTelemetry SDKs, loggers).
6) Cloud SDKs (AWS, GCP, Azure).
7) Native deps (sharp/canvas/sqlite/bcrypt) as a separate PR if needed.
Why this order? You want breakage to surface where it’s cheapest to diagnose—build and test—before app code and cloud adapters muddy the water.
Platform notes: what to double‑check
- AWS Lambda: rebuild layers with a Node 24 toolchain and package on Amazon Linux to avoid GLIBC mismatches. Confirm your bundler’s target (node16, node20, node22, etc.) matches the runtime, or your minified output can reference APIs that differ.
- Vercel: if you self‑host Next.js, aligning Node across dev, build, and runtime is mandatory. Middleware and image optimization paths can subtly differ when versions drift. Keep adapters up to date.
- GitHub Actions: pin your Node version via setup-node; don’t rely on “whatever the image has today.” Add a periodic job that prints versions so you notice when defaults change.
Performance: treat the upgrade like a free optimization sprint
Newer Node releases routinely bring throughput and startup gains. Take two hours to measure:
- Cold start and p95 duration for serverless.
- Build time on CI runners (Node 24 + modern bundlers often shave minutes).
- CPU-bound routes and image processing (sharp/canvas) after native rebuilds.
Lock those wins in: cap memory where appropriate and fail builds that regress p95 by more than, say, 10%.
Stakeholder memo: budget and timing
Leadership asks two questions: “How long will it take?” and “What could go wrong?” The two‑week plan above is realistic for most product teams, with one caveat: if you have many native modules, budget an extra day for per‑service rebuilds. The payoff is compounding—less emergency work, fewer security exceptions, and faster builds.
What to do next (today)
- Decide on Node 24 LTS and write it down in the repo and onboarding docs.
- Open a tracking issue for every app, function, and CI workflow still on Node 18.
- Ship a canary on 24 this week. Don’t wait for “after the holidays.”
- Add a periodic CI job that prints runtime versions; alerts when they drift.
Need a hand?
We’ve helped teams migrate frameworks and runtimes under tight deadlines—often while cleaning up their CI and caches so the next upgrade is painless. If you’d like an outside set of eyes on your plan, see what we do for engineering teams, browse a few recent projects, or just ping us via contact. And if you’re weighing a parallel Next.js upgrade while you’re at it, our practical Next.js 16 guide pairs nicely with this runtime move.
