Debian

Install Node.js on Ubuntu 26.04 / 24.04 and Debian 13

The nodejs package in Ubuntu’s own archive is the wrong Node.js for most projects. On 26.04 it installs Node 22 with npm 9.2, a release of npm that predates Node 22 itself, and on a fresh server it dragged in 568 packages and 738 MB because the archive’s npm is split into hundreds of separate Debian packages. On 24.04 the archive version is Node 18, which went end-of-life in April 2025.

Original content from computingforgeeks.com - post 100986

This guide shows you how to install Node.js on Ubuntu and Debian from NodeSource, which ships the current LTS as a single package with a matching npm, and with nvm when you need several versions side by side. It then builds a native module, runs a small Express app under systemd and moves the install to Node.js 26. We ran every step on Ubuntu 26.04 in October 2026 and repeated the NodeSource install on Ubuntu 24.04 and Debian 13, with Node.js 24.22.0 and 26.12.0.

Pick a Node.js line

Production code belongs on an LTS line, because those get 30 months of security fixes. October 2026 is a changeover month, so there are two LTS lines right now and a third about to join them:

LineStatus in October 2026Security fixes untilBest for
Node.js 26Current, becomes LTS on 28 October 202630 April 2029New projects that start now
Node.js 24Active LTS, moves to maintenance on 20 October 202630 April 2028Existing apps, the safe default today
Node.js 22Maintenance LTS30 April 2027Apps that cannot move yet

Dates come from the Node.js release schedule. Install Node.js 24 for anything already in production, and start new projects on 26, because it will be supported a full year longer. Node 26 is also the last line under the old twice-a-year model: from Node 27 the project ships one major a year, in April, and every release becomes LTS that October, so the odd/even rule no longer applies. If you only want one specific line, the Node.js 24 on Ubuntu and Node.js 24 on Debian guides go deeper on that release.

The distro packages lag well behind all of this:

  • Ubuntu 26.04 archive: Node 22 with npm 9.2
  • Ubuntu 24.04 archive: Node 18, out of support
  • Debian 13 archive: Node 20, out of support

Those packages exist for other Debian packages that depend on Node, not for application developers. They are also heavy, because the archive splits npm into hundreds of small packages: a simulated apt-get install nodejs npm wanted 540 new packages on 24.04 and 554 on Debian 13. Skip them.

What you need

Ubuntu server images ship everything this guide uses. A minimal Debian 13 install may not, because it skips sudo when you set a root password, has no curl, and only provides vi. Check that the machine has:

  • Ubuntu 26.04, Ubuntu 24.04 or Debian 13 on amd64
  • a user with sudo rights
  • curl and an editor (we use vim)
  • about 250 MB free for Node.js, plus 230 MB per extra nvm version

On Debian, install the missing tools as root first with apt install -y sudo curl vim, then add your user to the sudo group with usermod -aG sudo josphat (use your own username) and log in again so the group takes effect.

Install Node.js from NodeSource

NodeSource publishes one apt repository per Node major line, plus a setup script that adds it. Download the script first and read it, rather than piping it straight into a root shell, because it runs as root and edits your apt configuration.

curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
less nodesource_setup.sh

The script runs apt update, installs curl, gnupg, ca-certificates and apt-transport-https, and deletes any older NodeSource repository and key. Then it writes three things you will want to know about later:

  • /usr/share/keyrings/nodesource.gpg, the signing key
  • /etc/apt/sources.list.d/nodesource.sources, the repository
  • /etc/apt/preferences.d/nodejs and nsolid, two apt pins

Run it once you’re happy with what it does:

sudo -E bash nodesource_setup.sh

The script finishes by suggesting apt install nodejs. Before you run that, look at the repository and the nodejs pin, because those two files decide which build you get from now on.

cat /etc/apt/sources.list.d/nodesource.sources /etc/apt/preferences.d/nodejs

The repository uses the suite nodistro, so the same tree serves every Ubuntu and Debian release and you never wait for NodeSource to add a new codename. The pin gives NodeSource’s nodejs a priority of 600 over the archive’s 500, so apt picks NodeSource’s build even when the archive’s version string sorts higher. That matters more than it sounds: with setup_22.x on 26.04, Ubuntu’s 22.22.1+dfsg+~cs22.19.15-1ubuntu1 sorts above NodeSource’s 22.22.1-1nodesource1, and without the pin apt would install Ubuntu’s package.

Types: deb
URIs: https://deb.nodesource.com/node_24.x
Suites: nodistro
Components: main
Architectures: amd64
Signed-By: /usr/share/keyrings/nodesource.gpg
Package: nodejs
Pin: origin deb.nodesource.com
Pin-Priority: 600

Now install it. NodeSource bundles npm and npx inside the nodejs package, so leave npm out of the command. We use apt-get in the commands whose output we show, because on 26.04 the newer apt prints its summary in a different layout; both install the same package.

sudo apt-get install -y nodejs

That was one package and 246 MB on 26.04, 24.04 and Debian 13 alike, against 568 packages for the archive’s nodejs and npm on 26.04. Check the versions next.

node -v
npm -v
npx -v

Node 24 ships npm 11, so the tooling matches the runtime and you do not need to upgrade npm separately.

v24.22.0
11.19.1
11.19.1

For Node.js 26 instead, use setup_26.x in the first command and nothing else changes. The repository path is the only thing that differs between lines, which is also how the upgrade later in this guide works.

nodejs : Conflicts: npm

Installing Ubuntu’s npm package on top of NodeSource’s nodejs fails, because NodeSource’s package declares a conflict with it. The error looks like an impossible dependency problem.

The following packages have unmet dependencies:
 nodejs : Conflicts: npm

Nothing is broken. You already have npm, since it ships inside nodejs, so run npm -v to confirm it and drop npm from whatever script or Dockerfile tried to install it. Provisioning scripts written for the distro package hit this most often, because there the two really are separate packages.

Install build tools for native modules

Packages such as bcrypt, sharp and better-sqlite3 carry C++ code that npm compiles when no prebuilt binary matches your platform. Install a compiler and make up front, so those installs do not fail halfway through a deploy on a server that has no build tools.

sudo apt install -y build-essential

Recent npm versions also warn about dependencies that run install scripts. We saw this when installing bcrypt into a test project on both 26.04 and 24.04.

npm warn install-scripts   [email protected] (install: node-gyp-build)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

On npm 11 this is only a warning: the module still installed and hashed a password correctly. npm 12, already out, goes further. According to GitHub’s npm 12 changelog, it stops running dependency preinstall, install and postinstall scripts by default, including the implicit node-gyp rebuild for packages with a binding.gyp, until you approve them. Approve the packages you trust while you’re still on npm 11, so native modules keep building when your build server moves to npm 12.

Install global npm packages

Global installs go to /usr/lib/node_modules, which belongs to root, so a plain npm install -g as your user fails with a permission error.

npm error code EACCES
npm error syscall rename
npm error path /usr/lib/node_modules/pm2
npm error dest /usr/lib/node_modules/.pm2-zkn4vkCL
npm error errno -13
npm error Error: EACCES: permission denied, rename '/usr/lib/node_modules/pm2' -> '/usr/lib/node_modules/.pm2-zkn4vkCL'

With a NodeSource install, use sudo for global tools that everyone on the machine should share, such as the pm2 process manager. Our Uptime Kuma with pm2 guide shows it running a real service.

sudo npm install -g pm2
pm2 --version

Do not fix the error by changing the ownership of /usr/lib/node_modules. The next nodejs upgrade writes into that directory as root again, and you end up with a mix of owners that breaks both apt and npm. If you want per-user global packages with no sudo at all, the nvm section below does exactly that.

Run a Node.js app with systemd

A quick Express app proves the runtime, npm and the network stack all work together. Create a project and install Express into it.

mkdir -p ~/hello && cd ~/hello
npm init -y
npm install express

Create the app file in the project directory.

vim ~/hello/app.js

It answers on port 3000 and reports the Node version that served the request, which is handy after an upgrade:

const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello from Node ' + process.version + '\n'));
app.listen(3000, () => console.log('listening on :3000'));

Running it with node app.js works until you log out, because the process belongs to your SSH session. To keep it up across reboots and crashes, give it a systemd unit.

sudo vim /etc/systemd/system/hello-node.service

Change User and WorkingDirectory to your own account. ExecStart uses the full path to the NodeSource binary, because systemd does not load nvm or your shell profile, so a bare node could resolve to nothing at boot. Restart=on-failure brings the app back after a crash, which is most of what pm2 would give you here:

[Unit]
Description=Hello Node.js app
After=network.target

[Service]
User=josphat
WorkingDirectory=/home/josphat/hello
ExecStart=/usr/bin/node app.js
Restart=on-failure
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Start it, give Node a couple of seconds to bind the port, and send it a request. Without the pause, curl -s can hit the port before the app listens and print nothing at all.

sudo systemctl daemon-reload
sudo systemctl enable --now hello-node
sleep 2
systemctl is-active hello-node
curl -s http://127.0.0.1:3000/

The service reports active, and the reply carries the runtime version, which on our 24.04 box was the NodeSource build.

active
Hello from Node v24.22.0

If other machines must reach the app directly, open the port with sudo ufw allow 3000/tcp (Debian needs sudo apt install -y ufw first). For anything public, keep port 3000 closed and put nginx in front instead, so TLS, request limits and access logs live in one place and Node never faces the internet directly.

Run several versions with nvm

nvm installs Node.js under your home directory, one directory per version, and switches between them per shell. It suits a developer machine that juggles projects on different lines, and it can coexist with a NodeSource install. The longer guide to managing Node.js versions with nvm covers aliases, migrating global packages and per-project setup. Fetch the latest nvm release tag first:

NVM_VER=$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep '"tag_name"' | cut -d '"' -f 4)
echo $NVM_VER

The echo should print a tag such as v0.40.8. If it prints nothing, the GitHub API rate limit kicked in, so wait a minute and repeat it. The installer command below only runs when the variable is set, because an empty tag produces a redirect page that curl would pipe straight into bash:

[ -n "$NVM_VER" ] && curl -so- "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VER}/install.sh" | bash

The installer appends three lines to ~/.bashrc. Load them in the current shell, then install the newest LTS and Node 26 side by side.

source ~/.bashrc
nvm install --lts
nvm install 26

Each install downloads the official binary tarball and checks its SHA-256 before unpacking. Whatever version you install first becomes the default, so here new shells start on the LTS.

Computing checksum with sha256sum
Checksums matched!
Now using node v24.22.0 (npm v11.19.1)
Creating default alias: default -> lts/* (-> v24.22.0 *)

Pin a project to a line with an .nvmrc file in its root. Running nvm use inside that directory then picks the right version, so nobody on the team has to remember which project needs which Node. The mkdir -p is harmless if you already created ~/hello in the systemd section.

mkdir -p ~/hello && cd ~/hello
echo 26 > .nvmrc
nvm use
node -v

nvm reads the file and switches the current shell.

Found '/home/josphat/hello/.nvmrc' with version <26>
Now using node v26.12.0 (npm v11.20.0)
v26.12.0

Two things trip people up here. First, nvm only exists in your interactive shell, so sudo node, cron jobs and systemd units never see it: they resolve /usr/bin/node, the NodeSource build, whatever nvm use says in your terminal. Second, each version costs real disk, 208 MB for Node 24 and 230 MB for Node 26, so remove old ones with nvm uninstall. Use nvm for development, and NodeSource for anything a service runs.

Upgrade to Node.js 26

Node.js 26 becomes the Active LTS on 28 October 2026, and Node 24 drops to maintenance on 20 October. Moving a NodeSource install across major lines is a repository switch followed by an ordinary upgrade, so you don’t need to uninstall anything first.

curl -fsSL https://deb.nodesource.com/setup_26.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt-get install -y nodejs

Apt unpacks the new major over the old one in place.

1 upgraded, 0 newly installed, 0 to remove and 2 not upgraded.
Need to get 42.3 MB of archives.
After this operation, 44.6 MB of additional disk space will be used.
Unpacking nodejs (26.12.0-1nodesource1) over (24.22.0-1nodesource1) ...

Global packages survive the upgrade, since they live in /usr/lib/node_modules outside the package, and pm2 still worked straight after. The runtime under your code has changed, though, so three things need attention after the jump:

  1. Rebuild native modules in each project with npm rebuild, because a module compiled against one Node ABI can refuse to load on the next
  2. Restart every Node service, since running processes keep the old binary until they restart
  3. Fix any corepack calls in scripts and CI, because Node 26 no longer ships it

The third one breaks builds that relied on corepack enable to get Yarn or pnpm, and it fails loudly rather than subtly. On Node 24 the command still printed a version, but after the upgrade our non-interactive test shell could not find it, so any CI step that calls it stops the pipeline:

bash: line 1: corepack: command not found

Corepack itself is still maintained, just no longer bundled, so the corepack project tells you to install it from npm with sudo npm install -g corepack, which keeps corepack enable and the packageManager field in package.json working as before. If you would rather drop it, install the package manager directly with sudo npm install -g pnpm or sudo npm install -g yarn; a pnpm and Turborepo monorepo needs the same change in its CI image. Our bcrypt test module loaded on Node 26 even before the rebuild, because it ships prebuilt Node-API binaries, but older modules that compile against V8 directly do not, so run the rebuild anyway.

Upgrading Node.js 24 to Node.js 26 on Ubuntu 26.04 with the NodeSource repository

The capture above is the whole move on our 26.04 test box: the repository points at node_26.x, apt unpacks 26 over 24, and the Express app answers from the new runtime after a restart.

Remove Node.js

Stop and remove the test service first, so systemd isn’t left with a unit that fails at every boot once /usr/bin/node is gone:

sudo systemctl disable --now hello-node
sudo rm /etc/systemd/system/hello-node.service
sudo systemctl daemon-reload

Then purge the package and delete the repository, both pins and the keyring the setup script added. Leaving the pin and repository behind is the trap, because a later apt install nodejs would quietly pull NodeSource’s build again instead of the one you expect.

sudo apt-get purge -y nodejs
sudo rm -f /etc/apt/sources.list.d/nodesource.sources /etc/apt/preferences.d/nodejs /etc/apt/preferences.d/nsolid /usr/share/keyrings/nodesource.gpg
sudo rm -rf /usr/lib/node_modules
sudo apt update

Purging Node 26 freed 290 MB and warned that /usr/lib/node_modules was not empty, because globally installed tools such as pm2 are not part of the package. That’s why the third command deletes the directory. Afterwards apt-cache policy nodejs falls back to the archive candidate, which confirms the NodeSource repository is gone. For nvm, delete ~/.nvm and remove the three NVM_DIR lines from ~/.bashrc.

Keep reading

Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) Ubuntu Upgrade Ubuntu 24.04 to Ubuntu 26.04 LTS (Step by Step) UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt CapRover Docker Install on Debian 13: Deploy Your First App Containers CapRover Docker Install on Debian 13: Deploy Your First App Configure Network NIC Teaming on Debian 12/11/10 Debian Configure Network NIC Teaming on Debian 12/11/10

Leave a Comment

Press ESC to close