HYEHOST

Node.js 20 End of Life: Upgrade to Node.js 24 LTS

Node.js 20 is end-of-life. Move bots and apps to Node.js 24 LTS with dependency checks, Docker examples, staging tests and a practical rollback checklist.

Explore Bot HostingStart the upgrade checklist
HYEHOST bear mascot upgrading a Node.js 20 server to Node.js 24 LTS

A bot can keep responding long after its runtime stops receiving upstream maintenance. That makes runtime upgrades easy to postpone: nothing obviously breaks on the end-of-life date. The risk is that the working deployment becomes harder to maintain while the application, dependencies and platform move on.

For HYEHOST customers, this affects more than websites. Discord bots, Telegram bots, notification workers, dashboards and self-hosted automation tools can all depend on Node.js. Below is a practical path for identifying the real runtime, testing compatibility and moving an application without treating production as the test environment.

What changed: Node.js 20 is no longer supported upstream

The official Node.js release schedule lists 30 April 2026 as Node.js 20's end-of-life date. As of this article's publication, the relevant release lines are:

ReleaseStatus on 26 September 2026Scheduled end of life
Node.js 20End of life30 April 2026
Node.js 22Maintenance LTS30 April 2027
Node.js 24Active LTS30 April 2028
Node.js 26Current; not yet LTS30 April 2029

These are upstream lifecycle dates, not guarantees about a particular hosting image or third-party extended-support contract. The Node.js end-of-life policy explains why unsupported releases become a maintenance concern. If a distribution or vendor supplies backported fixes, check that specific policy rather than assuming the upstream version number tells the whole story.

You do not need to chase the highest major version merely because it exists. For this migration, Node.js 24 offers a supported LTS target with more remaining support time than 22. An application vendor's compatibility matrix still takes priority: do not force a packaged service onto a runtime it does not support.

1. Find the Node.js version your application actually uses

Start with a small inventory: application name, deployment location, startup command, runtime, package manager, data location and rollback owner. Include scheduled jobs and CI workers as well as the obvious bot process. A forgotten worker can remain on the old runtime after the main service has moved.

In the application's execution environment, run:

node --version
npm --version
node -p "process.execPath"
node -p "JSON.stringify({node:process.version,platform:process.platform,arch:process.arch})"

The process.execPath value identifies the executable that launched that Node process. Your interactive shell may find a different binary from a service manager, container or hosting panel. Verify the deployed startup path too; updating your own terminal environment is not proof that production changed.

For an existing Docker Compose service named app, check inside it:

docker compose exec app node --version
docker compose exec app node -p 'process.execPath'

Replace app with the real service name. Updating Node.js on the Docker host does not replace the runtime baked into a container image. For third-party applications, use the application's supported image upgrade path rather than modifying its container by hand.

2. Create a staging copy and a usable rollback point

Before changing the runtime, preserve the current source revision, lockfile, startup settings and deployable artifact. Back up persistent files and take an application-consistent database backup where needed. A copy of your source repository is not a database backup, and a running VM snapshot is not automatically a consistent backup of every application.

Use separate test credentials and non-production data. A staging bot should not consume the same queue, polling token or scheduled job stream as production. Otherwise the test may send real notifications, handle customer work twice or interfere with the live bot.

HYEHOST VPS Resource Pools can be useful for keeping a staging VM separate from the live VM, with placement in Wolverhampton or Ashburn. Each VM still consumes pool resources: leave enough capacity for both during the migration. For off-server copies, see our VPS backup strategy and Storage Box options.

3. Test the existing application under Node.js 24

First change the runtime in staging while keeping the application revision and dependency lockfile stable. That gives you a meaningful comparison. Combining a runtime upgrade, a framework rewrite and a database migration makes it much harder to identify the cause when something fails.

Choose a current patched Node.js 24 release through your hosting runtime selector or the official Node.js installation options. Do not install the original 24.0.0 release simply because an old tutorial uses it. Confirm the version before installing project dependencies.

For an npm-based project with a committed, matching package-lock.json, use the following in the staging checkout:

node --version
npm --version
npm ci
npm test
npm run build --if-present

npm ci installs from the lockfile and removes the checkout's existing node_modules. It fails when the manifest and lockfile do not agree rather than silently updating the lockfile. Preserve any required project-level npm flags. See the npm ci documentation. Do not run this casually in a directory used by a live process.

Run the test command your project actually defines; a missing test script is not a successful test. Likewise, --if-present allows projects without a build script, but does not prove that the application is ready. TypeScript and compiled frontends usually need their build dependencies installed before creating the production artifact.

Pay particular attention to native dependencies

Packages that include native binaries may need compatible prebuilt artifacts or a compiler toolchain. Reinstall dependencies in the target environment rather than copying node_modules from a laptop, another CPU architecture or an older container. If a package fails to compile, check its supported Node versions before adding random build flags.

Review behaviour, not just installation output

Node.js 24 introduced changes including a newer V8 engine, npm 11, HTTP-client updates and API deprecations. The official Node.js 24 release notes are a useful starting point, alongside the intervening major-version notes and your framework's migration guidance. Test the features your application uses rather than assuming a successful install establishes compatibility.

Check authentication, uploads, database access, outbound HTTPS calls, timers and shutdown handling. If your project mixes CommonJS and ES modules, resolve the actual import or package compatibility problem; do not rename every file or change the module type without understanding the impact.

4. Upgrade a bot on HYEHOST Bot Hosting

HYEHOST Bot Hosting offers Node.js 22, 24 and 26, with deployment in Wolverhampton, UK or Ashburn, US. Runtime changes preserve the service's persistent files, but dependencies and startup commands still need review. Preserved files are not the same as a tested application or an independent backup.

  1. Record the working configuration. Save the source revision, runtime choice, startup command and dependency lockfile. Store secrets securely, never in public screenshots.
  2. Test separately. Use a staging service or another suitable environment with a test bot account. Confirm the application works on Node.js 24.
  3. Schedule the switch. Stop the production bot before changing its runtime and reinstalling dependencies. Avoid competing instances using the same credentials.
  4. Select Node.js 24. Review the startup command and use the dependency workflow appropriate to your npm, pnpm or Yarn project. Do not mix lockfile formats during this step.
  5. Start and verify. Confirm the runtime, send a real test command and watch logs and resource metrics. Restart once more to check repeatable startup.

For Discord, test command handling, permissions and reconnect behaviour. For Telegram polling, keep only one active poller per token; our Telegram hosting guide explains that deployment distinction even though its code example uses Python. If the bot runs scheduled jobs, verify that switching deployments does not replay actions unexpectedly.

Do not assume a newer runtime needs a larger plan. Measure normal and peak memory, CPU and startup behaviour. If your bot downloads large files or processes media, resource requirements depend much more on that work than on the version label alone.

5. Upgrade a Docker-based Node.js application on a VPS

On a Cloud VPS, you control the container image and deployment process. For your own application, update the Dockerfile's Node base image, build a candidate and test it before replacing the running service. For packaged software, follow its vendor's release instructions instead.

This small Dockerfile is for a plain JavaScript application whose entry point is server.js, which has a committed npm lockfile and needs no compilation step. Adapt it to your actual project; it is not a universal framework image.

FROM node:24-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]

Use a .dockerignore so local dependencies, Git history and secrets do not enter the build context:

node_modules
.git
.env
.env.*
*.log
coverage

Review other secret paths too; this list cannot know your project. Inject runtime secrets through your deployment configuration. Projects needing TypeScript compilation, native build tools or generated clients should use an appropriate build stage rather than removing required development tools before the build.

The official Node container documentation describes image variants. A Debian-based image is a straightforward starting point; changing both Node major version and the underlying distribution at once adds another compatibility variable.

Build a locally tagged candidate:

docker build --pull -t my-node-app:node24-candidate .

The major-version tag can move to newer patches. For repeatable production releases, record the resulting artifact and consider pinning the reviewed base image by digest, then deliberately refresh it for updates. Docker's build guidance covers rebuilding and image pinning. A pinned image still needs an update process.

Run the candidate in staging with the environment, storage and networking your application needs. Do not attach a second active worker to production queues merely to see whether it starts. After deploying the reviewed image, inspect the runtime inside the new container and perform a real application check. Rebuilding an image alone does not replace an already running container.

6. Use a smoke-test checklist that reflects real work

CheckWhat success looks like
Startup and restartThe deployed process starts repeatedly with the intended Node version and configuration.
External APIsAuthentication, timeouts and error responses work without exposing tokens in logs.
Bot commands or API routesRepresentative real requests succeed, including a permission-denied case.
Persistent dataRecords or files survive a controlled restart and remain compatible with the application.
Jobs and queuesOnly the intended worker handles each job; retries do not create duplicate actions.
Resource behaviourMemory and CPU remain within the plan under representative activity.
ShutdownThe process stops cleanly without abandoning important work or corrupting state.

For a service with an existing health endpoint, test that endpoint from the correct network and also check a user-facing operation. A static “OK” response cannot tell you whether the database or bot connection works. Our Beszel monitoring guide can help with host metrics; application-level checks remain separate.

7. Cut over with a realistic rollback plan

Keep the last known-working artifact and configuration until the new deployment has passed its observation window. Define what triggers a rollback: repeated startup failures, broken commands, sustained errors or resource exhaustion. Assign someone to check these signals rather than declaring success the moment a process starts.

For web APIs, a separately tested deployment can be placed behind a reverse proxy and switched deliberately. For bots and single-consumer workers, stop the old process before activating its replacement unless the application explicitly supports coordinated parallel operation. “Blue-green” infrastructure does not make every workload safe to run twice.

Database changes need their own plan. Rolling back the Node binary or container does not undo a schema migration. Prefer a runtime-only change first, and do not restore an old database over newer customer writes without assessing data loss. If an emergency fallback temporarily returns to an unsupported runtime, treat it as a short-lived recovery measure with an owner and deadline, not the completed migration.

Common upgrade problems and what to check

ProblemCheck first
The service still reports Node.js 20The service's executable path, panel runtime selection or running container image—not just your shell.
Native module fails to loadPackage compatibility and a clean install for the target runtime, platform and architecture.
npm ci failsManifest/lockfile agreement and required project npm settings; do not discard the lockfile to hide the issue.
Build tools are missingWhether development dependencies were omitted before the build stage.
Module import errorsThe dependency's supported module format and your application's explicit CommonJS/ESM configuration.
Works locally but not after restartWorking directory, environment, permissions and the actual production startup command.
Messages or jobs happen twiceOld and new instances consuming the same production token, schedule or queue.

Keep dependency security work separate but do not ignore it. Review audit findings, package maintainers and release notes. Avoid blindly running forced dependency upgrades as part of the runtime switch: they can introduce unrelated breaking changes. A clean dependency audit does not establish that the Node runtime is supported.

Node.js end-of-life and upgrade FAQs

When did Node.js 20 reach end of life?

Node.js 20 reached its scheduled upstream end of life on 30 April 2026. It no longer receives normal upstream maintenance. Check any separately purchased extended support or distribution backport policy independently.

Should I upgrade to Node.js 22, 24 or 26?

As of 26 September 2026, Node.js 24 is Active LTS, Node.js 22 is Maintenance LTS and Node.js 26 is Current. Node.js 24 is the target used in this guide; choose a supported release that your application and dependencies explicitly support.

Will a Node.js upgrade break my Discord or Telegram bot?

It can if dependencies, native modules or application behaviour are incompatible. Test a clean dependency install under the new runtime, then verify commands, reconnects, scheduled jobs and persistence with separate test credentials before changing production.

Does changing the Docker host's Node.js version upgrade containers?

No. A container uses the runtime inside its image. Update the relevant image or Dockerfile, rebuild when necessary and recreate the service, then check the Node.js version inside the running container.

Can I use Node.js 24 on HYEHOST Bot Hosting?

Yes. HYEHOST Bot Hosting lists Node.js 22, 24 and 26 as runtime choices. Back up persistent data and review dependencies and the startup command before switching. File persistence does not guarantee application compatibility.

Does npm audit fix an end-of-life Node.js runtime?

No. Dependency audit results and runtime support are separate concerns. Updating packages does not replace an unsupported Node.js binary, and a clean audit report is not a complete security assessment.