LINESERVE
All tutorials
TutorialAll levels

How to Install Node.js on Ubuntu

Install Node.js on Ubuntu three ways — apt, NodeSource, or nvm. Real terminal output from a live 24.04 server, plus which method to actually pick.

Stephen NdegwaPublished Last updated 29 min read
Share

How to Install Node.js on Ubuntu

The command almost everyone reaches for first is sudo apt install nodejs. Depending on which Ubuntu you are running, that gives you anything from a currently supported Node.js to one that stopped receiving security fixes over a year ago — and Ubuntu tells you nothing either way.

This guide walks the three real ways to install Node.js on Ubuntu, tells you plainly which one to pick, and shows exactly what each leaves on your machine. Every command was run in order on a clean Ubuntu 24.04.4 server as an ordinary sudo user, and the terminal text you see is what that run produced — including the commands that failed, because you will hit those too.

In a hurry? If this is a server and you just want a current, supported Node.js, jump to Method A and run four commands. If it is your own development machine, go to Method B. The rest of this page explains why.

Which method should you use?

Read this table first. It will save you an hour.

Two terms it depends on, defined up front: npm is Node’s package manager, the tool you use to install JavaScript libraries and command-line tools; installing something globally (npm install -g) puts a tool on your whole machine rather than inside one project folder.

apt (Ubuntu’s own repo) NodeSource (apt, third-party repo) nvm (Node Version Manager)
Node version you get Whatever Ubuntu froze at release — 18.19.1 on 24.04 Current LTS — 24.18.0 today Any version you ask for
Stays up to date? No. Frozen for the life of the release Yes, via apt upgrade Yes, you choose when
Installs for Everyone on the machine Everyone on the machine Only your user
Needs sudo to install a global tool? Yes (or a workaround) Yes (or a workaround) No
Multiple Node versions side by side No No Yes
Survives a reboot Yes Yes Yes

Pick NodeSource if this is a server that runs one application. You get a current, supported Node that updates with the rest of the system, installed machine-wide so background services can see it.

Pick nvm if this is your development machine, or if you work on projects pinned to different Node versions. It installs into your home directory, needs no sudo, and switches versions per project.

Pick apt only if you don’t care which Node version you get — a throwaway box, a build step that just needs a JavaScript runtime, or another Ubuntu package that depends on it. It is the fastest to type and, on most Ubuntu releases, the least useful in production.

You can read just the section for your method. Verification, troubleshooting, and uninstall at the end apply to all three.

Conventions used in this guide

Every example uses one consistent set of values. The right-hand column tells you whether to keep the sample or substitute your own.

In this guide Value used Yours?
Linux username ubuntu Whatever user you log in as. It must be able to run sudo.
Server hostname app-01 Cosmetic — it is only what shows in the prompt. Keep yours.
Server IP address 203.0.113.10 Substitute your server’s real IP, from your provider’s dashboard.
Shell prompt ubuntu@app-01:~$ Yours shows your own user and host.
Verification folder ~/node-test Any name; this guide creates it for you.
Test app file ~/node-test/app.js Created for you in the verification section.
Test port 3000 Keep it unless something else already uses port 3000.
Node version installed 24.18.0 (Active LTS on 2026-07-19) Yours may be a later patch — expected and fine.

Two more terms, defined once:

  • LTS — “Long Term Support”. A Node.js release line that receives bug and security fixes for about three years. Node’s own guidance is that production applications run an LTS release rather than the newest one.
  • EOL — “End of Life”. A release line that receives no further fixes of any kind, including security fixes.

Commands to type are always in their own block. Output always sits in a separate block below, introduced by a sentence. If a block appears with no command above it, it is something the machine printed — never paste it back into your terminal.

Before you start

You need a machine running Ubuntu and a user account that can run sudo. That is all.

If this is a remote server, connect to it from your own computer’s terminal. On macOS and Linux open Terminal; on Windows open PowerShell or Windows Terminal. Then:

ssh [email protected]

Substitute your server’s IP address for 203.0.113.10, and your username for ubuntu if your provider gave you a different one. If you don’t have a server yet, or you’re still logging in as root, set that up first — Initial Ubuntu 24.04 Server Setup: Users, SSH, Firewall covers users, SSH keys and a firewall end to end. Working as root is the wrong habit to start with, and one of the methods below is built specifically around not being root.

Once connected, confirm who and where you are:

whoami
hostname
pwd

That printed:

ubuntu
app-01
/home/ubuntu

The pwd line matters: /home/ubuntu is your home directory, which is what ~ means throughout this guide. If yours shows something else, run cd ~ before continuing.

Next, find out which Ubuntu you are on — the whole next section turns on it:

lsb_release -a

The two lines that matter are the description and the codename:

Distributor ID: Ubuntu
Description:    Ubuntu 24.04.4 LTS
Release:    24.04
Codename:   noble

This machine is Ubuntu 24.04, codename noble. Note your own codename — it names your row in the table further down.

This guide uses curl — a tool for downloading things from the command line — in several places, so install it now regardless of which method you pick:

sudo apt update
sudo apt install -y curl
curl --version

The first line of the version output names it:

curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.10

Any 7.x or 8.x version is fine. Finally, check whether Node is already here:

node -v

On a clean Ubuntu system, Ubuntu’s “command not found” helper answers instead of Node:

Command 'node' not found, but can be installed with:
sudo apt install nodejs

That is Ubuntu making a suggestion, not a recommendation. The next section is why you should think before taking it.

What apt install nodejs actually gives you

Before installing anything, ask apt what it is offering. apt-cache policy reports what is installed and what apt would install, and changes nothing:

apt-cache policy nodejs

Here is the answer on Ubuntu 24.04, and it is the most important output on this page:

nodejs:
  Installed: (none)
  Candidate: 18.19.1+dfsg-6ubuntu5
  Version table:
     18.19.1+dfsg-6ubuntu5 500
        500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu noble/universe amd64 Packages

Installed: (none) confirms Node isn’t here yet. Candidate: 18.19.1 is what you would get. And Node.js 18 reached end of life on 30 April 2025 — upstream ships it no further fixes, security included.

This is not a bug in Ubuntu. Ubuntu freezes package versions when a release ships so the system stays stable for its whole supported life; 18.19.1 was current when 24.04 was published in April 2024. The consequence is that what you get depends entirely on which Ubuntu you run:

Ubuntu release apt gives you Upstream status on 2026-07-19
22.04 LTS (jammy) Node 12.22.9 EOL since April 2022
24.04 LTS (noble) Node 18.19.1 EOL since 30 April 2025
25.10 (questing) Node 20.19.4 EOL since 30 April 2026
26.04 LTS (resolute) Node 22.22.1 Maintenance LTS, supported to April 2027

Those package versions come from Ubuntu’s package index; the support dates come from Node’s release schedule.

So on Ubuntu 26.04, released in April 2026, apt install nodejs is a perfectly defensible choice — it gives you a supported Node 22. On 24.04, the most widely deployed LTS right now, it does not. Run the command on your own machine rather than trusting any guide’s number, including this one.

One nuance in that output: the noble/universe line says this package lives in Ubuntu’s universe component, which is community-maintained rather than covered by Canonical’s standard security commitment. Fixes do land there, but you are relying on Ubuntu backporting them rather than on upstream Node releases.

If none of that troubles you, skip down to Method C near the end of this guide. Otherwise, keep reading.

NodeSource publishes an apt repository carrying current Node.js builds for Ubuntu and Debian. Adding it means apt install nodejs gives you a supported Node, and apt upgrade keeps it current alongside everything else. This is the right choice for a server.

A repository is simply an extra source of packages apt may install from; a keyring is the cryptographic key apt uses to verify those packages genuinely came from NodeSource and weren’t altered in transit. The setup script installs both.

This method needs a 64-bit machine — the setup script accepts amd64 (standard Intel/AMD servers) and arm64, and exits with an error on anything else. Check yours:

uname -m

The lab machine reported:

x86_64

x86_64 is amd64 and aarch64 is arm64; either is fine to continue on. Anything else means this method won’t work for you — use Method B instead.

Step 1 — download the setup script

Make sure you’re in your home directory, then download the script there:

cd ~
curl -fsSL https://deb.nodesource.com/setup_lts.x -o nodesource_setup.sh

That prints nothing when it works — silence is success here. Confirm the file arrived:

ls -l nodesource_setup.sh

The listing shows its size:

-rw-rw-r-- 1 ubuntu ubuntu 3907 Jul 19 14:39 nodesource_setup.sh

A few thousand bytes is right. If it is much smaller, or curl printed an error, the download failed — the -f flag exists precisely so that a server error becomes a visible failure instead of an HTML error page silently saved as a shell script.

Note the URL: setup_lts.x, not a pinned version like setup_24.x. NodeSource keeps setup_lts.x aimed at the current LTS, so this same command keeps working after Node 24 hands over to Node 26. (setup_current.x is its counterpart for the newest release — but for a server, you want LTS.)

Step 2 — read the script before you run it

You are about to run a downloaded script with sudo, which gives it complete control of the machine. Looking first is a good habit, not paranoia. grep searches a file for lines matching some text, and -n makes it report line numbers — use it to find the line naming the Node version this script will configure:

grep -n 'NODE_VERSION=' nodesource_setup.sh

The match, with its line number:

114:NODE_VERSION="24.x"

That confirms setup_lts.x currently resolves to the Node 24 line, matching the Active LTS on nodejs.org. If grep prints nothing at all, it found no such line — treat that as a sign the download is not what you expected, and fetch it again rather than running it. To read the whole script, less nodesource_setup.sh shows it one screen at a time — press q to quit.

Step 3 — run the setup script

sudo -E bash nodesource_setup.sh

The -E flag keeps your environment, which matters if your network needs proxy settings. The script adds the repository, installs the signing key, and refreshes apt. Its closing lines:

Get:4 https://deb.nodesource.com/node_24.x nodistro InRelease [12.1 kB]
Get:6 https://deb.nodesource.com/node_24.x nodistro/main amd64 Packages [7732 B]
Fetched 19.9 kB in 1s (34.6 kB/s)
Reading package lists...
Building dependency tree...
Reading state information...
13 packages can be upgraded. Run 'apt list --upgradable' to see them.
2026-07-19 14:40:02 - Repository configured successfully.
2026-07-19 14:40:02 - To install Node.js, run: apt install nodejs -y

Repository configured successfully is the line to look for. Note it has not installed Node — it only set up the source. Errors mentioning GPG or NO_PUBKEY mean the key failed to install; re-run the script before continuing.

Check that apt is now offering something better:

apt-cache policy nodejs

The candidate has changed, and so has where it comes from:

nodejs:
  Installed: (none)
  Candidate: 24.18.0-1nodesource1
  Version table:
     24.18.0-1nodesource1 600
        500 https://deb.nodesource.com/node_24.x nodistro/main amd64 Packages
     24.17.0-1nodesource1 600
        500 https://deb.nodesource.com/node_24.x nodistro/main amd64 Packages

From 18.19.1 to 24.18.0, sourced from deb.nodesource.com instead of Ubuntu’s archive. (The 600 is a priority the script sets so NodeSource’s package wins over Ubuntu’s — it matters again when you uninstall.)

Step 4 — install Node.js

sudo apt install -y nodejs

This pulls a fairly large package. Trimmed to the lines that tell you what happened:

The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 13 not upgraded.
Get:1 https://deb.nodesource.com/node_24.x nodistro/main amd64 nodejs amd64 24.18.0-1nodesource1 [38.7 MB]
Setting up nodejs (24.18.0-1nodesource1) ...

Setting up nodejs (24.18.0-1nodesource1) with no error after it means you’re done.

Check both tools:

node -v
npm -v

Two version numbers, Node then npm:

v24.18.0
11.16.0

v24.18.0 matches the Active LTS published on nodejs.org exactly, and npm arrived inside the same package. Do not run sudo apt install npm afterwards: that pulls Ubuntu’s much older npm and drags Ubuntu’s Node 18 back in alongside your Node 24 — precisely the conflict you just avoided.

Now skip to “Verify it actually works”.

nvm, the Node Version Manager, installs Node into your home directory instead of machine-wide. That one difference buys you three things: no sudo for global tools, several Node versions side by side, and per-project version pinning. It is the standard tool for development work.

The trade-off: because nvm lives in your home directory and loads through your shell, Node installed this way is visible only to your user, in an interactive terminal session. Background services started by systemd — the program Ubuntu uses to launch and restart services automatically, including at boot — will not find it. That is fine on a laptop and a real limitation on a server, which is why Method A exists.

Step 1 — run the installer

cd ~
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.6/install.sh | bash

The v0.40.6 is a specific nvm release. Check nvm’s README for the current tag — the project deliberately publishes a pinned URL rather than a “latest” one, so guides go stale here more than anywhere else.

The installer clones nvm, then tells you what it did:

=> Close and reopen your terminal to start using nvm or run the following to use it now:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Those three lines were also appended to ~/.bashrc, the file bash reads every time you open an interactive shell. Confirm with tail, which prints the last few lines of a file:

tail -4 ~/.bashrc

The end of the file now reads:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Step 2 — load nvm into your current shell

Here is where most people get stuck. Try nvm right now, in the shell you ran the installer in:

command -v nvm

Nothing comes back at all, and the exit status is 1.

That is not a failed install. ~/.bashrc is read when a shell starts, and your current shell started before those lines existed. nvm is also not a program on disk — it is a shell function, which is why command -v nvm is the right check and which nvm will never work regardless.

Re-read the file:

source ~/.bashrc

Then check again:

command -v nvm
nvm --version

The function name, then its version:

nvm
0.40.6

nvm printing its own name means it is loaded. Every new terminal or SSH session from now on loads nvm automatically — you only need source this once.

Step 3 — install Node

Ask nvm for the current LTS:

nvm install --lts

nvm downloads a prebuilt binary, checks it, and switches you to it:

Downloading and installing node v24.18.0...
Downloading https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz...
######################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v24.18.0 (npm v11.16.0)
Creating default alias: default -> lts/* (-> v24.18.0 *)

Two lines matter. A checksum is a fingerprint of a file; nvm recomputes it and compares against the value Node publishes, so Checksums matched! means the download arrived intact and untampered. If it ever says otherwise, delete the download and retry rather than continuing. Creating default alias: default -> lts/* is nvm deciding what new shells will use — which turns out to matter a great deal, as Step 5 shows.

Confirm:

node -v
npm -v
which node

Versions, then the location:

v24.18.0
11.16.0
/home/ubuntu/.nvm/versions/node/v24.18.0/bin/node

That path is the tell: Node lives under your home directory, owned by you. Nothing was installed machine-wide.

Step 4 — install and switch between versions

Install a second version by major version number — here the previous LTS line, Node 22:

nvm install 22

nvm fetches it and switches to it:

Downloading https://nodejs.org/dist/v22.23.1/node-v22.23.1-linux-x64.tar.xz...
######################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v22.23.1 (npm v10.9.8)

Installing always switches you to what you just installed. Move back to the LTS:

nvm use 24
node -v

nvm use reports the switch and node -v agrees:

Now using node v24.18.0 (npm v11.16.0)
v24.18.0

List everything nvm knows about:

nvm ls

The top of that listing shows your installed versions and aliases. Below what’s shown here nvm also prints every LTS codename it recognises — about fifteen more lines, cut for length:

       v22.23.1 *
->     v24.18.0 *
         system * (-> v24.18.0)
default -> lts/* (-> v24.18.0 *)
node -> stable (-> v24.18.0 *) (default)
lts/jod -> v22.23.1 *
lts/krypton -> v24.18.0 *

The -> arrow on the left marks the version this shell is using. default -> lts/* is what new shells will pick up. Names like lts/krypton are codenames for LTS lines, usable anywhere a version number goes — nvm install lts/jod works fine.

The system line appears only because the machine behind this guide also had a machine-wide Node from Method A. If you installed nvm alone, you will not have a system entry, and its absence is correct rather than a fault.

Older versions work the same way. If a legacy project needs Node 20:

nvm install 20
node -v

The download, then confirmation that this shell switched to it:

Downloading and installing node v20.20.2...
Downloading https://nodejs.org/dist/v20.20.2/node-v20.20.2-linux-x64.tar.xz...
######################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v20.20.2 (npm v10.8.2)
v20.20.2

Return to the LTS before continuing:

nvm use 24
node -v

Back on 24:

Now using node v24.18.0 (npm v11.16.0)
v24.18.0

Step 5 — make your choice stick

This catches nearly everyone. nvm use changes only the shell you type it in. Prove it to yourself rather than taking it on faith. In your current terminal:

nvm use 22
node -v

This shell is now on Node 22:

Now using node v22.23.1 (npm v10.9.8)
v22.23.1

Now open a second terminal — a fresh SSH session to the same server, exactly as you did in “Before you start” — and in that new session run:

node -v

It answers with a completely different version:

v24.18.0

The new shell ignored your nvm use entirely and followed the default alias instead. That is the trap: switch a version, come back tomorrow, and your project is building against something else. To change what new shells get, set the default:

nvm alias default 24

Or track the LTS line generally, which is what nvm install --lts already did for you:

nvm alias default 'lts/*'

Either way, confirm where the default now points before you trust it:

nvm alias

The first line is the one you just set:

default -> lts/* (-> v24.18.0 *)
node -> stable (-> v24.18.0 *) (default)
stable -> 24.18 (-> v24.18.0 *) (default)
lts/* -> lts/krypton (-> v24.18.0 *)
lts/jod -> v22.23.1 *
lts/krypton -> v24.18.0 *

Step 6 — pin a version per project

If a project needs a specific Node, record it in a file called .nvmrc in that project’s root folder. Create a folder and the file:

mkdir -p ~/legacy-project
cd ~/legacy-project
echo "22" > .nvmrc

Now a bare nvm use in that folder reads the file:

nvm use

nvm names the file it found and switches accordingly:

Found '/home/ubuntu/legacy-project/.nvmrc' with version <22>
Now using node v22.23.1 (npm v10.9.8)

Commit .nvmrc to your repository and everyone on the project gets the same Node from one command. This pairs with pinning your dependencies properly — Tilde (~) vs. Caret (^) in package.json: Mastering npm Semantic Versioning for Node.js explains what the ^ and ~ symbols in package.json actually do.

Before moving on, return to your home directory so the pin doesn’t follow you into the next section:

cd ~
node -v

Outside the pinned folder you are back on the default:

v24.18.0

Method C — apt from Ubuntu’s repository

Now that you know which Node your release offers, here it is. Ubuntu splits Node and npm into separate packages, so you need both:

sudo apt update
sudo apt install -y nodejs npm

This installs a long list of dependencies. The line confirming the main package, from the end of that output:

Setting up nodejs (18.19.1+dfsg-6ubuntu5) ...

Then check what you got:

node -v
npm -v
which node

Node, npm, and the location — all machine-wide under /usr/bin:

v18.19.1
9.2.0
/usr/bin/node

One historical wrinkle you’ll meet in older guides: on long-past Ubuntu versions the binary was called nodejs while node was an unrelated package, so tutorials told you to create a symlink. That is over. On 24.04 both names work and give the same answer:

nodejs -v

Same version, either name:

v18.19.1

No symlink needed.

Verify it actually works

Whichever method you used, prove it by running a real program rather than printing a version number.

Create a folder and move into it:

mkdir -p ~/node-test
cd ~/node-test
pwd

pwd echoes back the folder you just made and moved into:

/home/ubuntu/node-test

Now create the file. The block below is a single command: it starts writing app.js at the <<'EOF' marker and stops at the closing EOF. Paste all of it, including that final EOF line, then press Enter:

cat > app.js <<'EOF'
const http = require('http');
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello from Node.js ' + process.version + '\n');
});
server.listen(3000, '127.0.0.1', () => {
  console.log('Listening on http://127.0.0.1:3000');
});
EOF

(Prefer an editor? nano app.js opens one; paste the lines between the two EOF markers, then press Ctrl+O, Enter, Ctrl+X to save and exit.)

Check the file landed:

ls -l

One file, a few hundred bytes:

total 4
-rw-rw-r-- 1 ubuntu ubuntu 296 Jul 19 14:38 app.js

Start it:

node app.js

The server announces itself, then goes quiet:

Listening on http://127.0.0.1:3000

The command does not return to a prompt. That is correct — the server is running and waiting. Leave it alone.

Open a second terminal window. If Ubuntu is running on the machine in front of you, that is all you need — skip to the curl command. If your Ubuntu is a remote server, log into it again in this second window first, and wait for the prompt to come back before typing anything else:

ssh [email protected]

Then, in that second session:

curl http://127.0.0.1:3000

The address 127.0.0.1 always means “the machine I am typing on”, which is why the curl has to run on the same machine as the app — from your laptop it would reach your laptop. Your app answers:

Hello from Node.js v24.18.0

That is Node parsing your JavaScript, starting an HTTP server, and serving a request. Your version number will be whatever you installed — on the apt method this same test printed Hello from Node.js v18.19.1.

Go back to the first terminal and press Ctrl+C to stop the server.

Because it listens on 127.0.0.1, this test server is not reachable from the internet. Serving an app to real visitors needs a reverse proxy, a process manager and a firewall — Deploying a Node.js App to Production on a VPS: The Complete Guide walks through that, and Building a Production-Ready REST API with Node.js, Express, and PostgreSQL takes the same starting point through to a real API with a database behind it.

The EACCES error you will hit with apt or NodeSource

Sooner or later you will install a command-line tool globally — a process manager, a framework’s CLI. With Node installed machine-wide (Methods A and C), this happens:

npm install -g pm2

This is a genuine failure, captured from the run behind this guide, and it is what you will see too. On the apt install, which bundles npm 9:

npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules'
npm ERR!  [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/usr/local/lib/node_modules'
npm ERR! }
npm ERR! 
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user

EACCES is “permission denied”: global packages go to a system directory your user cannot write to.

npm 10.6.0 changed the prefix from npm ERR! to npm error, so on a NodeSource install the same failure opens like this — different prefix, different directory, identical cause:

npm error code EACCES
npm error syscall mkdir
npm error path /usr/lib/node_modules/pm2

The obvious fix is sudo npm install -g pm2, and it does work. It is also how people end up with root-owned files through their home directory and an npm cache they can no longer write to. Prefer one of these.

Fix 1 — give npm a directory you own. Create it, point npm at it, and add it to your PATH so the shell can find what it installs:

mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

PATH is the list of folders your shell searches when you type a command name. Confirm npm accepted the setting:

npm config get prefix

npm reads the setting back:

/home/ubuntu/.npm-global

Because you edited ~/.bashrc, confirm the change actually survives a new shell rather than assuming it. Log out and back in (or open a second terminal), then check that your new folder is on PATH and install something without sudo:

echo "$PATH" | tr ':' '\n' | head -2
npm install -g pm2
which pm2

The first two entries of PATH in the fresh session, then where the tool landed:

/home/ubuntu/.npm-global/bin
/usr/local/sbin
/home/ubuntu/.npm-global/bin/pm2

Your directory comes first in PATH, and pm2 installed into your own home with no sudo — so the fix persists across logins.

If you use nvm, do not do this — setting a global npm prefix overrides nvm’s per-version directories and breaks version switching. nvm users need Fix 2 instead.

Fix 2 — use nvm. nvm avoids the problem entirely, because its global directory already sits inside your home. On the nvm install, the identical command needed no configuration and no sudo:

npm install -g pm2
which pm2

It landed inside nvm’s directory for the version you’re on:

/home/ubuntu/.nvm/versions/node/v24.18.0/bin/pm2

Note this is per Node version — install a new Node with nvm and you reinstall your global tools for it.

Does it survive a reboot?

Rather than assume, the test machine was rebooted with all three installs present and checked again. You can reproduce this. Reboot:

sudo reboot

Your SSH session will disconnect immediately — that is expected, not a fault. Wait about thirty seconds, then reconnect with the same ssh [email protected] command you used earlier.

Whichever method you used, start here:

uptime -p
node -v

A freshly booted machine, and Node still answering:

up 1 minute
v24.18.0

up 1 minute confirms this really is a machine that just restarted. Your version number will be whichever you installed — on the apt method it reports v18.19.1.

If you used Method B (nvm), check that nvm itself came back too:

command -v nvm
nvm current
which node

nvm loaded on its own and the default version is still selected:

nvm
v24.18.0
/home/ubuntu/.nvm/versions/node/v24.18.0/bin/node

If you used Method A (NodeSource), check the machine-wide copy directly:

/usr/bin/node -v

The NodeSource install came through the restart untouched:

v24.18.0

A Method C reader running that same command sees Ubuntu’s version — v18.19.1 — because /usr/bin/node is whatever apt put there. On an nvm-only machine it reports No such file or directory, which is correct: there is no machine-wide Node to find.

So all three methods persist across reboots. The thing that does not persist is nvm use, which is why Step 5 has you set nvm alias default.

Troubleshooting

nvm: command not found after installing nvm. Your shell started before nvm was added to ~/.bashrc. Run source ~/.bashrc, or open a new terminal. If it still fails, confirm the lines are present with grep NVM_DIR ~/.bashrc. Using zsh instead of bash? The same lines need to be in ~/.zshrc.

which nvm returns nothing even though nvm works. Expected. nvm is a shell function, not a program on disk, so which cannot see it. Use command -v nvm.

node -v shows a different version than you expect. You have more than one Node installed and the wrong one comes first in your PATH. List them all:

which -a node

On a machine with both NodeSource and nvm:

/home/ubuntu/.nvm/versions/node/v24.18.0/bin/node
/usr/bin/node
/bin/node

which -a lists every match in PATH order and the first one wins, so nvm’s copy shadows the machine-wide one. (/usr/bin/node and /bin/node are the same file — /bin is a symlink to /usr/bin on modern Ubuntu.) This is the usual explanation for node -v in your terminal disagreeing with the version a systemd service runs: services get the machine-wide copy, your terminal gets nvm’s. With nvm, nvm use <version> fixes the current shell and nvm alias default <version> fixes new ones. Better still, pick one method per machine.

E: Unable to locate package nodejs. Your package lists are stale or empty. Run sudo apt update first. On a minimal install, also confirm the universe component is enabled with sudo add-apt-repository universe.

NO_PUBKEY or GPG errors from apt after adding NodeSource. The signing key did not install. Re-run sudo -E bash nodesource_setup.sh, then confirm /usr/share/keyrings/nodesource.gpg exists.

Unsupported architecture from the NodeSource script. It accepts only amd64 and arm64. On a 32-bit ARM board, use nvm or Node’s official binaries instead.

npm warns a new major version is available. Informational, not an error. The npm bundled with your Node is supported; update it with npm install -g npm@latest if you want, subject to the EACCES caveats above.

Your app fails to build a native module. Some npm packages compile C++ during install. Install the toolchain: sudo apt install -y build-essential.

Uninstalling

Removing an apt or NodeSource install

Remove the package:

sudo apt purge -y nodejs

If you installed Ubuntu’s npm separately (Method C), include it: sudo apt purge -y nodejs npm.

For NodeSource you also want the repository and its key gone, or apt keeps checking a source you no longer use. NodeSource’s own documentation is out of date here. It tells you to remove /etc/apt/sources.list.d/nodesource.list and /etc/apt/keyrings/nodesource.gpg.

Do not bother running those — neither path exists on a current install. This is what happens if you do, and it is why this section exists:

rm: cannot remove '/etc/apt/sources.list.d/nodesource.list': No such file or directory
rm: cannot remove '/etc/apt/keyrings/nodesource.gpg': No such file or directory

The current setup script writes a different filename in a newer format and puts the key elsewhere. Confirm what is actually on your machine:

ls -l /etc/apt/sources.list.d/nodesource.sources /usr/share/keyrings/nodesource.gpg

Both real, both owned by root:

-rw-r--r-- 1 root root  155 Jul 19 14:40 /etc/apt/sources.list.d/nodesource.sources
-rw-r--r-- 1 root root 1207 Jul 19 14:40 /usr/share/keyrings/nodesource.gpg

The script also writes two pin files — the source of that 600 priority you saw earlier — which nobody’s instructions mention at all. Remove all four, then refresh apt:

sudo rm -f /etc/apt/sources.list.d/nodesource.sources \
           /usr/share/keyrings/nodesource.gpg \
           /etc/apt/preferences.d/nodejs \
           /etc/apt/preferences.d/nsolid
sudo apt update

Confirm apt has fallen back to Ubuntu’s own package:

apt-cache policy nodejs

Nothing installed, and the candidate is Ubuntu’s again:

nodejs:
  Installed: (none)
  Candidate: 18.19.1+dfsg-6ubuntu5
  Version table:
     18.19.1+dfsg-6ubuntu5 500
        500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu noble/universe amd64 Packages

If NodeSource changes these paths again, find them rather than guessing: grep -rl nodesource /etc/apt/sources.list.d/ /etc/apt/preferences.d/.

Removing a single Node version from nvm

List what you have with nvm ls, then remove one by version:

nvm uninstall 20

nvm names what it removed:

Uninstalled node v20.20.2

Substitute whichever version you want gone — the output above is from clearing the Node 20 installed back in Step 4. You cannot remove the version currently in use; nvm use something else first.

Removing nvm entirely

Two parts, and skipping the second is the usual mistake. First deactivate and delete the directory:

nvm deactivate
rm -rf ~/.nvm

nvm deactivate reports the PATH change; rm -rf prints nothing at all:

/home/ubuntu/.nvm/*/bin removed from ${PATH}

That removes nvm and every Node it installed — but the three lines the installer added to ~/.bashrc remain, now pointing at a directory that no longer exists. Find them:

grep -n 'NVM_DIR' ~/.bashrc

Three lines, with their line numbers:

119:export NVM_DIR="$HOME/.nvm"
120:[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
121:[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Open the file with nano ~/.bashrc, delete those three lines, then save with Ctrl+O, Enter, Ctrl+X. Open a new terminal and check both commands are gone:

command -v nvm
node -v

command -v nvm prints nothing, and Ubuntu’s helper answers for node:

Command 'node' not found, but can be installed with:
sudo apt install nodejs

That sudo apt install nodejs line is Ubuntu’s suggestion inside its own message, not an instruction from this guide. You are back where you started, cleanly.

Keeping Node up to date

NodeSource: Node updates with everything else, since it is now just another apt package:

sudo apt update && sudo apt upgrade -y

This stays inside the major version line you configured. Moving from Node 24 to Node 26 later means re-running setup_lts.x after the LTS rolls over.

nvm: install the new version and repoint the default:

nvm install --lts
nvm alias default 'lts/*'

Old versions stay installed until you nvm uninstall them, so rolling back is easy.

apt (Ubuntu’s own): you don’t. The version is frozen for the life of the Ubuntu release. A newer Node means switching to one of the other two methods, or upgrading Ubuntu itself.

Check Node’s release schedule before you upgrade. As of 19 July 2026, v24 is Active LTS, v22 is in maintenance until April 2027, and both v20 and v25 reached end of life earlier in 2026 — a reminder that “it was fine last year” is not a security posture. Node is one of several things worth keeping patched on a public server; 8 Essential Security Steps for Every Production VPS covers the rest.

Frequently asked questions

Which Node version should I install? The current LTS — v24.18.0 as of 19 July 2026 — for anything you care about. Node’s own guidance is that production applications run Active or Maintenance LTS. Use the newest release only if you specifically need something in it.

Do I need to install npm separately? With NodeSource and nvm, no — npm is bundled. With Ubuntu’s apt package, yes: sudo apt install -y npm.

Does this work on Ubuntu 22.04 or 26.04? The three methods are the same on every current Ubuntu; what changes is the version apt offers, listed in the table earlier. This guide was verified end to end on 24.04 only, so on another release run apt-cache policy nodejs yourself to see what you’d be getting before choosing. Note that on 26.04 the apt route is genuinely reasonable — it ships a supported Node 22.

Can I use nvm and NodeSource on the same machine? Technically yes, and the machine behind this guide ran both. But nvm’s Node shadows the machine-wide one in your terminal while systemd services keep using the machine-wide copy, which turns “which Node is actually running?” into a question you must answer every time something breaks. Pick one.

Does this work on ARM servers? nvm downloads a binary matching your architecture. The NodeSource script accepts amd64 and arm64 only and exits with Unsupported architecture on anything else, including 32-bit ARM. Check yours with uname -mx86_64 is a standard 64-bit Intel/AMD server, aarch64 is 64-bit ARM.

Where did my global tools go after switching Node versions with nvm? Each nvm version keeps its own global package directory, so they don’t carry over. Reinstall them, or copy them across at install time with nvm install 24 --reinstall-packages-from=22.

Run it on a server built for your users

Everything above was verified on a 1 vCPU / 2 GB machine — the same footprint as a Lineserve Linux VPS S1: 1 vCPU, 2 GB RAM, 20 GB SSD, 1 TB transfer and a dedicated IPv4 address, at KES 1,746 / TZS 33,775 / NGN 21,616 per month (roughly $13.51). A Node app that outgrows it steps up to M1 — 2 vCPU, 4 GB RAM, 80 GB SSD, 3 TB transfer, dedicated IPv4 — at KES 4,418 / TZS 85,450 / NGN 54,688 (roughly $34.18).

Lineserve runs its own regions in Nairobi, Dar es Salaam and Lagos, so a Node app serving East or West African users answers from a local region instead of a datacenter on another continent. You pay in your own currency — M-Pesa and other mobile money, bank transfer or card in Kenya and Tanzania; card or bank transfer in Naira in Nigeria — with no forex conversion required.

New to it? Getting Started with Lineserve Cloud: Launch Your First VPS takes you from signup to a running server, and you can come straight back here to install Node on it.