Installing Node.js
Node.js is a server-side JavaScript runtime that uses the V8 engine from Google Chrome. This article explains how to install Node.js on a Linux server, run a first application, and manage it as a system service — for developers who want to run a backend or API in JavaScript.
Prerequisites
- A Linux server (Ubuntu 22.04 or 24.04, Debian 12, or another distribution with systemd)
- Access to the server via SSH with a user that has `sudo` rights
- No existing Node.js installation that conflicts (check with `node --version`)
Installing Node.js
The fastest route is the NodeSource repository. It offers more recent versions than the standard distro packages. Ubuntu 22.04, for example, provides Node.js 12 out of the box — which has been end-of-life since April 2022. NodeSource gives you Node.js 20 LTS or 22 LTS, depending on what you choose.curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejsThis adds the NodeSource repository to your apt sources and installs both `nodejs` and `npm`. The `-E` flag with sudo ensures your own environment variables are preserved, which NodeSource needs to select the correct repository.
If you choose Node.js 20 instead of 22, replace `setup_22.x` with `setup_20.x`. Node 20 is LTS until April 2026, Node 22 until April 2027. For a new server, 22 is the wiser choice; you will need to migrate less soon.
Running a first application
Create a project directory and a minimal HTTP script:mkdir ~/node-app && cd ~/node-app
npm init -yThe `npm init -y` command creates a `package.json` with default values. You can adjust it later; for now, it is enough to be able to install dependencies.
Create a file `server.js`:
const http = require('http');</p><p>const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Node.js runs on Pixzo\n');
});</p><p>server.listen(3000, () => {
console.log('Server listening on port 3000');
});This is deliberately the bare `http` module, not Express. For a first test, you want to see that the runtime works, not that a framework wraps it neatly. Express comes later.
Start the server:
node server.js
Open a second SSH session and test:
curl http://localhost:3000
You will see the line of text that the script returns. The server is running — but only as long as your SSH session is open. Close the terminal and the server dies. For a real deployment, you want a process manager or a systemd service.
Running the application as a systemd service
systemd is the standard init system on almost every modern Linux distribution. It restarts your application on a crash, starts it when the server boots, and logs neatly to journald.Create a service file:
sudo nano /etc/systemd/system/node-app.service
[Unit]
Description=Node.js application
After=network.target</p><p>[Service]
ExecStart=/usr/bin/node /home/username/node-app/server.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
User=username</p><p>[Install]
WantedBy=multi-user.targetReplace `username` with your own username. The `User=` field is important: do not run the service as root. If your Node.js application is compromised, the attacker will only have the rights of that single user, not of the entire system.
sudo systemctl daemon-reload
sudo systemctl enable --now node-app`enable` ensures the service starts at boot, `--now` starts it immediately. Check the status:
sudo systemctl status node-app
You will see `active (running)` and the latest log lines. The server now runs independently of your SSH session.
Verification
curl http://localhost:3000
node --version
npm --versionThe first returns the text of your application. The second and third show the Node.js and npm versions, respectively. Node.js 22.x and npm 10.x are the versions you should see after a fresh installation via NodeSource.
Common problems
Port 3000 is already in use. You will see an error message `EADDRINUSE`. Find the process holding the port with `sudo lsof -i :3000` and stop it, or change the port in your script to, for example, 3001.`node` is not found in the systemd service. The service uses an absolute path to the binary. Check with `which node` where your Node.js installation is located and adjust `ExecStart`. With a NodeSource installation, this is usually `/usr/bin/node`, but if you installed Node.js via nvm or another version manager, it is somewhere else and the path will not work.
The service starts but keeps restarting. View the logs with `sudo journalctl -u node-app -n 50`. If there is an error in your JavaScript, you will see it here. The `Restart=always` setting will keep restarting the service, even on a syntax error — the logs tell you what is wrong.
What this costs you
This setup is simple and robust, but not complete. There is no reverse proxy in front of it, no HTTPS, no process manager with clustering. For a public server, you put Nginx or Caddy in front of it, which handles TLS termination and forwards traffic to port 3000. For an application that wants to use multiple CPU cores, you need the cluster module from Node.js or a process manager like PM2 — systemd can only run one process per service.That is the trade-off: this article gives you a running Node.js server in ten minutes. You build scalable architecture afterward, once the first version proves itself. Self-hosting sounds wonderful, until something falls over on Sunday evening at half past eleven. Then you are not a customer waiting on the helpdesk. Then you are the helpdesk — and then it is nice that you know why the service is set up the way it is.