Debian

Install Node.js 24 LTS on Debian 13 / 12

Debian 13 (Trixie) ships with Node.js 20 in its default repositories. That version reached end of life in April 2026, so if you are starting a new project or maintaining an existing one, you should be on Node.js 24. It landed as Active LTS “Krypton” in October 2025 and brings V8 13.6, npm 11, Express 5 as the default, a permission model that is no longer experimental, and a built-in test runner with proper describe blocks.

Original content from computingforgeeks.com - post 164323

We tested three install methods on a fresh Debian 13 VM, built an Express 5 API with two endpoints, ran the built-in test runner, exercised the permission model and SQLite module, and set up a systemd service. Everything below is real output, not fabricated. For the same guide on other distributions, see our Ubuntu 24.04 and Rocky Linux / AlmaLinux guides.

Tested March 2026 on Debian 13.4 (Trixie) with Node.js 24.14.0, npm 11.9.0, V8 13.6.233.17, gcc 14.2

Requirements

  • Debian 13 (Trixie) or Debian 12 (Bookworm) with sudo access
  • About 80 MB disk space for Node.js and npm
  • Port 3000/tcp open if running web applications

What Debian Ships by Default

Neither Debian 13 nor 12 includes a current Node.js version in their default repositories:

ComponentDebian 13 (Trixie)Debian 12 (Bookworm)
Default Node.js20.19.2 (EOL)18.19.x (EOL)
gcc14.212.2
Python 33.133.11

The install methods below work on both releases.

NodeSource Repository (Production)

NodeSource packages Node.js as .deb files with automatic apt updates. This is the recommended method for production servers.

If the default Debian nodejs package is installed, remove it first to avoid conflicts:

sudo apt remove -y nodejs npm 2>/dev/null; true

Add the NodeSource repository and install:

curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
sudo apt install -y nodejs

Confirm the version and runtime details:

node -v
npm -v
node -e "console.log('V8:', process.versions.v8, '| OpenSSL:', process.versions.openssl, '| Modules:', process.versions.modules)"

From our Debian 13 test VM:

v24.14.0
11.9.0
V8: 13.6.233.17-node.41 | OpenSSL: 3.5.5 | Modules: 137

The module ABI version 137 is worth noting. If you upgrade from Node.js 22 (which uses 134), all native addons need a rebuild.

NVM (Development)

NVM installs Node.js in your home directory and lets you switch between multiple versions per project. No sudo needed for global npm installs.

NVM_VER=$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep tag_name | cut -d \" -f4)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VER}/install.sh | bash
source ~/.bashrc

Install Node.js 24 and set it as default:

nvm install 24
nvm alias default 24

Switch between versions with nvm use 22 or nvm use 24. Each version gets its own isolated global npm packages. Keep in mind that NVM installs to ~/.nvm/, so systemd services and other users cannot access it. For production services, stick with NodeSource.

Binary Tarball (Air-gapped Systems)

Download the prebuilt binary directly from nodejs.org:

NODE_VER=$(curl -s https://nodejs.org/dist/latest-v24.x/ | grep -oP 'node-v\K[0-9.]+' | head -1)
curl -sLO https://nodejs.org/dist/v${NODE_VER}/node-v${NODE_VER}-linux-x64.tar.xz
sudo tar xJf node-v${NODE_VER}-linux-x64.tar.xz -C /usr/local --strip-components=1
rm node-v${NODE_VER}-linux-x64.tar.xz

Verify with node -v and npm -v.

Build Tools for Native Modules

Some npm packages compile native C/C++ addons. If you see gyp ERR! during an install, the fix is always the same:

sudo apt install -y build-essential

Debian 13 pulls in gcc 14.2 and make. Debian 12 gets gcc 12.2 instead. Both compile native Node.js modules without issues.

Express 5 API Demo

Express 5.2.1 is now what you get from npm install express. Quick sanity check:

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

npm pulls in Express 5.2.1 with 22 dependencies:

[email protected] /home/debian/node-demo
└── [email protected]

Create a server with two endpoints:

vi ~/node-demo/server.js
const express = require("express");
const os = require("os");
const app = express();

app.get("/", (req, res) => {
  res.json({
    runtime: "Node.js " + process.version + " on Debian " + os.release().split("-").pop(),
    v8: process.versions.v8,
    openssl: process.versions.openssl,
    arch: process.arch,
    memoryMB: Math.round(process.memoryUsage().rss / 1024 / 1024)
  });
});

app.get("/env", (req, res) => {
  res.json({
    nodeEnv: process.env.NODE_ENV || "development",
    pid: process.pid,
    uptime: Math.round(process.uptime()) + "s",
    cwd: process.cwd()
  });
});

app.listen(3000, () => console.log("Listening on :3000"));

Start the server and test both endpoints:

node server.js &
curl -s http://localhost:3000/ | python3 -m json.tool

Real response from our Debian 13 VM:

{
    "runtime": "Node.js v24.14.0 on Debian amd64",
    "v8": "13.6.233.17-node.41",
    "openssl": "3.5.5",
    "arch": "x64",
    "memoryMB": 64
}

The /env endpoint shows the process details:

curl -s http://localhost:3000/env | python3 -m json.tool
{
    "nodeEnv": "development",
    "pid": 2181,
    "uptime": "2s",
    "cwd": "/home/debian/node-demo"
}

Stop the server with kill %1.

Built-in Test Runner

Node.js 24 includes a test runner in node:test that supports describe/test blocks, async tests, and multiple reporters. No npm dependencies needed.

vi ~/node-demo/test.mjs

Write tests that verify the runtime and exercise new Node.js 24 features:

import { test, describe } from "node:test";
import assert from "node:assert";

describe("Core checks", () => {
  test("running on Node.js 24", () => {
    assert.ok(process.version.startsWith("v24"));
  });
  test("V8 13.6", () => {
    assert.ok(process.versions.v8.startsWith("13.6"));
  });
});

describe("New features", () => {
  test("URLPattern is global", () => {
    const p = new URLPattern({ pathname: "/api/:version/users/:id" });
    const m = p.exec({ pathname: "/api/v2/users/99" });
    assert.strictEqual(m.pathname.groups.version, "v2");
    assert.strictEqual(m.pathname.groups.id, "99");
  });
  test("Error.isError works", () => {
    assert.strictEqual(Error.isError(new TypeError("test")), true);
    assert.strictEqual(Error.isError("not an error"), false);
  });
  test("fetch is global", async () => {
    assert.strictEqual(typeof fetch, "function");
  });
});

Run them:

node --test test.mjs

5 tests across 2 suites, all green:

▶ Core checks
  ✔ running on Node.js 24 (0.94482ms)
  ✔ V8 13.6 (0.278318ms)
✔ Core checks (2.82456ms)
▶ New features
  ✔ URLPattern is global (1.371578ms)
  ✔ Error.isError works (0.215904ms)
  ✔ fetch is global (0.199697ms)
✔ New features (2.150982ms)
ℹ tests 5
ℹ suites 2
ℹ pass 5
ℹ fail 0
ℹ duration_ms 76.385397

Permission Model

The --permission flag (no longer experimental in Node.js 24) restricts what a process can access. Without it, everything works normally. With it, file system and network access are denied unless explicitly allowed.

echo 'const fs = require("fs"); console.log(fs.readFileSync("/etc/hostname", "utf8").trim());' > /tmp/ptest.js
node /tmp/ptest.js
node --permission /tmp/ptest.js

The first run prints the hostname. The second throws an access error:

test-debian-13-computingforgeeks-com
node:fs:440
    return binding.readFileUtf8(path, stringToFlags(options.flag));
                   ^
Error: Access to this API has been restricted

Grant specific access with --allow-fs-read:

node --permission --allow-fs-read=/etc /tmp/ptest.js

Now it reads the file because /etc is explicitly allowed. This is useful for sandboxing untrusted scripts or locking down production deployments.

Built-in SQLite

Node.js 24 includes SQLite through the node:sqlite module. It works with in-memory and file-based databases:

node -e "
const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE packages (name TEXT, version TEXT)');
db.exec(\"INSERT INTO packages VALUES ('express', '5.2.1')\");
db.exec(\"INSERT INTO packages VALUES ('node', '24.14.0')\");
console.log(db.prepare('SELECT * FROM packages').all());
db.close();
"

Real output from Debian 13:

[
  [Object: null prototype] { name: 'express', version: '5.2.1' },
  [Object: null prototype] { name: 'node', version: '24.14.0' }
]

As of v24.14.0, the SQLite module still prints an ExperimentalWarning. It works, but the API could change before it is fully stable.

systemd Service

For production, let systemd manage your Node.js app. Create a service user and copy the app:

sudo useradd -r -s /sbin/nologin nodeapp
sudo mkdir -p /opt/myapp
sudo cp ~/node-demo/server.js /opt/myapp/
sudo cp -r ~/node-demo/node_modules /opt/myapp/
sudo cp ~/node-demo/package.json /opt/myapp/
sudo chown -R nodeapp:nodeapp /opt/myapp

Create the unit file:

sudo vi /etc/systemd/system/nodeapp.service
[Unit]
Description=Node.js Express App
After=network.target

[Service]
Type=simple
User=nodeapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now nodeapp

The service starts immediately and restarts on crash after a 5-second delay:

sudo systemctl status nodeapp
● nodeapp.service - Node.js Express App
     Loaded: loaded (/etc/systemd/system/nodeapp.service; enabled; preset: enabled)
     Active: active (running)
   Main PID: 2377 (MainThread)
     Memory: 19.7M
        CPU: 137ms
     CGroup: /system.slice/nodeapp.service
             └─2377 /usr/bin/node server.js

Notice the NODE_ENV=production environment variable is picked up by the app:

curl -s http://localhost:3000/env | python3 -m json.tool
{
    "nodeEnv": "production",
    "pid": 2377,
    "uptime": "2s",
    "cwd": "/opt/myapp"
}

View logs with sudo journalctl -u nodeapp -f. For cluster mode and zero-downtime reloads, see PM2 process manager.

Upgrade from Node.js 22

Swap the NodeSource repository:

sudo apt remove -y nodejs
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
sudo apt install -y nodejs

Then rebuild native modules in every project:

cd /path/to/your/project
npm rebuild

NODE_MODULE_VERSION changed from 134 to 137, so native addons compiled for 22 won’t load. If npm rebuild doesn’t fix it, delete node_modules entirely: rm -rf node_modules package-lock.json && npm install.

Node.js 22 vs 24 on Debian

FeatureNode.js 22 (Jod)Node.js 24 (Krypton)
SupportMaintenance LTS until Apr 2027Active LTS until Apr 2028
V812.413.6 (Float16Array, RegExp.escape(), Error.isError())
npm10.x11.9 (faster installs)
Express (default)4.x5.x
Permission model--experimental-permission--permission (stable)
URLPatternRequires importGlobal
SQLiteExperimentalWorks (still shows ExperimentalWarning)
Test runnerBasicdescribe blocks, global setup/teardown
NODE_MODULE_VERSION134137

Stable on 22? No rush. Upgrade when you need npm 11, the V8 13.6 improvements, or when 22 hits end-of-life.

Yarn and Corepack

Yarn Classic:

sudo npm install -g yarn
yarn --version

Installs Yarn 1.22.22. For Yarn 4.x (Berry), enable Corepack first:

sudo corepack enable
cd /path/to/your/project
corepack use yarn@stable

The sudo on corepack enable is required with NodeSource because it creates symlinks in /usr/bin.

Uninstall

NodeSource:

sudo apt remove --purge -y nodejs
sudo rm -f /etc/apt/sources.list.d/nodesource.list
sudo apt update

NVM:

nvm uninstall 24
rm -rf ~/.nvm

If the default Debian nodejs package is also present, remove it separately: sudo apt remove --purge -y nodejs npm.

Node.js 24 on Debian is a clean install regardless of which method you pick. The official release notes have the full list of breaking changes if you are upgrading a complex project.

Related Articles

CentOS How to install and manage Flatpak applications on Linux Databases Configure MariaDB Replication on Ubuntu / Debian Security Install Linux Malware Detect on CentOS / Fedora / Ubuntu / Debian Debian Run Debian 13 (Trixie) on OpenStack or KVM

Leave a Comment

Press ESC to close