Back to Article List

Upgrade from Node.js 20 to 24 LTS on Ubuntu

Upgrade from Node.js 20 to 24 LTS on Ubuntu

Node.js 20 is aging. Plan your upgrade.

If you're running Node.js 20 on your VPS, mark your calendar: April 30, 2026 is end-of-life. After that date, no security patches. No vulnerability fixes. Your app becomes a target for attackers who find CVEs and know Node 20 won't get patches. The longer you wait, the more technical debt accumulates. New dependencies drop support for Node 20 first. Your team spends time patching old code instead of building features.

The path to Node.js 24 LTS is clear. It's not a jump to an unstable version. Node 24 is the long-term support release, backed until April 30, 2028. That's three more years of security updates, performance improvements and breaking-change stability. Upgrading now means breathing room.

This guide walks you through upgrading from 20 to 24. You'll test first, run both versions side-by-side if needed and deploy with zero downtime. The process is straightforward when you know what to expect.

Understanding the Node.js LTS schedule

Node.js follows a predictable release cycle. Every even-numbered version becomes LTS. Node 20 (October 2023 release) is Active LTS until April 2026, then enters Maintenance LTS until April 2027. Node 24 (April 2025 release) enters Active LTS immediately and stays there until April 2028.

In practical terms: Node 20 gets security fixes and important bug patches for one more year. After that, it's Maintenance LTS (only critical security patches, no new features). By late 2026, vendors stop supporting it. Your dependencies will require Node 22 or 24.

Node 22 (October 2024 release) also exists as LTS until April 2027. It's a middle ground if you want a smaller jump. But upgrading straight from 20 to 24 is actually safer than two hops. You test once, deploy once.

What Node.js 24 brings

V8 engine updates and performance implications

Node 24 ships with V8 13.6, up from V8 12.9 in Node 20. This isn't just a version number bump. V8 13 significantly improved garbage collection, reduced memory overhead and improved computation performance on math-heavy operations. Benchmarks show 8 to 15% faster execution on compute-intensive workloads. GC pause times are shorter, meaning less jank in your application under load.

The trade-off: stricter validation of JavaScript syntax. Code that worked on Node 20 might throw parse errors on 24 if it uses undocumented V8 internals or eval with malformed strings. This is rare and usually indicates the code was fragile to begin with.

OpenSSL 3.5 and cryptographic stricter defaults

Node 20 uses OpenSSL 3.4. Node 24 upgrades to OpenSSL 3.5 with default security level 2 (tighter than Node 20's default level 1). This means RSA keys shorter than 2048 bits are now rejected. DSA and DH keys under 2048 bits fail. ECC keys under 224 bits are blocked. RC4 and other obsolete ciphers don't work anymore.

This breaks compatibility with legacy APIs and old certificate chains. If you're connecting to an internal service with a weak self-signed cert or outdated API using RC4, Node 24 will throw "SSLV3_ALERT_HANDSHAKE_FAILURE" or similar. Modern APIs (AWS, Google, Azure, major SaaS providers) have all upgraded their TLS requirements. Most applications won't hit this. But custom internal services or APIs from 2015 will need updates.

Workaround if needed (not recommended): set NODE_OPTIONS="--tls-min-version=TLSv1.0" temporarily, but that's a security downgrade. Better to fix the certificate or API instead.

npm 11 and stricter peer dependency resolution

Node 20 ships with npm 10. Node 24 comes with npm 11. The upgrade isn't just faster (npm v11 benchmarks at 65% faster for large installs). npm 11 resolves peer dependencies more strictly. If your package.json has a package that declares a peer dependency you don't explicitly install, npm 10 might ignore it. npm 11 will warn you, and in some cases fail the install.

Example: express might declare fastify as an optional peer dependency. npm 10 ignores the missing peer. npm 11 warns you about the unmet peer dependency. This isn't a breaking change unless your project relied on npm's silence. Usually it just means you need to add an explicit dependency.

Run npm ci (clean install) before upgrading to see what peer dependency warnings you'll get on Node 24. Address them ahead of time.

Permission model now stable and enabled by default with --permission flag

Node 20 introduced the permission model as experimental (behind --experimental-permission). Node 24 marks it stable (--permission). It's not on by default, but you can enable it to sandbox your process.

When you run Node with --permission, the process can't access the file system, spawn child processes or load native modules unless you explicitly allow them with --allow-fs-read, --allow-fs-write, --allow-child-process and --allow-worker flags.

Example: node --permission --allow-fs-read=/var/www/app --allow-fs-write=/var/www/app/logs --allow-net server.js runs your app with read access to /var/www/app, write access to logs, and network access. It can't read /etc/passwd or spawn a shell.

This is significant for security. If a dependency is compromised, the attacker's code can't access more than the permission flags allow. Most apps don't need this yet, but production apps should adopt it gradually.

Native .env file loading with --env-file flag

Node.js 20.6+ supports --env-file=.env natively. Node 24 makes this fully stable. You no longer need the dotenv npm package for simple use cases. Just run node --env-file=.env app.js and process.env gets populated from the .env file.

You can pass multiple --env-file flags; later files override earlier ones. Environment variables set before the node command take precedence over .env values. This is cleaner than managing dotenv in your code and reduces a dependency.

Your existing dotenv code still works, so there's no urgency to migrate. But for new projects, using --env-file is simpler.

URLPattern API and Undici improvements

Node 24 makes URLPattern a global API (no import needed). If your code parses or validates URLs, URLPattern gives you reliable parsing without regex. Undici, the built-in HTTP client, gets faster with better connection pooling and streaming defaults.

Breaking changes from Node 20 to 24

Removed and deprecated APIs

Node 24 removes several APIs that were deprecated in earlier versions. util.isBoolean(), util.isNumber(), util.isString() and similar type checkers are gone. If your code uses them, replace with native JavaScript: typeof value === 'boolean' instead. tls.createSecurePair() is removed; use tls.TLSSocket() instead. fs.truncate() with file descriptor argument is gone; use fs.ftruncate().

These are edge-case removals. Standard Node.js APIs (fs, http, path, etc.) are all stable. Only deprecated utilities disappear.

Stricter TLS and certificate validation

Beyond OpenSSL level changes, Node 24 enforces HSTS preloading and stricter certificate chain validation. If you're using mitmproxy or Charles Proxy for local debugging, certificates might fail validation. For production, this tightens security.

Fetch API and AbortSignal validation

Node's built-in fetch() becomes more spec-compliant in v24. Some edge cases around AbortSignal timing and stream error handling change. If you're using fetch extensively, test your edge cases on v24 before deploying.

Native addon ABI changes

If you use native modules (bcrypt, canvas, better-sqlite3, etc.), they're compiled for a specific Node.js ABI (Application Binary Interface). Node 24 changes the ABI. Old binaries don't work. You must recompile them for Node 24. This is why the "rebuild native modules" step is critical.

Pre-upgrade checklist

Before you touch production, run through this:

Dependency audit. Check which packages you depend on. Visit their GitHub releases or npm pages. Do they support Node 24? Some older packages don't. npm outdated shows versions available. npm update updates to compatible versions within your semver constraints.

Run tests. Your test suite is your safety net. Make sure you have one. If tests pass on Node 24, you're mostly safe.

Check for native modules. Run npm ls | grep -E 'bcrypt|canvas|sqlite|grpc'. Any result means native modules. They need rebuilding.

Check for custom TLS/SSL. If you configure TLS directly (custom certificates, old cipher suites, weak keys), test on Node 24 early. Catch OpenSSL errors before production.

Check env variable usage. If you use dotenv or load .env manually, make sure your new approach (--env-file or staying with dotenv) is decided and tested.

Step-by-step upgrade with nvm

Install Node 24 alongside Node 20

First, confirm you're on Node 20:

node --version
nvm list

Install Node 24 without switching to it:

nvm install 24

Verify both are available:

nvm list

The asterisk still points to your current version (20). You're not live on Node 24 yet.

Test your app on Node 24 in isolation

Switch to Node 24 temporarily:

nvm use 24
node --version

Navigate to your app directory. Clean install dependencies:

npm ci

The npm ci command (clean install) is safer than npm install. It respects package-lock.json exactly. If npm warns about unmet peer dependencies, note them. These are potential issues on Node 24.

If you have a build step, run it:

npm run build

If you have tests:

npm test

If tests fail, don't panic. Read the error. Is it a V8 parsing issue? A TLS certificate problem? A missing dependency? Fix it here while you're in a test environment, not in production.

Rebuild or reinstall native modules

Native modules (those with .node binaries) were built for Node 20's ABI. Node 24 has a different ABI. The old binaries won't work. Rebuild them:

npm rebuild

This recompiles all native modules in node_modules for Node 24. It takes a few minutes if you have large packages like better-sqlite3.

Alternatively, nuke node_modules and reinstall fresh (npm will recompile during install):

rm -rf node_modules package-lock.json
npm ci

The fresh install approach is slower but cleaner. Choose based on how large your node_modules is.

Update PM2 itself

PM2 runs your Node processes. Its daemon needs to be compatible with Node 24:

npm install -g pm2@latest
pm2 update

The pm2 update command refreshes the daemon without restarting your processes. It's fast.

Deploy and switch default version

Once you're confident (tests pass, local testing looks good), switch Node 24 as your default:

nvm alias default 24
nvm use 24
node --version

If you manage your app with PM2, reload it gracefully:

pm2 reload my-app --wait-ready --listen-timeout 10000

This restarts one process at a time, waiting for each to be ready before moving to the next. No downtime.

If you use a systemd service instead of PM2, restart it:

sudo systemctl restart myapp

Monitor logs for the first few minutes:

pm2 logs my-app
# or
sudo journalctl -u myapp -f

Look for errors. If something breaks, you know immediately.

Blue-green deployment with both versions running

If you want maximum safety, run both Node 20 and Node 24 in parallel. Put Nginx in front as a load balancer. Route some traffic to Node 24, watch for errors, then shift more. This is overkill for most upgrades but necessary for critical systems.

Create two PM2 ecosystem configs. ecosystem.20.config.js and ecosystem.24.config.js:

// ecosystem.20.config.js
module.exports = {
  apps: [
    {
      name: 'app-node20',
      script: './server.js',
      instances: 4,
      exec_mode: 'cluster',
      env: { NODE_ENV: 'production' }
    }
  ]
};

// ecosystem.24.config.js
module.exports = {
  apps: [
    {
      name: 'app-node24',
      script: './server.js',
      instances: 4,
      exec_mode: 'cluster',
      env: { NODE_ENV: 'production' }
    }
  ]
};

Start both:

nvm use 20
pm2 start ecosystem.20.config.js
nvm use 24
pm2 start ecosystem.24.config.js
nvm use 20  # default stays on 20 for safety

Now configure Nginx upstream with weights:

upstream app_upstream {
  server 127.0.0.1:3000 weight=9;  # Node 20, 90% of traffic
  server 127.0.0.1:3001 weight=1;  # Node 24, 10% of traffic
}

server {
  listen 80;
  server_name example.com;

  location / {
    proxy_pass http://app_upstream;
    proxy_http_version 1.1;
    proxy_set_header Connection '';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

Verify Nginx config and restart:

sudo nginx -t
sudo systemctl restart nginx

Monitor error logs for 24 hours. If no errors, increase Node 24 traffic gradually:

weight=5;  # 50/50
weight=9;  # 90% on 24

Once fully migrated, stop the Node 20 processes and clean up.

Updating your CI/CD pipelines

If you use GitHub Actions, your workflow file specifies the Node version. Update it:

- uses: actions/setup-node@v4
  with:
    node-version: '24'

Commit and push. Future builds run on Node 24. Your staging environment now matches production.

For GitLab CI, update .gitlab-ci.yml:

image: node:24-alpine

For other CI systems (Jenkins, CircleCI, etc.), find where the Node version is specified and update it.

Handling common upgrade errors

SSLV3_ALERT_HANDSHAKE_FAILURE or certificate validation errors

This means your app is connecting to a service with a weak certificate or old TLS configuration. On Node 24, OpenSSL 3.5 rejects it by default.

Solution: Fix the certificate or the service. Request your API provider upgrade. If it's internal, renew the certificate with stronger key material.

Temporary workaround (not for production): set NODE_OPTIONS="--openssl-legacy-provider" but this is a security downgrade.

ERR_MODULE_NOT_FOUND for native modules

Native modules weren't recompiled for Node 24's ABI. Run npm rebuild or npm ci again.

Unmet peer dependencies warnings

npm 11 is stricter. If you see warnings, add the missing dependencies to your package.json or update the packages that declare them as optional peers.

Parse errors in eval or dynamic code

V8 13 validates syntax more strictly. If you have code that builds code strings dynamically, check the syntax on Node 24. Most code doesn't do this.

Rollback plan

If Node 24 causes unexpected problems in production, rollback is fast:

nvm use 20
nvm alias default 20
pm2 reload my-app --wait-ready

You're back on Node 20 in seconds. Keep Node 20 installed for a week after upgrading, just in case.

For more on managing memory and stability through version changes, read about fixing Node.js memory errors on your VPS. And to monitor your app's performance after upgrading, set up Prometheus and Grafana for historical metrics and alerts. For orchestrating zero-downtime deploys, see zero-downtime deployment patterns with PM2.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
Cycle de facturation

1 GB RAM VPS

$3.99 Save  25 %
$2.99 Mensuel
  • 1 vCPU AMD EPYC
  • 30 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Gestion du pare-feu
  • Suivi serveur gratuit

2 GB RAM VPS

$5.99 Save  17 %
$4.99 Mensuel
  • 2 vCPU AMD EPYC
  • 30 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Gestion du pare-feu
  • Suivi serveur gratuit

6 GB RAM VPS

$14.99 Save  33 %
$9.99 Mensuel
  • 6 vCPU AMD EPYC
  • 70 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Gestion du pare-feu
  • Suivi serveur gratuit

AMD EPYC VPS.P1

$7.99 Save  25 %
$5.99 Mensuel
  • 2 vCPU AMD EPYC
  • 4 GB RAM mémoire
  • 40 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

AMD EPYC VPS.P2

$14.99 Save  27 %
$10.99 Mensuel
  • 2 vCPU AMD EPYC
  • 8 GB RAM mémoire
  • 80 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

AMD EPYC VPS.P4

$29.99 Save  20 %
$23.99 Mensuel
  • 4 vCPU AMD EPYC
  • 16 GB RAM mémoire
  • 160 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

AMD EPYC VPS.P5

$36.49 Save  21 %
$28.99 Mensuel
  • 8 vCPU AMD EPYC
  • 16 GB RAM mémoire
  • 180 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

AMD EPYC VPS.P6

$56.99 Save  21 %
$44.99 Mensuel
  • 8 vCPU AMD EPYC
  • 32 GB RAM mémoire
  • 200 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

AMD EPYC VPS.P7

$69.99 Save  20 %
$55.99 Mensuel
  • 16 vCPU AMD EPYC
  • 32 GB RAM mémoire
  • 240 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

EPYC Genoa VPS.G1

$4.99 Save  20 %
$3.99 Mensuel
  • 1 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4ᵉ génération 9xx4 à 3,25 GHz ou équivalent, basé sur l’architecture Zen 4.
  • 1 GB DDR5 mémoire
  • 25 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

EPYC Genoa VPS.G2

$12.99 Save  23 %
$9.99 Mensuel
  • 2 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4ᵉ génération 9xx4 à 3,25 GHz ou équivalent, basé sur l’architecture Zen 4.
  • 4 GB DDR5 mémoire
  • 50 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

EPYC Genoa VPS.G4

$25.99 Save  27 %
$18.99 Mensuel
  • 4 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4ᵉ génération 9xx4 à 3,25 GHz ou équivalent, basé sur l’architecture Zen 4.
  • 8 GB DDR5 mémoire
  • 100 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

EPYC Genoa VPS.G6

$48.99 Save  31 %
$33.99 Mensuel
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4ᵉ génération 9xx4 à 3,25 GHz ou équivalent, basé sur l’architecture Zen 4.
  • 16 GB DDR5 mémoire
  • 200 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

EPYC Genoa VPS.G7

$74.99 Save  27 %
$54.99 Mensuel
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4ᵉ génération 9xx4 à 3,25 GHz ou équivalent, basé sur l’architecture Zen 4.
  • 32 GB DDR5 mémoire
  • 250 GB NVMe stockage
  • Bande passante illimitée
  • IPv4 & IPv6 inclus La prise en charge d’IPv6 n’est actuellement pas disponible en France, en Finlande ou aux Pays-Bas.
  • 1 Gbps réseau
  • Sauvegarde auto incluse
  • Gestion du pare-feu
  • Suivi serveur gratuit

FAQ

Can I skip Node 22 and upgrade directly from 20 to 24?

Yes. Node.js LTS versions are designed for direct jumps. There are no hidden breaking changes between 22 and 24 that would break a working app on 20. Going straight from 20 to 24 is actually cleaner. You test once, deploy once. No two-stage migration.

Skip the setup, start deploying

Your server comes ready with PM2, Nginx and Certbot. Pick a datacenter and push your first build in minutes.

GPU products are in high demand at the moment. Fill the form to get notified as soon as your preferred GPU server is back in stock.