nvm auf Ubuntu Server einrichten: Node.js-Versionen, systemd und Cron
nvm auf Ubuntu Server: Node.js LTS installieren, Standardversion und .nvmrc setzen, Versionen wechseln und Node-Apps zuverlässig per systemd und Cron starten.
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Mit nvm auf Ubuntu Server installieren Sie Node.js pro Benutzer, wechseln zwischen Versionen und pinnen je Projekt eine Version per .nvmrc. Diese Anleitung zeigt den kompletten Weg auf Ubuntu 24.04 LTS, getestet mit nvm 0.40.8, Node.js 24.22.0 und 22.23.3, und erklärt die drei Stellen, an denen nvm im Serverbetrieb regelmäßig scheitert: sudo, systemd und Cron.
Voraussetzungen
- Ubuntu Server 24.04 LTS (der Test lief auf Ubuntu 24.04.5 LTS), x86_64. nvm lädt fertige Node-Binärpakete von nodejs.org, ein Compiler ist dafür nicht nötig.
- Mindestens 1 CPU-Kern und 1 GB RAM. Der Test lief in einer Instanz mit 2 Kernen und 1 GiB RAM, die Beispiel-App belegte als Dienst rund 10 MB.
- Freier Speicher im Home-Verzeichnis:
~/.nvmbelegte im Test mit einer installierten Version samt Download-Cache 316 MB. Jede weitere Version braucht zusätzlichen Platz. - Ein normaler Benutzer mit sudo-Rechten, im Beispiel
deploy. Wie Sie Benutzer und sudo-Rechte anlegen, zeigt die Anleitung Linux-Benutzer, Gruppen und Rechte. curlund ausgehender HTTPS-Zugriff aufraw.githubusercontent.comundnodejs.org.
| Eckdaten | Wert im Test |
|---|---|
| nvm-Version | v0.40.8 (aktuelles Release laut GitHub) |
| Installationsort | ~/.nvm des jeweiligen Benutzers |
| Node.js LTS | v24.22.0 (Krypton), npm 11.19.1 |
| Zweite Version | v22.23.3 (Jod), npm 10.9.9 |
| Lizenz | MIT |
| Port der Beispiel-App | 3000, nur an 127.0.0.1 |
Schritt 1: nvm auf Ubuntu Server installieren, Version pinnen
Das offizielle Installationsskript liegt im Repository nvm-sh/nvm. Verwenden Sie in der URL immer einen Versions-Tag statt master, dann installieren Sie genau den Stand, den Sie geprüft haben. Die aktuelle Version zeigt die Weiterleitung von /releases/latest:
curl -sI https://github.com/nvm-sh/nvm/releases/latest | grep -i location
# location: https://github.com/nvm-sh/nvm/releases/tag/v0.40.8
Melden Sie sich als der Benutzer an, der Node.js später nutzt, nicht als root. Wer ein Skript nicht ungelesen an bash weiterreichen will, lädt es zuerst herunter, liest es und führt es dann aus:
curl -fsSL -o install-nvm.sh https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh
less install-nvm.sh
bash install-nvm.sh
Die Kurzform aus der README lautet curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.8/install.sh | bash. Im Test meldete das Skript:
=> Downloading nvm as script to '/home/deploy/.nvm'
=> Appending nvm source string to /home/deploy/.bashrc
=> Appending bash_completion source string to /home/deploy/.bashrc
Ist git installiert, klont das Skript das Repository, sonst lädt es die Dateien einzeln herunter. Am Ende von ~/.bashrc stehen danach drei Zeilen mit NVM_DIR, die nvm bei jeder interaktiven Shell laden. Öffnen Sie eine neue SSH-Sitzung oder laden Sie die Datei neu:
source ~/.bashrc
nvm --version
Verifizieren: nvm --version gibt 0.40.8 aus, command -v nvm liefert nur das Wort nvm und keinen Pfad. Genau das ist der Kern aller späteren Fallstricke: nvm ist eine Funktion der Shell, keine Datei in /usr/bin.
Schritt 2: Node.js LTS installieren und Standard setzen
Die verfügbaren LTS-Versionen listet nvm ls-remote --lts. Im Test endete die Liste mit v24.22.0 (Latest LTS: Krypton). Installieren Sie die aktuelle LTS:
nvm install --lts
Downloading and installing node v24.22.0...
Computing checksum with sha256sum
Checksums matched!
Now using node v24.22.0 (npm v11.19.1)
Creating default alias: default -> lts/* (-> v24.22.0 *)
nvm prüft die SHA-256-Prüfsumme des Downloads und legt beim ersten Installieren automatisch den Alias default an. Der Alias lts/* zeigt aber immer auf die neueste LTS-Linie. Auf einem Server wollen Sie die Hauptversion bewusst festlegen:
nvm alias default 24
Die Ausgabe lautet default -> 24 (-> v24.22.0 *). Der Alias verweist auf die Hauptversion 24 und nicht auf einen festen Patchstand; nvm löst ihn auf die installierte 24er-Version auf.
Verifizieren: node -v zeigt v24.22.0, which node zeigt /home/deploy/.nvm/versions/node/v24.22.0/bin/node, und nvm current gibt dieselbe Version aus.
Schritt 3: Zweite Version, .nvmrc und Versionswechsel
Ein älteres Projekt braucht Node 22? Installieren Sie die Version zusätzlich:
nvm install 22
nvm schaltet die laufende Shell dabei sofort um (Now using node v22.23.3 (npm v10.9.9)). In einer neuen Shell gilt wieder die Standardversion, nvm ls zeigt dann:
v22.23.3 *
-> v24.22.0 *
default -> 24 (-> v24.22.0 *)
...
lts/jod -> v22.23.3 *
lts/krypton -> v24.22.0 *
Der Pfeil markiert die Version der aktuellen Shell. Für das Projekt legen Sie im Projektordner eine Datei .nvmrc mit der gewünschten Version an. nvm use ohne Argument liest sie:
cd ~/app
echo 22 > .nvmrc
nvm use
Found '/home/deploy/app/.nvmrc' with version <22>
Now using node v22.23.3 (npm v10.9.9)
Wichtig für die Praxis: Der Wechsel gilt nur für die laufende Shell. Eine neue SSH-Sitzung landet auch im Projektordner wieder bei der Standardversion v24.22.0, bis Sie erneut nvm use aufrufen. Wer das automatisieren möchte, findet in der README unter „Deeper Shell Integration“ Snippets für ~/.bashrc, die laut README von Nutzern beigesteuert und nicht vom nvm-Team unterstützt werden. In einem Ordner ohne .nvmrc bricht nvm use mit No version provided and no .nvmrc file found ab.
Einen einzelnen Befehl mit einer anderen Version starten Sie, ohne die Shell umzustellen:
nvm exec 22 node -v
# Running node v22.23.3 (npm v10.9.9)
# v22.23.3
Global installierte npm-Pakete gehören immer zu einer Node-Version. Im Test war ein mit npm install -g cowsay unter v24 installiertes Paket unter v22 nicht vorhanden. Beim Installieren einer neuen Version übernimmt nvm install 22 --reinstall-packages-from=24 die globalen Pakete; im Test meldete nvm Reinstalling global packages from v24.22.0... und cowsay@1.6.0 stand danach auch unter v22 bereit.
Verifizieren: Im Projektordner zeigt nvm use && node -v die Version aus der .nvmrc, außerhalb zeigt node -v die Standardversion.
Schritt 4: Fallstrick sudo und root
nvm installiert alles unter ~/.nvm des Benutzers und ergänzt den PATH nur in dessen Shell. sudo setzt den PATH auf den Wert aus secure_path zurück, root hat kein nvm geladen. Im Test lieferten beide Varianten einen Fehler:
$ sudo node -v
sudo: node: command not found
$ sudo -i node -v
-bash: line 1: node: command not found
Auch sudo nvm scheitert mit sudo: nvm: command not found, weil sudo keine Shell-Funktionen kennt. Brauchen Sie Node.js einmalig mit Root-Rechten, etwa für ein Skript, das an einen Port unter 1024 bindet, übergeben Sie den absoluten Pfad:
sudo "$(nvm which default)" -v
# v24.22.0
sudo env "PATH=$PATH" node -v funktionierte im Test ebenfalls, reicht aber den kompletten Benutzer-PATH an root weiter. Der absolute Pfad ist enger gefasst. Für npm gilt ohnehin: Mit nvm installieren Sie globale Pakete ohne sudo, laut README ist sudo npm install -g unnötig.
Verifizieren: Als root (etwa nach sudo -i) gibt bash -lc 'command -v node || echo "root: kein node"' die Meldung root: kein node aus. Root nutzt also bewusst kein nvm-Node, solange Sie keinen Pfad vorgeben.
Schritt 5: Node-App als systemd-Dienst mit nvm
systemd startet Dienste ohne Login-Shell, ~/.bashrc wird nie gelesen. Zum Test dient eine kleine App in /home/deploy/app/server.js:
const http = require('http');
const port = process.env.PORT || 3000;
http.createServer((req, res) => {
res.end(`Hallo von Node ${process.version}\n`);
}).listen(port, '127.0.0.1', () => console.log(`App läuft auf Port ${port} mit Node ${process.version}`));
Eine Unit mit ExecStart=node server.js scheitert. Im Test zeigte systemctl status nodeapp:
× nodeapp.service - Node.js Beispiel-App
Active: failed (Result: exit-code) since Sun 2026-10-18 06:05:10 UTC; 802ms ago
Duration: 639us
Process: 10946 ExecStart=node server.js (code=exited, status=203/EXEC)
...
nodeapp.service: Start request repeated too quickly.
Der Status 203/EXEC bedeutet: systemd hat die Programmdatei nicht gefunden. Wegen Restart=on-failure versuchte systemd es fünfmal und griff dann das Startlimit. Die funktionierende Unit trägt den absoluten Pfad aus nvm which 24 ein. Legen Sie /etc/systemd/system/nodeapp.service an:
[Unit]
Description=Node.js Beispiel-App
After=network.target
[Service]
User=deploy
WorkingDirectory=/home/deploy/app
Environment=PORT=3000 NODE_ENV=production
ExecStart=/home/deploy/.nvm/versions/node/v24.22.0/bin/node server.js
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl reset-failed nodeapp
sudo systemctl enable --now nodeapp
reset-failed ist nur nötig, wenn der Dienst vorher am Startlimit hing. Im Test scheiterte enable --now nach der Korrektur zunächst mit Job for nodeapp.service failed because the control process exited with error code., erst nach reset-failed lief der Dienst. Der Pfad enthält die Patchversion. Nach einem Node-Update passen Sie ihn an, sonst startet der Dienst weiter mit der alten Version oder gar nicht, wenn Sie diese entfernt haben.
Die Alternative ohne festen Pfad ist das mitgelieferte Hilfsskript ~/.nvm/nvm-exec. Es lädt nvm, wählt die Version aus der Variable NODE_VERSION oder aus der .nvmrc im Arbeitsverzeichnis und startet dann den Befehl:
Environment=PORT=3000 NODE_ENV=production NODE_VERSION=default
ExecStart=/home/deploy/.nvm/nvm-exec node server.js
Im Test startete die Unit ohne NODE_VERSION mit der .nvmrc des Projekts (Log: Found '/home/deploy/app/.nvmrc' with version <22>, App auf v22.23.3), mit NODE_VERSION=default mit v24.22.0. Der Vorteil: Ein nvm alias default oder eine geänderte .nvmrc wirkt beim nächsten Neustart, ohne die Unit zu ändern. Der Nachteil: Der Start lädt zuerst nvm, im Test zeigte systemctl status 219 ms CPU-Zeit statt 32 ms mit festem Pfad. Grundlagen zu Unit-Dateien vertieft die Anleitung systemd-Service erstellen und verwalten.
Verifizieren: systemctl status nodeapp zeigt active (running) und als Prozess /home/deploy/.nvm/versions/node/v24.22.0/bin/node server.js, das Journal enthält App läuft auf Port 3000 mit Node v24.22.0, und curl -s http://127.0.0.1:3000 antwortet mit Hallo von Node v24.22.0. Fehlermeldungen lesen Sie mit journalctl -u nodeapp, siehe Logs lesen mit journalctl.
Schritt 6: Node-Skripte per Cron starten
Cron führt Jobs mit /bin/sh und einem minimalen PATH aus. Im Test lief dasselbe Skript job.js mit drei Varianten in der Crontab von deploy (crontab -e):
* * * * * cd /home/deploy/app && node job.js >> /home/deploy/cron-falsch.log 2>&1
* * * * * cd /home/deploy/app && /home/deploy/.nvm/versions/node/v24.22.0/bin/node job.js >> /home/deploy/cron-pfad.log 2>&1
* * * * * cd /home/deploy/app && NODE_VERSION=22 /home/deploy/.nvm/nvm-exec node job.js >> /home/deploy/cron-nvmexec.log 2>&1
Nach zwei Minuten stand in den Logdateien:
== /home/deploy/cron-falsch.log
/bin/sh: 1: node: not found
/bin/sh: 1: node: not found
== /home/deploy/cron-nvmexec.log
2026-10-18T06:06:01.405Z Job lief mit Node v22.23.3
2026-10-18T06:07:01.570Z Job lief mit Node v22.23.3
== /home/deploy/cron-pfad.log
2026-10-18T06:06:01.274Z Job lief mit Node v24.22.0
2026-10-18T06:07:01.433Z Job lief mit Node v24.22.0
Die erste Datei zeigt den typischen Fehler. Leiten Sie Ausgaben mit >> datei 2>&1 immer in eine Datei oder ins Journal um, sonst bemerken Sie einen solchen Fehlstart leicht nicht. Mehr zur Syntax erklärt Cron und Crontab: Grundlagen unter Linux.
Verifizieren: Nach dem nächsten vollen Minutenwechsel enthält die Logdatei des Jobs eine Zeile mit der erwarteten Node-Version und keine Meldung node: not found.
Schritt 7: Versionen und nvm entfernen
Eine nicht mehr benötigte Node-Version entfernen Sie mit nvm uninstall. Die gerade aktive Version lässt sich nicht löschen:
$ nvm uninstall 24
nvm: Cannot uninstall currently-active node version, v24.22.0 (inferred from 24).
Wechseln Sie vorher mit nvm use auf eine andere Version oder geben Sie mit nvm deactivate den PATH frei. Prüfen Sie außerdem, ob ein systemd-Dienst oder Cronjob den Pfad dieser Version nutzt: grep -r "versions/node" /etc/systemd/system/; crontab -l.
nvm komplett entfernen Sie, indem Sie den Ordner löschen und die drei Zeilen aus ~/.bashrc streichen. Stoppen Sie vorher Dienste, die Node aus ~/.nvm starten. Die README empfiehlt, zuerst mit nvm unload die Funktion aus der laufenden Shell zu entfernen:
nvm unload
rm -rf "$HOME/.nvm"
sed -i '/NVM_DIR/d' ~/.bashrc
Alle drei von nvm angelegten Zeilen enthalten NVM_DIR, der sed-Befehl entfernt genau diese. Sichern Sie ~/.bashrc vorher, falls Sie die Variable selbst an anderer Stelle nutzen.
Verifizieren: In einer neuen Shell liefert command -v nvm node || echo "nvm und node entfernt" die Meldung nvm und node entfernt, und ls -d ~/.nvm meldet No such file or directory.
Typische Fehler
| Meldung | Ursache | Lösung |
|---|---|---|
nvm: command not found in Skripten, bei su - deploy -c '...' oder per SSH-Befehl | Ubuntus ~/.bashrc bricht bei nicht interaktiven Shells sofort ab, die nvm-Zeilen stehen am Ende | Im Skript export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" laden oder nvm-exec nutzen |
sudo: node: command not found | sudo nutzt secure_path, nvm-Pfade fehlen | sudo "$(nvm which default)" ... |
status=203/EXEC in systemd | ExecStart ohne absoluten Node-Pfad | Pfad aus nvm which oder nvm-exec eintragen |
Start request repeated too quickly | Startlimit nach mehreren Fehlstarts | Unit korrigieren, systemctl reset-failed nodeapp |
/bin/sh: 1: node: not found im Cron-Log | Minimaler PATH in Cron | Absoluter Pfad oder NODE_VERSION=… ~/.nvm/nvm-exec |
No version provided and no .nvmrc file found | nvm use ohne Argument außerhalb eines Projektordners | Version angeben oder .nvmrc anlegen |
| Globales npm-Paket plötzlich weg | Pakete gehören zur jeweiligen Node-Version | nvm install X --reinstall-packages-from=Y |
Die Ursache der ersten Zeile lässt sich direkt nachlesen: grep -n -A3 "If not running interactively" ~/.bashrc zeigt den case $- in-Block, der nicht interaktive Shells vor den nvm-Zeilen beendet.
Häufige Fragen
Ist nvm für Produktivserver geeignet?
Für Server, auf denen ein Benutzer mehrere Projekte mit unterschiedlichen Node-Versionen betreibt, ja. nvm ist laut Projekt dafür gebaut, pro Benutzer installiert und pro Shell aufgerufen zu werden. Dienste und Cronjobs müssen Sie dann wie in Schritt 5 und 6 gezielt verdrahten. Läuft nur eine einzige Node-Version für alle Benutzer, ist ein systemweit installiertes Paket einfacher zu verwalten.
Kann ich nvm für alle Benutzer und root gemeinsam installieren?
Das Projekt sieht eine Installation pro Benutzer vor. Im Test fand root weder node noch nvm, obwohl der Benutzer deploy beides eingerichtet hatte. Jeder Benutzer, der Node braucht, installiert nvm selbst, oder Dienste greifen über den absoluten Pfad auf die Installation eines Dienstbenutzers zu.
Wie aktualisiere ich nvm selbst?
Laut README dient dasselbe Installationsskript zum Installieren und zum Aktualisieren. Sie führen es mit der neuen Versionsnummer in der URL erneut aus und prüfen danach nvm --version.
Wie aktualisiere ich Node.js auf den neuesten Patchstand?
Die README beschreibt dafür nvm install --reinstall-packages-from=<alte Version> <neue Version>: nvm installiert die neue Version und übernimmt die globalen Pakete der alten, im Test mit nvm install 22 --reinstall-packages-from=24. Systemd-Units und Cronjobs mit absolutem Pfad müssen Sie anpassen, Varianten mit nvm-exec nicht.
Warum funktioniert nvm per SSH-Befehl nicht?
ssh server 'node -v' startet eine nicht interaktive Shell. Ubuntus ~/.bashrc endet dann vor den nvm-Zeilen; im Test lieferte su - deploy -c 'nvm --version' aus demselben Grund -bash: line 1: nvm: command not found. Rufen Sie bash -ic auf oder laden Sie nvm.sh im Befehl selbst.
Fazit
nvm macht mehrere Node-Versionen auf einem Ubuntu-Server unkompliziert, solange Sie sich merken, dass es nur in interaktiven Shells existiert. Mit gepinnter Installationsversion, festem default-Alias und .nvmrc je Projekt bleibt die Umgebung nachvollziehbar. Für systemd und Cron entscheiden Sie zwischen dem absoluten Pfad (einfach, muss nach Updates angepasst werden) und nvm-exec (folgt Alias und .nvmrc). Wer die Node-App öffentlich erreichbar macht, setzt einen Reverse Proxy davor und lässt die App wie im Beispiel nur an 127.0.0.1 lauschen.


