Node.js
Der Node.js-Container (envoryx-<projekt>-node, Image ghcr.io/envoryx/envoryx-node:<version>) bringt
npm, pnpm und yarn mit und kann zwei ziemlich unterschiedliche Rollen spielen.
Neben PHP, Python, Go, Ruby, Java oder .NET ist er dein Werkzeugkasten: Er baut mit npm run build die Assets oder
betreibt einen Vite-Dev-Server fürs Frontend, während das Backend sein eigenes Ding macht. In einem
Node.js-Projekt - mit einer Node.js-Vorlage oder Node.js-Anwendung für ein leeres Projekt - ist er die
Anwendung, und PHP brauchst du gar nicht.
Versionen
Abschnitt betitelt „Versionen“Standard ist 24, weil das die aktuelle LTS ist. 22 ist ebenfalls LTS, und 26 gibt es, wenn du die neuesten Funktionen willst. 20 und 18 stehen noch für Projekte bereit, die nicht umziehen können, haben ihr Lebensende aber hinter sich.
Wechselst du die Version, kommen die Container mit dem neuen Node.js zurück, node_modules bleibt aber,
wie es ist. Reinen JavaScript-Paketen ist das egal. Native Module (bcrypt, better-sqlite3 und Co.) wurden
für die alte Version gebaut, deshalb führst du danach im Terminal npm rebuild aus (oder installierst
neu).
Was im Container steckt
Abschnitt betitelt „Was im Container steckt“Der Container hat kein eigenes NODE_ENV. Ein npm run build aus dem Bereich Aktionen oder dem
Terminal baut deshalb so, wie die Werkzeuge es von sich aus tun, bei Next.js und Vite also ein echtes
Produktions-Bundle, und npm install installiert trotzdem deine devDependencies. Nur der Dev-Server
setzt für sich selbst NODE_ENV=development (außer du setzt im Bereich Umgebung ein eigenes), und ein
Produktions-Build läuft mit NODE_ENV=production. Worker und Cron-Jobs richten sich nach dem
Dev-Server: Sie bekommen production, solange er einen Produktions-Build ausliefert, sonst
development, und in der Entwicklung gewinnt auch bei ihnen ein NODE_ENV, das du selbst setzt. Im
Produktionsmodus ist es immer production.
pnpm und yarn kommen über Corepack, und yarn ist das aktuelle Yarn (4.x), nicht Yarn 1. Nutzt dein
Projekt noch Yarn 1, legst du das in der package.json mit "packageManager": "yarn@1.22.22" fest, und
Corepack holt diese Version für das Projekt.
Eine Compiler-Toolchain hat das Image nicht (kein Python, kein make, kein g++). Native Module
funktionieren, solange sie ein vorgebautes Binary für deine Node.js-Version und Linux mitbringen, was die
verbreiteten tun. Muss ein Paket mit node-gyp aus dem Quellcode bauen, scheitert es mit etwas wie
gyp ERR! find Python. Dann hilft meist eine Version des Pakets, die für deine Node.js-Version ein
Prebuild hat, oder du bleibst bei der Node.js-Version, für die es gemacht ist. Muss das Paket wirklich
kompilieren, erledigt das ein eigenes Image mit build-essential
und Python auf Basis des Envoryx-Images.
Einen Dev-Server betreiben
Abschnitt betitelt „Einen Dev-Server betreiben“Schalte beim Node.js-Dienst Dev-Server ausführen ein, im Assistenten oder im Bereich Laufzeit. Der
Container führt dann ein Skript aus deiner package.json (dev, wenn du nichts anderes wählst) als
Hauptprozess aus und startet es neu, falls es abstürzt.


Jedes Framework will Host und Port anders mitgeteilt bekommen, deshalb gibt es Vorlagen:
| Vorlage | Was Envoryx übergibt | Standard-Port |
|---|---|---|
| Vite | --host 0.0.0.0 --port |
5173 |
| Next.js | -H -p |
3000 |
| Nuxt | --host --port |
3000 |
| Sonstige | setzt nur HOST und PORT |
- |
Bei npm stehen die Flags hinter einem -- (npm run dev -- --host 0.0.0.0 …), das npm selbst
schluckt. pnpm und Yarn würden dieses -- ans Skript weiterreichen, und Vite ignorierte dann alles
dahinter und lauschte nur auf localhost. Bei diesen beiden folgen die Flags deshalb direkt auf den
Skriptnamen.
Wie Laravels Vite-Plugin hinter dem Dev-Host funktioniert, steht auf der
PHP-Seite: Die Adresse des Dev-Servers bekommt jeder Container des
Projekts als ENVORYX_DEV_URL.
Wo er antwortet
Abschnitt betitelt „Wo er antwortet“In einem Node.js-Projekt ohne PHP schickt der Proxy https://<projekt>.<basis>, jede zusätzliche Domain
und https://<projekt>-dev.<basis> zum Dev-Server - Hot Module Reloading über WebSockets inklusive.
Außerdem bekommt der Container einen eigenen Host-Port (Bereich Domains → Direktzugriff), der auch ohne
eingerichtetes DNS funktioniert.
Neben PHP ist es umgekehrt: PHP bleibt die Anwendung, und der Dev-Server behält
https://<projekt>-dev.<basis>.
Solange der Dev-Server das Projekt ausliefert, ist der Port des Webserver-Containers bewusst nicht
veröffentlicht. Sonst wäre der Document Root - meist das ganze Projekt samt .env und Quellcode - im
Netz lesbar. Schaltest du den Dev-Server aus, kommt der Port mit derselben Nummer zurück.
Preset, Skript und Port des Dev-Servers bleiben gespeichert, solange er aus ist, und auch seinen
Host-Port behält er. Schaltest du ihn wieder ein (in der Oberfläche, über API oder CLI), ist er
genau so zurück, wie er war.
Die Host-Prüfung von Vite
Abschnitt betitelt „Die Host-Prüfung von Vite“Vite lehnt Anfragen für Hostnamen ab, die es nicht kennt. Envoryx setzt
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.<basis> und lässt damit <projekt>.<basis>,
<projekt>-dev.<basis> und jede weitere Domain unter deiner Basisdomain durch. Nimmst du eine Domain
außerhalb der Basisdomain dazu, trag sie in server.allowedHosts in deiner vite.config ein. Nuxt
prüft Hosts nicht. Next.js ab 15 schaut dagegen, woher Dev-Anfragen kommen, und Fast Refresh verbindet
sich nie von einem Origin, der in allowedDevOrigins fehlt. Das Next.js-Template schreibt diese Liste
für dich in die next.config.ts: *.<basis>, das das Projekt, seinen Dev-Host und eine spätere
Umbenennung abdeckt, dazu die zusätzlichen Domains, die das Projekt beim Anlegen hat. Hängst du später
eine Domain außerhalb der Basisdomain an, trägst du sie dort selbst nach. Bei einer Next.js-App, die du
mitgebracht hast, ist das typische Zeichen für eine fehlende Liste: Die Seite lädt hinter einer
Projektdomain, aber das Hot Reloading bleibt stumm.
Ein frischer Checkout
Abschnitt betitelt „Ein frischer Checkout“Ein ganz neues Node.js-Projekt hat noch keine package.json, es gibt also nichts zu starten. Statt
dauernd abzustürzen, wartet der Dev-Server und sagt das im Log (envoryx: waiting for package.json …).
Das gilt auch für einen Dev-Server neben PHP.
Gibt es eine package.json mit Abhängigkeiten, aber kein node_modules, etwa direkt nach einem Klon,
installiert der Dev-Server sie, bevor er startet. Das tut er nur aus einem Lockfile und im
Frozen-Modus: npm ci bei package-lock.json, pnpm install --frozen-lockfile bei pnpm-lock.yaml
und yarn install --frozen-lockfile bei yarn.lock. So bekommst du genau das, was das Repository
festschreibt, und Envoryx schreibt nie in eine deiner Dateien. Ohne Lockfile, oder wenn die
Installation scheitert, wartet der Dev-Server stattdessen (envoryx: waiting for node_modules …), und
die Projektübersicht sagt dir das. Dann führst du die Installation im Bereich Aktionen aus, und der
Server startet, sobald sie fertig ist. Steht in der Node.js-Karte pnpm oder Yarn als Paketmanager,
sind deren Aktionen install und build von Anfang an da, auch ohne Lockfile, damit ein frisches
pnpm- oder Yarn-Projekt nicht mit einer package-lock.json endet. Die Aktionen und envoryx project exec laufen mit CI=1, und das allein brächte Yarn 2+ und pnpm dazu, das Lockfile nicht anzufassen;
Envoryx schaltet das für sie ab, damit yarn install eine neue yarn.lock schreiben oder einer
bestehenden eine Abhängigkeit hinzufügen kann. Worker warten ebenfalls auf die Abhängigkeiten, installieren
sie aber nie selbst, damit sich nicht zwei Installationen in die Quere kommen. Templates installieren
ihre Abhängigkeiten schon beim Anlegen.
Produktions- und statische Builds
Abschnitt betitelt „Produktions- und statische Builds“Manchmal willst du sehen, wie sich die App verhält, wenn sie richtig gebaut ist. Stell den Modus im Bereich
Laufzeit auf Produktions-Build: Dann führt jeder Containerstart das Build-Skript (standardmäßig
build) und danach das Serve-Skript (start, bei Vite und Nuxt preview) mit NODE_ENV=production
aus. Beides sind Namen von Skripten aus deiner package.json, keine Befehle. Willst du einen Nuxt-Build
etwa mit node .output/server/index.mjs ausliefern, legst du dafür ein start-Skript an und wählst es. Für
eine Next.js- oder Nuxt-App mit Server-Side-Rendering ist das ein produktionsnaher Lauf hinter
denselben URLs.
Wird deine App zu reinen Dateien gebaut, brauchst du gar keinen Node.js-Server. Schalte den Dev-Server
aus, stell den Document Root auf die Build-Ausgabe (dist bei Vite - das Template macht das schon -
oder out bei einem statischen Next.js-Export) und führ npm run build aus. Bei clientseitigem Routing
schaltest du in der Webserver-Karte zusätzlich SPA-Fallback auf index.html ein, damit ein Neuladen
auf /settings nicht mit einem 404 endet.
Debugging
Abschnitt betitelt „Debugging“Node.js-Inspector-Port veröffentlichen im Bereich Laufzeit (braucht den Dev-Server) legt Port 9229 auf
einen eigenen Host-Port. Der Bereich IDE zeigt Host, Port, Pfadzuordnung und Beispiele für die
package.json.
Envoryx setzt NODE_OPTIONS=--inspect absichtlich nicht am Container: npm ist selbst ein Node-Prozess
und würde sich den Port schnappen, bevor deine App dran ist. Starte den Inspector stattdessen in deinem
eigenen Skript, etwa mit NODE_OPTIONS='--inspect=0.0.0.0:9229' nuxt dev oder node --inspect=0.0.0.0:9229 server.js, und verbinde dann WebStorm (Attach to Node.js/Chrome) oder VS Code
(request: attach).
Next.js tanzt aus der Reihe: Es öffnet den Inspector seines Server-Prozesses, in dem dein Server-Code
läuft, einen Port über dem in --inspect. Starte es also einen Port unter dem veröffentlichten, beim
Standardport 9229 mit NODE_OPTIONS='--inspect=0.0.0.0:9228' next dev; der Server-Prozess landet
dann auf 9229, und dorthin führt der Host-Port. Das Beispiel im Bereich IDE macht genau das, für
jeden Wert von Inspector-Port im Container.
Templates, Aktionen, Tests und Worker
Abschnitt betitelt „Templates, Aktionen, Tests und Worker“Als Templates gibt es Vite + React (TypeScript), Next.js (App Router, TypeScript) und Nuxt.
Im Bereich Aktionen findest du node -v und die Installations- und Build-Befehle von npm (install, ci,
run build), pnpm und yarn. Die von pnpm und Yarn brauchen ihre Lock-Datei, außer der Paketmanager
des Projekts steht auf genau diesem (siehe oben).
Der Bereich Tests erkennt die test-Skripte deiner package.json, Playwright und Cypress; siehe
Aktionen und Tests. Und für alles, was dauerhaft laufen soll, gibt
es die Worker npm-Script (npm run <name>) und Node.js-Script (node <datei>), beschrieben unter
Worker und Cron-Jobs.