Node-RED als OpenBSD-Dienst: Eine kleine Lektion in rc.subr-Interna

29.09.2026 3 Min. Lesezeit

Wer OpenBSD länger nutzt, kennt das Muster: Einen neuen Dienst installiert, ein minimalistisches rc.d-Skript geschrieben, rcctl start funktioniert scheinbar, aber rcctl check behauptet hartnäckig:

nodered(failed)

Genau das ist mir bei einer Node-RED-Installation auf OpenBSD 7.9 passiert. Der Weg zur Lösung war lehrreicher als erwartet und liefert einen interessanten Einblick in die Interna von rc.subr.

Ausgangslage

Das erste rc.d-Skript sah so aus:

#!/bin/ksh

daemon="/usr/local/bin/node-red"
daemon_user="_nodered"

daemon_flags="-u /home/_nodered/.node-red"

. /etc/rc.d/rc.subr

rc_bg=YES

rc_cmd $1

Der Start funktionierte:

# rcctl start nodered
nodered(ok)

Der Statuscheck jedoch nicht:

# rcctl check nodered
nodered(failed)

Erste Vermutung: Falscher Prozessname

Ein Blick auf die Prozessliste zeigte:

$ ps -ax -o pid,user,command | grep node
63240 _nodered node: node-red (node)

Interessant war, dass der laufende Prozess eben nicht als /usr/local/bin/node-red erschien, sondern als node: node-red.

Daher lag die Vermutung nahe, dass procname oder pexp angepasst werden müssten. Versuche wie:

procname="/usr/local/bin/node"

oder

pexp="node-red"

brachten jedoch keine Änderung.

Debugging mit rcctl -d

Der eigentliche Durchbruch kam durch:

# rcctl -d start nodered

Die Ausgabe endete mit:

doing _rc_wait_for_start
doing rc_check
...
Alarm clock
doing _rc_write_runfile
(timeout)

Das war die entscheidende Erkenntnis: Node-RED startete erfolgreich. Der Timeout entstand nicht beim Start selbst, sondern weil OpenBSD während der Wartephase nie einen erfolgreichen rc_check() erhielt.

Blick in die Quelle

Also wurde direkt in /etc/rc.d/rc.subr nachgesehen:

grep -n "rc_check()" /etc/rc.d/rc.subr

Das Ergebnis:

rc_check() {
        pgrep -T "${daemon_rtable}" -q -xf "${pexp}"
}

Hier steckt die Lösung. OpenBSD verwendet intern pgrep -xf, also:

  • -f → volle Kommandozeile
  • -x → exakte Übereinstimmung

Das bedeutet: pexp="node-red" funktioniert nur dann, wenn die gesamte Kommandozeile exakt node-red lautet.

Wie sieht Node-RED wirklich aus?

Ein paar Tests:

$ pgrep -fl node
902 node: node-red

$ ps -ww -p 902 -o command=
node: node-red (node)

Und schließlich:

$ pgrep -xf "node-red"

Kein Treffer. Aber:

$ pgrep -xf "node: node-red"
902

Treffer. Auch:

$ pgrep -xf "node.*node-red"
902

funktionierte. Damit war bewiesen, welche Zeichenkette rc.subr tatsächlich sehen musste.

Warum passiert das?

Node-RED wird zwar über /usr/local/bin/node-red gestartet. Intern setzt Node.js jedoch den sichtbaren Prozessnamen um zu node: node-red. Dadurch passt die tatsächliche Prozessbezeichnung nicht mehr zu dem Muster, das rc.subr ursprünglich aus

daemon="/usr/local/bin/node-red"
daemon_flags="-u ..."

erzeugt. Das führte zu folgender Kette:

  1. Node-RED startete.
  2. Das rc-Framework suchte nach dem Prozess.
  3. Die Suche scheiterte.
  4. Nach 30 Sekunden wurde der Timeout ausgelöst.
  5. rcctl check schlug dauerhaft fehl.

Die Lösung

Das fertige Skript:

#!/bin/ksh

daemon="/usr/local/bin/node-red"
daemon_user="_nodered"

daemon_flags="-u /home/_nodered/.node-red"

. /etc/rc.d/rc.subr

pexp="node: node-red"

rc_bg=YES

rc_cmd $1

Danach:

# rcctl start nodered
nodered(ok)

# rcctl check nodered
nodered(ok)

# rcctl restart nodered
nodered(ok)

Erkenntnisse

1. rcctl start bedeutet nicht automatisch, dass der Dienst erkannt wurde

Ein Dienst kann vollständig laufen und trotzdem später bei check oder restart scheitern.

2. rcctl -d ist Gold wert

Der Schalter

rcctl -d start <dienst>

zeigt sehr präzise, an welcher Stelle des Startvorgangs das Framework scheitert.

3. rc_check() verwendet pgrep -xf

Viele Administratoren gehen davon aus, dass ein pexp lediglich ein Suchbegriff sei. Tatsächlich muss das Muster bei OpenBSD häufig zur vollständigen Kommandozeile bzw. Prozessbezeichnung passen.

4. Ein Blick in rc.subr spart viel Rätselraten

OpenBSDs rc-Framework ist erfreulich transparent. Ein kurzes

grep -n rc_check /etc/rc.d/rc.subr

hat letztlich mehr zur Problemlösung beigetragen als jede Dokumentation.

Fazit

Das Problem lag weder an Node-RED noch an Node.js. Die eigentliche Ursache war die Kombination aus:

  • rc_bg=YES
  • der von Node.js veränderten Prozessbezeichnung
  • und dem sehr strikt arbeitenden pgrep -xf in rc.subr.

Sobald das Prozessmuster mit pexp="node: node-red" an die tatsächliche Realität angepasst wurde, funktionierte die Integration in OpenBSDs Service-Framework vollständig.


Eine schöne Erinnerung daran, dass OpenBSD-Probleme oft am schnellsten gelöst werden, wenn man einfach in die Quelle schaut.