Node.js on Ubuntu: Installation for Sustainable Development Environments
Node.js continues as the backbone for JavaScript-driven backend systems, powering APIs at scale. Installing Node.js on Ubuntu isn’t just about running apt install
. The goal: an environment that aligns with your CI/CD flows, supports long-term updates, and avoids dependency hell.
Clean State: Purging Legacy Node.js Installs
Legacy system-wide Node.js can sabotage newer builds, particularly if you shift between LTS and current releases. Critically, start by sanitizing your environment:
sudo apt-get remove --purge nodejs npm
which node && rm -f $(which node)
which npm && rm -f $(which npm)
Global npm packages installed by root or another user can linger:
npm list -g --depth=0 || true
If unsure, flush the npm cache:
npm cache clean --force
Note: Pay attention to any locally managed ~/.npm-global
or .nvm
directories. Stray PATH entries cause version confusion.
Selecting the Installation Channel: Official PPA or nvm?
- Ubuntu APT repositories: Almost always behind, occasionally years out-of-date.
- NodeSource PPA: Single-version, system-wide, safe for production. Zero fuss with updates.
- nvm: Per-user, multi-version, sandboxed. Strong isolation; CI-friendly for testing across versions.
Method | Use case | Version switching | System-wide? |
---|---|---|---|
NodeSource PPA | Production, single version | No | Yes |
nvm | Development, multiple versions | Yes | No |
Guideline: Production = PPA. Polyglot dev = nvm.
Fast Path: Install Node.js 18.x with NodeSource PPA
As of this writing, Node.js 18.x LTS is stable. This method remains repeatable for future LTS versions—swap the version in the setup script as needed.
sudo apt-get update
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
Confirm binaries:
node -v # Expected: v18.15.0 (or higher)
npm -v # Expected: 9.x+
which node # Should point to /usr/bin/node
Known issue: On some legacy Ubuntu images, node
only exists as nodejs
. Create a symlink if required.
sudo ln -s /usr/bin/nodejs /usr/bin/node
Add Build Essentials — Don’t Ignore Native Module Requirements
Compilation of native modules (node-gyp
dependencies) is a frequent stumbling block:
sudo apt-get install -y build-essential python3
Without these, you’ll see errors like:
npm ERR! gyp ERR! build error
This can break packages like bcrypt
, canvas
, or sharp
.
Multi-Version: Managing Node.js with nvm
Development projects with conflicting engine requirements? nvm provides flexible, per-shell Node.js versioning. Minimal risk to global config, useful for CI, especially matrix builds.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
# Activate nvm in current shell (adjust for your shell)
source ~/.bashrc
Install latest LTS and set as default:
nvm install --lts
nvm alias default lts/*
Switch between versions on demand:
nvm install 16
nvm use 16
Side note: nvm manipulates $PATH
, so any running terminal needs a reload.
Permissions: Avoid Global npm sudo Pitfalls
Common mistake: sudo npm install -g ...
leads eventually to permissions errors and brittle setups. Safer alternatives:
- Configure a user-level global prefix (
npm config set prefix ~/.npm-global
) and adjust your PATH. - For production, stick to the OS package manager.
- npm official docs detail this problem and solution.
Sanity Check: Minimal HTTP Server
Verify install integrity with a trivial Node.js service:
// app.js
const http = require('http')
const server = http.createServer((_, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'})
res.end('Node.js running on Ubuntu\n')
})
server.listen(3000, () =>
console.log('Listening at http://127.0.0.1:3000/')
)
Launch:
node app.js
# Or explicitly: /usr/bin/node app.js
Output should be:
Listening at http://127.0.0.1:3000/
Browse to http://localhost:3000
—a plain response confirms end-to-end functionality.
Troubleshooting
node -v
returns "command not found": Recheck install method, shell session, and PATH.- Global modules missing post-install: Did you switch Node.js versions (
nvm use
)? Each version has its own module tree. - Version mismatch on fresh shell: Add nvm's init stanza to
.bashrc
or.zshrc
.
Non-Obvious Tips
nvm use
can be auto-triggered by adding a.nvmrc
file with a version number in your project root.- PPA installs can survive unattended upgrades, but watch for Node.js major bump—API incompatibilities may silently break builds.
- On Docker-based pipelines, pair the Node.js base image version with your deployment method for consistency.
Node.js on Ubuntu isn’t inherently difficult, but predictability in deployment—and easy handoff to the next engineer—hinges on rigor at this step. Avoid shortcuts now to save deep-dive debugging later.