Windows-Server-Deployment
Diese Betriebsart installiert NodePilot als Windows-Dienst für den produktiven Netzwerkbetrieb. Die mitgelieferten Skripte befinden sich unter deploy\. Die vollständige Parameterreferenz steht zusätzlich in deploy\README.md.
Zielzustand
Browser / API-Client
|
| HTTPS
v
Kestrel im Windows-Dienst "NodePilot"
|
+--> SQL Server 2022
| oder
+--> PostgreSQL 16+
|
+--> Windows-Zielsysteme über WinRM
Single-Node-Installationen verwenden Kestrel direkt. Active/Passive-Installationen verwenden die mitgelieferte HAProxy-Vorlage.
Unterstützte Varianten
Dienstidentität
| Variante | Einsatz |
|---|---|
| LocalSystem | Einfacher Einzelserver; Netzwerkzugriffe erfolgen als Computerkonto DOMAIN\HOST$ |
| gMSA | Least-Privilege, gemeinsame Identität und empfohlener HA-Pfad |
Datenbank
| Provider | Authentifizierung | Produktions-TLS |
|---|---|---|
| SQL Server 2022 ab CU1 (Build ≥ 16.0.4003.1) | Windows Integrated Security | Encrypt=Strict;TrustServerCertificate=False |
| PostgreSQL 16+ | Benutzername und Passwort | SSL Mode=VerifyFull mit Root-CA |
Encrypt=Strict ist TDS 8.0: SQL Server 2019 und älter beherrschen es nicht, und SQL Server
2022 RTM enthält einen TDS-8.0-Fehler, der parametrisierte Abfragen mit Error 8005
abbrechen lässt — behoben ab CU1. Der Installer prüft den Patchstand im Preflight; manuell:
SELECT SERVERPROPERTY('ProductVersion').
Voraussetzungen
Zielserver
- Windows Server 2022 oder 2025
- Domain-Mitgliedschaft
- PowerShell 5.1 oder PowerShell 7
- .NET Runtime und ASP.NET Core Runtime 10.0.11 oder neuer, beide x64. Der Setup-Assistent bringt beide mit und installiert sie bei Bedarf. Für die Installation per Skript müssen beide vorab installiert sein, und zwar die eigenständigen Runtimes, nicht das Hosting Bundle
- Netzwerkzugriff zur Datenbank
- TLS-Zertifikat mit privatem Schlüssel in
LocalMachine\My - Lokale Administratorrechte für die Installation
Netzwerkpfad zu den Clients
Die Live-Ansicht der Oberfläche (laufende Schritte, Ausführungsstatus, Dashboard-Zähler) hält eine
SignalR-Verbindung auf /hubs/execution. Der Client handelt den Transport aus und arbeitet dabei
eine Leiter ab: WebSockets → Server-Sent Events → Long-Polling.
Ein Proxy oder eine TLS-Inspektion, die den Upgrade: websocket-Handshake verwirft, legt die
Live-Ansicht deshalb nicht lahm — sie fällt selbsttätig auf Server-Sent Events zurück. Sichtbar
wird es nur als wiederkehrender Verbindungsfehler in der Browser-Konsole, dazu etwas mehr Last pro
Verbindung. Wer die Konsole sauber halten will, lässt den Host in Proxy bzw. PAC-Datei umgehen oder
WebSocket-Durchlass für ihn freischalten.
Der Vollständigkeit halber: Kestrel annonciert HTTP/2 per ALPN. Eine TLS-terminierende Appliance muss WebSockets damit als Extended CONNECT (RFC 8441) weiterreichen — das beherrschen längst nicht alle, während gewöhnliche Requests unauffällig durchgehen.
Build-Host
- .NET 10 SDK
- Node.js LTS und npm
- Code-Signing-Zertifikat für Artefaktmanifest und Verteilung
Datenbank
Die Datenbank muss vor der Installation existieren. Die NodePilot-Identität benötigt DDL-Rechte, damit EF-Migrationen beim Start angewendet werden können.
1. Dienstidentität vorbereiten
Variante A: LocalSystem
Keine Dienstkontoanlage ist erforderlich. Für SQL Server muss das Computerkonto des NodePilot-Hosts als Login vorhanden sein:
USE master;
CREATE LOGIN [CONTOSO\NPSRV01$] FROM WINDOWS;
CREATE DATABASE NodePilot;
USE NodePilot;
CREATE USER [CONTOSO\NPSRV01$] FOR LOGIN [CONTOSO\NPSRV01$];
ALTER ROLE db_owner ADD MEMBER [CONTOSO\NPSRV01$];
Läuft SQL Server auf dem NodePilot-Host selbst, sieht er den Dienst als NT AUTHORITY\SYSTEM statt als Computerkonto. Dann bekommt dieser vorhandene Login den Datenbankbenutzer und db_owner anstelle von CONTOSO\NPSRV01$. Das Setup erledigt das selbst.
Variante B: gMSA
Das gMSA wird in Active Directory angelegt, für den Zielserver freigegeben und anschließend auf dem Zielserver installiert:
Install-ADServiceAccount -Identity svc-nodepilot
Test-ADServiceAccount -Identity svc-nodepilot
Das erwartete Testergebnis ist True. Für SQL Server wird das gMSA analog zum Computerkonto als Login und Datenbankbenutzer mit db_owner angelegt.
Beides kann das Setup übernehmen
Wer mit NodePilot-Server-Setup-<version>.exe installiert, kann das SQL oben überspringen, sofern das ausführende Konto sysadmin ist (oder CREATE ANY DATABASE besitzt). Die Readiness-Seite prüft den Datenbank-Zugriff der Dienst-Identität getrennt von der reinen Erreichbarkeit — erreichbar wird als installierender Admin getestet, angemeldet wird zur Laufzeit der Dienst — und legt Login, Benutzer und db_owner bei Bedarf an. Die Zeile kommt vorangehakt; unbeaufsichtigt fordert "provisioning": { "createDatabaseAndLogin": true } in der Answer-File dasselbe an. Existenzgeprüft: ist alles vorhanden, passiert nichts. Fehlen die Rechte, wird nichts verändert und die Anweisungen für den DBA werden angezeigt.
PostgreSQL ebenso, mit einem Unterschied. Das Setup bringt psql mit, meldet sich im Pre-Flight als NodePilot-Rolle an (sslmode=verify-full gegen das angegebene Root-Zertifikat) und legt Rolle und Datenbank auf Wunsch an. Weil PostgreSQL kein Gegenstück zu Trusted_Connection hat, verlangt das Superuser-Zugangsdaten: zwei zusätzliche Felder auf der Credentials-Seite bzw. provisioning.postgresSuperUser / .postgresSuperPassword in der Answer-File. Ohne sie bleibt die Zeile eine Diagnose ohne Knopf. Das Passwort einer bereits vorhandenen Rolle wird dabei nie überschrieben.
Was die Dienstidentität für Zielmaschinen bedeutet
Für ein Ziel ohne hinterlegte Credentials verbindet sich NodePilot direkt als Dienstprozessidentität: als gMSA oder unter LocalSystem als Computerkonto des Hosts. Erteile diesem Konto die erforderlichen Endpunkt- und Ressourcenrechte. Bei LocalSystem in einer Active/Passive-Installation muss jedes Knotenkonto berechtigt sein.
Dieser direkte erste Hop erfordert keine resource-based constrained delegation. NodePilot impersoniert dafür keinen Browserbenutzer. Richte Delegation nur für eine gesondert begründete Topologie mit delegiertem Benutzer oder zweitem Hop ein. Bei hinterlegten Credentials wird stattdessen deren Konto berechtigt.
2. Datenbank vorbereiten
SQL Server
Das TLS-Zertifikat. SQL Server braucht ein Zertifikat auf seinen FQDN, dem der NodePilot-Server vertraut. Erstellen, zuweisen und vertrauen: Datenbank-Zertifikate.
Datenbank und Login für die Dienst-Identität, ausgeführt als sysadmin. Bei LocalSystem
statt der gMSA das Computerkonto einsetzen (CORP\APPHOST$):
CREATE LOGIN [CORP\svc-nodepilot$] FROM WINDOWS;
CREATE DATABASE [NodePilot];
GO
USE [NodePilot];
CREATE USER [CORP\svc-nodepilot$] FOR LOGIN [CORP\svc-nodepilot$];
ALTER ROLE db_owner ADD MEMBER [CORP\svc-nodepilot$];
Zusätzlich sollte Read-Committed-Snapshot-Isolation aktiviert sein:
ALTER DATABASE [NodePilot]
SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
Der Installer versucht diese Einstellung automatisch zu setzen. Ohne ausreichende Rechte gibt der Preflight die erforderliche SQL-Anweisung aus.
PostgreSQL
CREATE ROLE nodepilot WITH LOGIN PASSWORD '<strong-secret>';
CREATE DATABASE nodepilot OWNER nodepilot;
Der PostgreSQL-Server muss ein Zertifikat präsentieren, dessen Hostname und Vertrauenskette geprüft werden können. Die Root-CA wird dem Installer als PEM-Datei übergeben. Ohne interne CA: Datenbank-Zertifikate.
Der Dienst prüft zusätzlich online, ob das Zertifikat gesperrt ist. Der Pre-Flight des Installers prüft dasselbe und bricht mit Aborted: Postgres pre-flight failed - certificate revocation validation failed. ab, wenn das Zertifikat gesperrt ist oder keine CRL erreichbar ist. psql prüft die Sperrung nicht, eine erfolgreiche Anmeldung mit psql sagt hier also nichts aus. Abhilfe: den im Zertifikat eingetragenen CRL-Verteilungspunkt vom NodePilot-Server aus erreichbar machen oder die CRL dort in den Speicher LocalMachine\CA importieren (certutil -addstore CA <datei>.crl).
3. HTTPS-Zertifikat importieren
$certificatePassword = Read-Host -AsSecureString "PFX password"
Import-PfxCertificate `
-FilePath C:\Certs\nodepilot.pfx `
-CertStoreLocation Cert:\LocalMachine\My `
-Password $certificatePassword
Thumbprint ermitteln:
Get-ChildItem Cert:\LocalMachine\My |
Where-Object Subject -Like "*nodepilot*" |
Select-Object Subject, Thumbprint, NotAfter
Der Zertifikatsname muss zum öffentlichen Hostnamen passen.
4. Produktionsartefakt beziehen
Entweder das veröffentlichte Release herunterladen oder selbst bauen — der Installer verlangt in beiden Fällen ein signiertes Artefakt und den Thumbprint des Publishers.
Variante A — Release herunterladen. Am aktuellen Release
hängen das Zip, manifest.json, .p7s, NodePilot-<version>.SHA256SUMS.txt und das öffentliche
Signaturzertifikat.
Prüfsummen vergleichen und den Thumbprint gegen die Release-Notes abgleichen — dieser Abgleich
ist die Vertrauensentscheidung. Der Installer verlangt genau diesen Signierer und prüft
Codesignatur-Zweck, KeyUsage und Gültigkeit; ob die Maschine dem Herausgeber vertraut, spielt keine
Rolle. Ein Import nach Cert:\LocalMachine\Root ist optional und bewirkt nur, dass Windows die
Authenticode-Signatur der Installer selbst validiert.
Wer mit dem GUI-Setup installiert (Kapitel 5, Variante A), braucht aus diesem Kapitel nichts:
NodePilot-Server-Setup-<version>.exehängt am selben Release, trägt das signierte Artefakt in sich und prüft dessen Signatur selbst — eine Datei statt fünf, kein Thumbprint-Abgleich von Hand.
Beim ersten Start: das blaue SmartScreen-Fenster
Ein heruntergeladener Installer löst beim Start „Der Computer wurde durch Windows geschützt" aus, mit Nicht ausführen als einziger sichtbarer Schaltfläche. Das ist erwartet und kein Hinweis auf eine beschädigte Datei.
Dafür müssen zwei Dinge zutreffen, und beim Download treffen beide zu:
- Die Datei trägt einen Mark of the Web. Alles, was ein Browser speichert, bekommt einen
Alternate Data Stream mit der Zone „Internet", und SmartScreen bewertet ausschließlich Dateien
mit dieser Markierung. Ein selbst gebauter Installer aus
out\hat sie nicht und startet wortlos — deshalb erscheint der Dialog beim ersten veröffentlichten Release, obwohl frühere eigene Builds desselben Produkts ihn nie ausgelöst haben. - Der Herausgeber hat keine Reputation. NodePilot wird mit einem selbstsignierten Zertifikat signiert; SmartScreen hat also nichts zu bewerten und meldet eine unbekannte App.
Die Markierung lässt sich sichtbar machen und entfernen:
Get-Content -LiteralPath .\NodePilot-Server-Setup-<version>.exe -Stream Zone.Identifier
# ZoneId=3 (3 = Internet)
Unblock-File -Path .\NodePilot-Server-Setup-<version>.exe
Erst prüfen, dann wegklicken. Der Dialog ist nicht die Vertrauensentscheidung — Prüfsumme und
Thumbprint sind es. Beides vergleichen, danach Weitere Informationen → Trotzdem ausführen.
Get-AuthenticodeSignature meldet dabei UnknownError; das ist bei einem selbstsignierten
Herausgeber der erwartete Befund, nicht ein Fehler.
Dasselbe gilt für den Zip-Weg: Aus einem markierten Archiv entpackte Dateien erben die Markierung,
also das Zip vor dem Entpacken mit Unblock-File freigeben — sonst behandelt Windows die darin
enthaltenen Skripte als heruntergeladenen Inhalt.
Antiviren-Ausschlüsse ändern daran nichts; SmartScreen ist ein getrennter Reputationsdienst und ignoriert sie (siehe Antiviren-Ausschlüsse). Dauerhaft verschwindet der Dialog nur mit einem reputationstragenden Zertifikat.
Variante B — selbst bauen. Im Repository auf dem Build-Host:
$releaseSigner = "0123456789ABCDEF0123456789ABCDEF01234567"
.\deploy\Build-Artifact.ps1 -SigningCertificateThumbprint $releaseSigner
-Version ist optional und fällt auf die Produktversion aus Directory.Build.props zurück.
Ergebnis:
out\NodePilot-<version>.zip
out\NodePilot-<version>.zip.manifest.json
out\NodePilot-<version>.zip.manifest.json.p7s
out\NodePilot-Deploy-Scripts-<version>.zip
out\nodepilot-release-signing.cer
out\NodePilot-<version>.SHA256SUMS.txt
Mit -IncludeServerInstaller entsteht im selben Lauf zusätzlich NodePilot-Server-Setup-<version>.exe,
mit -IncludeDesktopInstaller -PgBinariesPath <pgsql> außerdem NodePilot-Desktop-Setup-<version>.exe
— alles unter derselben Version. -InstallerSigningCertificateThumbprint <tp> signiert beide
Installer als Teil des Laufs, was zwingend vor der Prüfsummenbildung passieren muss.
Installer und Updater prüfen Signatur (Thumbprint-Pin; keine Kettenvalidierung), Codesignatur-Eignung, Gültigkeit und KeyUsage des Signaturzertifikats sowie Dateiname, Länge und SHA-256-Hash vor jeder Änderung.
5. NodePilot installieren
Es gibt zwei Wege zur selben Installation.
Variante A: GUI-Setup
NodePilot-Server-Setup-<version>.exe aus dem Release herunterladen und ausführen. Es bringt das
signierte Artefakt und beide .NET-Runtimes mit. Die Prüfseite zeigt eine scrollbare Statusliste;
vollständige Erklärungen und kopierbare Anleitungen stehen beim ausgewählten Eintrag.
Check again prüft nur. Apply selected fixes richtet ausgewählte Voraussetzungen ein und
prüft danach erneut. Next navigiert erst weiter, wenn keine blockierenden Fehler bestehen
und kein Fix mehr angehakt ist; angehakte Fixes werden entweder angewendet oder abgewählt.
Auf Wunsch installiert Setup die Runtimes, legt Datenbank und Login an, importiert eine PFX oder
bereitet eine vorhandene gMSA lokal vor, einschließlich fehlender AD-PowerShell-Werkzeuge.
Das Konto und seine Passwortabrufberechtigungen müssen bereits im AD existieren. Ein benötigter
Neustart wird angezeigt und nicht automatisch ausgeführt. Bereits eingerichtete Voraussetzungen
bleiben bei einem Abbruch des Wizards erhalten.
Prüfung, Einrichtung und Installation zeigen die aktuelle Aktion, Laufzeit und einen Zugang zum Setup-Protokoll. Beim Kopieren werden echte Byte- und Dateizähler angezeigt. Vorgänge mit unbekannter Dauer erhalten eine Aktivitätsanzeige. Die Netzwerkseite zeigt die spätere URL und bietet geprüfte Portvorschläge an; deren Übernahme muss bestätigt werden.
Dazu gehört die Vertrauensfrage aus Kapitel 4 als optionaler Hinweis: Das
Setup nennt den Thumbprint des Herausgebers CN=NodePilot Release Signing und bietet den Import
nach LocalMachine\Root an, angeboten und nicht vorangehakt. Die Installation braucht ihn nicht;
sie verifiziert die Signatur gegen den einkompilierten Thumbprint. Der Import bewirkt nur, dass
Windows die Authenticode-Signatur der Installer selbst validiert. Blockierend wird die Zeile dagegen
bei allem, was der Installer selbst ablehnt: falscher Thumbprint, abgelaufenes oder noch nicht
gültiges Zertifikat, fehlende Codesignatur-Eignung.
Die eigene HTTPS-Zertifikatseite bietet ein vorhandenes Zertifikat aus Cert:\LocalMachine\My,
einen PFX-Import mit Dateiauswahl und Passwort oder die ausdrückliche Erzeugung eines Laborzertifikats.
Vorhandene Zertifikate sind nach Ablauf sortiert; fehlende private Schlüssel werden gekennzeichnet.
Der PFX-Import erfolgt erst über Apply selected fixes. Das Passwort wird über die geschützte
Setup-Sitzung übergeben, nicht über die Kommandozeile. Es wird kein Stammzertifikat automatisch vertraut.
Wer noch gar keins hat, lässt das Feld leer. Die Prüfseite meldet dann „No certificate
selected" und bietet an, ein selbstsigniertes zu erzeugen — angeboten, nicht vorangehakt, denn ein
Laborzertifikat entsteht auf Ansage. Es gilt zwei Jahre und wird bewusst nicht in den
Root-Store importiert; für Produktion bleibt ein Zertifikat aus der eigenen PKI der Weg. Das Skript
gibt neben dem Thumbprint auch den SHA-256-Fingerprint aus: Clients können das Zertifikat entweder
systemweit in Cert:\LocalMachine\Root importieren oder — nur für np und den MCP-Server — genau
dieses eine Zertifikat pinnen (np auth login --tls-thumbprint <SHA-256>, siehe
CLI).
Unbeaufsichtigt entspricht das einem leeren certificate.thumbprint plus
"provisioning": { "generateSelfSignedCertificate": true }.
Die Abschlussseite zeigt alles, was für den ersten Zugriff nötig ist: Adresse, Setup-Token für die erste Anmeldung, External-Trigger-API-Key, Zertifikats-Thumbprint sowie Dienstname und Pfade. Der API-Key erscheint nur dort — er ist danach nicht mehr rekonstruierbar. Der Text ist markierbar, und „Save this summary…" legt ihn als Datei ab. „Open NodePilot" öffnet die installierte Anwendung.
Schlüsselfertig ohne Token-Eingabe. Zwei Wege, die sich gegenseitig ausschließen:
bootstrap-Gruppe mitadminUsername— das Setup legt den ersten Administrator selbst an. Kennwort pro Maschine zufällig erzeugt und in einer ACL-geschützten Datei unter<DataPath>\bootstrap-admin.jsonhinterlegt (nur SYSTEM und Administratoren). Feste Standard-Zugangsdaten gibt es bewusst nicht: sie wären über alle Maschinen gleich und würden gefunden statt geraten.seed-Gruppe mitbackupPathundpassphrase— eine Referenzmaschine einmal einrichten,np backup export, und jede weitere Installation spielt diesen Stand beim ersten Start ein: Benutzer, Workflows, Maschinen, Credentials und Einstellungen. Dann entsteht gar kein Token. Die Passphrase landet im Dienstschlüssel, nie in der Konfigurationsdatei; die Seed-Datei wird nach dem Einspielen gelöscht.
Der Seed gewinnt: bringt er Benutzer mit, gibt es nichts einzulösen. Er füllt außerdem nur eine leere Instanz — eine Maschine im Betrieb behält alles, was sie hat. Und er ist fail-closed: eine falsche Passphrase lässt den Dienst nicht starten, statt eine scheinbar provisionierte, in Wahrheit leere Instanz zu hinterlassen.
Unbeaufsichtigt für SCCM oder GPO:
NodePilot-Server-Setup-<version>.exe /VERYSILENT /SUPPRESSMSGBOXES /ANSWERFILE=C:\prod\answers.json
Ein erneuter Lauf erkennt eine vorhandene Installation und bietet drei Wege an: per Default ein
Update, alternativ neu aufsetzen — das erzeugt allerdings einen neuen
External-Trigger-API-Key, der alte ist nicht rekonstruierbar — oder entfernen. Die dritte
Option übergibt an denselben Uninstaller, den auch „Apps & Features" startet; sie fragt nichts
doppelt, sondern lässt ihn seine eine Frage stellen: Datenverzeichnis behalten oder löschen. Die
Datenbank bleibt in jedem Fall unberührt — dieses Setup hat sie nicht angelegt und entfernt sie
nicht. /FULLREINSTALL überspringt die Auswahl und setzt direkt neu auf. Schema der Antwortdatei,
Schalter und Exit-Codes stehen in deploy/server/README.md.
Variante B: Skripte
Der Weg, den das Setup intern selbst geht, und der richtige für Automatisierung. Die Installationsbefehle laufen als lokaler Administrator auf dem Zielserver.
SQL Server mit LocalSystem
$releaseSigner = "0123456789ABCDEF0123456789ABCDEF01234567"
.\deploy\Install-NodePilot.ps1 `
-ArtifactPath C:\Packages\NodePilot-2026.07.27.zip `
-TrustedArtifactSignerThumbprint $releaseSigner `
-UseLocalSystem `
-SqlServer "sql01.contoso.local" `
-SqlDatabase "NodePilot" `
-CertThumbprint "A1B2C3D4E5F6..." `
-PublicHostname "nodepilot.contoso.local"
SQL Server mit gMSA
$releaseSigner = "0123456789ABCDEF0123456789ABCDEF01234567"
.\deploy\Install-NodePilot.ps1 `
-ArtifactPath C:\Packages\NodePilot-2026.07.27.zip `
-TrustedArtifactSignerThumbprint $releaseSigner `
-ServiceAccount "CONTOSO\svc-nodepilotquot; `
-SqlServer "sql01.contoso.local" `
-SqlDatabase "NodePilot" `
-CertThumbprint "A1B2C3D4E5F6..." `
-PublicHostname "nodepilot.contoso.local"
PostgreSQL mit gMSA
$releaseSigner = "0123456789ABCDEF0123456789ABCDEF01234567"
$postgresPassword = Read-Host -AsSecureString "PostgreSQL password"
.\deploy\Install-NodePilot.ps1 `
-ArtifactPath C:\Packages\NodePilot-2026.07.27.zip `
-TrustedArtifactSignerThumbprint $releaseSigner `
-ServiceAccount "CONTOSO\svc-nodepilotquot; `
-DbProvider postgres `
-PostgresHost "pg01.contoso.local" `
-PostgresDatabase "nodepilot" `
-PostgresUser "nodepilot" `
-PostgresPassword $postgresPassword `
-PostgresRootCertificate C:\PKI\postgres-root-ca.pem `
-CertThumbprint "A1B2C3D4E5F6..." `
-PublicHostname "nodepilot.contoso.local"
Für PostgreSQL mit LocalSystem ersetzt -UseLocalSystem den Parameter -ServiceAccount.
6. Installationswirkung
Der Installer führt folgende Schritte aus:
- Voraussetzungen, Signatur, Zertifikat, Dienstkonto und Datenbankzugriff prüfen.
- Vorhandenen NodePilot-Dienst kontrolliert stoppen.
- Binaries nach
C:\Program Files\NodePilotinstallieren. - Betriebsdaten unter
C:\ProgramData\NodePilotanlegen. - Produktionskonfiguration rendern.
- Dateisystem- und Zertifikats-ACLs setzen. Das Installationsverzeichnis bekommt dabei eine geschützte ACL: SYSTEM und Administratoren
FullControl, das Dienstkonto nurReadAndExecute— es führt die Binaries aus, es überschreibt sie nie. Ein abweichender-InstallPathwird vorher geprüft (lokal, NTFS oder ReFS, keine Junctions im Pfad) und nach dem Kopieren erneut verifiziert; sonst würde er die Rechte seines übergeordneten Verzeichnisses erben, was auf einem eigenen Volume schreibenden Zugriff für alle Benutzer bedeuten kann. - HTTPS-Firewallregel anlegen.
- Windows-Dienst mit Auto Start und Recovery Actions registrieren (bei einem gMSA zusätzlich abhängig von Netlogon, damit der Logon nicht vor dem DC-Kontakt scheitert). Der Dienst startet also ohne feste Verzögerung und wartet stattdessen selbst auf die Datenbank — Obergrenze
Database:StartupWaitSeconds, Standard 300 Sekunden. - Dienst starten und Readiness prüfen.
- Admin-Setup-Token und External-Trigger-API-Key ausgeben. Das Setup übergibt stattdessen
-SuppressSecretOutputund zeigt beide Werte auf seiner Abschlussseite, damit sein Transkript unter%TEMP%sie nicht enthält.
Der PostgreSQL-Connection-String wird nicht in die JSON-Datei geschrieben. Er liegt im ACL-geschützten Service-Environment.
7. Installation prüfen
Get-Service NodePilot
Invoke-WebRequest https://nodepilot.contoso.local/healthz/live
Invoke-WebRequest https://nodepilot.contoso.local/healthz/ready
Erwartete Ergebnisse:
| Prüfung | Erwartung |
|---|---|
| Dienststatus | Running |
/healthz/live | HTTP 200 |
/healthz/ready | HTTP 200 und erreichbare Datenbank |
| Browserzugriff | Login- oder Setup-Seite ohne Zertifikatswarnung |
Bei aktivierter Verzeichnisanbindung ist /healthz/directory separat zu prüfen. Die allgemeine Readiness bleibt absichtlich auf die Datenbank beschränkt.
Die np-CLI auf dem Server nutzt dieselbe öffentliche URL wie jeder andere Client — das Zertifikat
nennt den öffentlichen Hostnamen, nicht localhost:
np config set server https://nodepilot.contoso.local
Bei einer Installation abseits von 443 kommt :<HttpsPort> dahinter. Auf https://localhost zu
zeigen scheitert mit „The hostname is not among the certificate names", weil dieser Name nicht im
Zertifikat steht.
Schlägt eine der Prüfungen fehl, steht die Ursache im Anwendungslog unter C:\ProgramData\NodePilot\logs\nodepilot-<Datum>.log. Welche Datei bei welchem Störungsbild weiterhilft — inklusive des Setup-Transkripts unter %TEMP%, das ein Abbruch der Installation hinterlässt —, steht unter Logs & Diagnose.
Verhalten bei Datenbank-Ausfall im laufenden Betrieb
Bei einem Laufzeit-Ausfall bleibt der Dienst oben; DB-abhängige HTTP-Aufrufe antworten schnell mit
dem gemeinsamen 503 DATABASE_UNAVAILABLE-Vertrag, SignalR-Aufrufe mit demselben Fehlercode. Die UI
bleibt erreichbar, zeigt Banner und Status-Ampel und verbindet sich nach erfolgreicher
SELECT 1-Recovery automatisch neu — inklusive SignalR-Gruppen, ohne Dienst-Neustart.
RejectedByServer bedeutet dagegen falsche
Zugangsdaten, Datenbankauswahl oder TLS-Konfiguration (retryable: false): Hier muss ein
Administrator die Konfiguration korrigieren. Wurden NodePilot-Verbindungsdaten geändert, ist danach
ein Dienst-Neustart erforderlich; serverseitige Korrekturen erkennt die Sonde automatisch.
| Endpoint | Verhalten im Ausfall |
|---|---|
/healthz/live | HTTP 200; kein Prozess-Restart |
/healthz/ready | schneller HTTP 503; Traffic wegrouten |
/healthz/database | HTTP 200 mit status und reason; Statusbericht statt Gate |
Laufende Workflows warten an der dauerhaften Step-Grenze. Nur von aktiven Trigger-Sources beobachtete
Fires werden gezählt verworfen und nicht nachgeholt. Notifications sind at-least-once; Empfänger
sollten mit eventKey aus JSON beziehungsweise X-NodePilot-Event-Key deduplizieren. Timeout- und
Probe-Defaults stehen unter Datenbank-Provider, das genaue HTTP-Schema
unter API-Endpunkte.
8. Ersten Admin-Account anlegen
Der Installer zeigt den einmaligen Setup-Token aus C:\ProgramData\NodePilot\admin-setup.token an. Im Browser mit Wunsch-Benutzername und Passwort anmelden: Beim ersten Versuch blendet die Login-Seite ein Setup-Token-Feld ein — Token einfügen, erneut anmelden. Danach ist das Admin-Konto angelegt und der Token gelöscht.
Konnte der Installer den Token nicht anzeigen: Die Datei ist per Owner-only-ACL auf das Dienstkonto beschränkt (auch für Administratoren nicht direkt lesbar — Absicht). Ohne ACL-Änderung per Backup-Semantik lesen:
robocopy C:\ProgramData\NodePilot $env:TEMP admin-setup.token /B | Out-Null
Get-Content "$env:TEMP\admin-setup.token"
Remove-Item "$env:TEMP\admin-setup.token" # nach dem ersten Login
Der External-Trigger-API-Key wird nur einmal angezeigt und muss in einem Secret-Management-System gespeichert werden.
Verzeichnis- und Dateiaufteilung
| Pfad | Inhalt | Dienstzugriff |
|---|---|---|
C:\Program Files\NodePilot\ | API, DLLs und wwwroot | Lesen |
C:\Program Files\NodePilot\appsettings.Production.json | Produktionskonfiguration | Lesen |
C:\ProgramData\NodePilot\ | Schlüssel, Setup-Token, Logs und Betriebsdaten | Ändern |
C:\Program Files\NodePilot\wwwroot\docs\ | Diese Dokumentation, ausgeliefert unter /docs | Lesen |
C:\ProgramData\NodePilot\logs\ | Anwendungslog nodepilot-JJJJMMTT.log und Support-Log nodepilot-support-JJJJMMTT.log | Ändern |
Der Log-Ordner ist nur für Administratoren lesbar. Aufbewahrung und Auswahl der richtigen Datei: Logs & Diagnose.
Der Produkt-Quellcode ist optional
Das Server-Artefakt trägt unter knowledge\source einen Schnappschuss des Produkt-Quellcodes —
rund 2500 Dateien und 27 MB —, aus dem der KI-Assistent Quellcode-Fragen beantwortet. Das Setup
bietet dafür auf der Seite „Optionale Inhalte" ein Häkchen; abgewählt bleibt der Quellcode von der
Maschine fern. Unbeaufsichtigt lautet dieselbe Wahl "includeSourceSnapshot": false in der
Antwortdatei oder -OmitSourceSnapshot an Install-NodePilot.ps1.
Betroffen ist ausschließlich die Quellcode-Wissensquelle des Assistenten, und die ist ohnehin
standardmäßig aus (AiKnowledge:SourceCodeEnabled). An der Installation ändert sich sonst nichts.
Der Schnappschuss wird nach der Artefakt-Prüfung gelöscht, nicht beim Kopieren übersprungen: Die Installation wird gegen das signierte Manifest geprüft und muss exakt dessen Inhalt tragen — ein früheres Weglassen würde die Installation scheitern lassen. Ein Update erhält die Wahl: der Updater stellt den vorgefundenen Zustand wieder her, ein abgelehnter Quellcode kommt also nicht zurück.
Dokumentation auf dem Server
Der Installer legt dieselbe Dokumentation, die auch auf der öffentlichen Website steht, unter
wwwroot\docs ab. Sie ist über https://<host>/docs erreichbar — ohne Anmeldung, damit sie
auch dann zur Verfügung steht, wenn gerade die Anmeldung das Problem ist, und ohne
Internetzugang. Die ausgelieferte Fassung gehört zur installierten Version, während die Website
immer den aktuellen Entwicklungsstand zeigt. In der Oberfläche führt der Dokumentations-Knopf
unten links in der Seitenleiste dorthin.
Die Dokumentation hängt nicht an der Datenbank und bleibt deshalb auch während einer
Datenbankstörung lesbar. Sie steht allerdings nur zur Verfügung, solange der Dienst läuft —
startet er gar nicht erst, bleiben das Setup-Transkript unter %TEMP% und das Anwendungslog
unter C:\ProgramData\NodePilot\logs die Quelle.
Update und automatischer Rollback
$releaseSigner = "0123456789ABCDEF0123456789ABCDEF01234567"
.\deploy\Update-NodePilot.ps1 `
-ArtifactPath C:\Packages\NodePilot-2026.08.10.zip `
-TrustedArtifactSignerThumbprint $releaseSigner
Der Updater:
- prüft das neue Artefakt,
- lehnt ein Artefakt, das älter ist als die installierte Version (aus
VERSION.txtim Installationsverzeichnis), vor jeder Änderung ab;-AllowDowngradeinstalliert es trotzdem, das Datenbankschema bleibt dabei aber auf dem neueren Stand, - gleicht jedes Framework, das der neue Build nennt, gegen die Runtimes des Hosts ab und bricht vor jeder Änderung ab, wenn eines davon zu alt ist — Roll-Forward geht nie rückwärts, ein älterer Patch auf dem Host heißt also: der Dienst würde gar nicht erst starten,
- sichert die vorhandenen Binaries,
- wartet nach dem Dienststopp bis zu 30 Sekunden auf das Ende von Prozessen aus dem Installationsverzeichnis und beendet Verbliebene erzwungen; nur wenn das nicht greift, bricht der Updater mit Prozessname und PID vor der ersten Dateilöschung ab (ein gestoppter Dienst genügt nicht: verwaiste Worker halten ihre DLLs weiterhin gemappt),
- erhält Datenbank, Dienstkonto, Produktionskonfiguration und die vollständige Switcher-Konfiguration (Server-URL, SCOrch-Endpunkte, Allowlisten, Profile; die Datei wird byte-genau übernommen, und ihr Inhalt bricht das Update nie ab — ist sie für Windows PowerShell kein striktes JSON, bleibt die Server-URL ungeprüft und der Updater gibt eine Warnung aus),
- behält den Deinstaller des Server-Setups und dessen
deploy\-Skripte im Installationsverzeichnis, damit „Apps & Features“ eine von Hand aktualisierte Installation weiterhin deinstallieren kann, - startet den Dienst neu,
- prüft den Health-Endpunkt auf dem Port aus der installierten Konfiguration (
-HttpsPortist nur zum Überschreiben nötig); die Anfrage geht anlocalhostund akzeptiert nur das Zertifikat, dessen Fingerabdruck inKestrel:Https:CertificateThumbprintsteht, angegeben werden muss dafür nichts; lässt sich die Konfiguration nicht lesen, warnt das Update und prüft ohne diesen Abgleich, - stellt bei einem fehlgeschlagenen Health-Check die vorherigen Binaries wieder her und lässt den Dienst im Zustand vor dem Update,
- prüft den Health-Endpunkt nach einem Rollback erneut und warnt, wenn der wiederhergestellte Dienst nicht antwortet.
Ein erfolgreicher Update lässt den Dienst immer laufen, unabhängig davon, ob er vorher gestoppt war. Nur ein fehlgeschlagener Update stellt den Ausgangszustand wieder her.
Ein Rollback stellt nur die Binaries wieder her. Die neue Version migriert das Datenbankschema beim ersten Start vorwärts, und der Rollback macht das nicht rückgängig. Vor dem Update ein Datenbank-Backup anlegen und es zurückspielen, falls ein zurückgerolltes Binary das migrierte Schema ablehnt.
Workflow-History wird beim Start absichtlich noch nicht umgeschlüsselt, damit der unmittelbare
Health-Check-Rollback sicher bleibt. Während eines gemischten HA-Upgrades Workflow-Edits und
Rollbacks pausieren (oder nach dem ersten Schreibzugriff eines neuen Knotens keinen Failback auf
alte Knoten mehr zulassen): neue Knoten schreiben neue History-Snapshots sofort als np:wfv:v1:,
das alte Binary kann diese Zeilen nicht lesen. Sobald der Health-Check erfolgreich war und alle
HA-Knoten die neue Version ausführen, nach dem regulären DB-Backup
np secrets reencrypt --yes (oder die Aktion unter Admin-Einstellungen → Security) ausführen.
Danach sind auch alte WorkflowVersions als np:wfv:v1: geschützt. Nach dem ersten neuen
History-Write oder diesem Cutover darf nicht mehr auf eine Binärversion ohne Format-Unterstützung
zurückgerollt werden.
Das Binärbackup enthält keine secret-haltige appsettings.Production.json. Sie wird beim Austausch deshalb als Letztes ersetzt, damit ein Abbruch sie nicht zerstört.
Update bricht bei doppelten Custom-Activity-Keys ab
Custom-Activity-Keys sind unter allen nicht gelöschten Definitionen eindeutig, Entwürfe eingeschlossen. Die Migration, die das erzwingt, stoppt den ersten Start der neuen Version, wenn zwei solche Definitionen bereits denselben Key tragen, mit einer Meldung über doppelte Custom-Activity-Keys. Die Migration löscht und benennt nichts um, weil Workflows eine Definition über ihre ID und ihren Key referenzieren. Der fehlgeschlagene Start löst den automatischen Rollback der Binaries durch den Updater aus.
Zuerst ein Datenbank-Backup anlegen, dann die doppelten Keys mit der Abfrage für die eigene Datenbank auflisten:
-- PostgreSQL (SQLite: 0 statt FALSE)
SELECT "Key", COUNT(*) AS "Count"
FROM "CustomActivityDefinitions"
WHERE "IsDeleted" = FALSE
GROUP BY "Key"
HAVING COUNT(*) > 1;
-- SQL Server
SELECT [Key], COUNT(*) AS [Count]
FROM [CustomActivityDefinitions]
WHERE [IsDeleted] = 0
GROUP BY [Key]
HAVING COUNT(*) > 1;
Pro Key entscheiden, welche Definition bleibt, prüfen, welche Workflows die übrigen referenzieren, und die Duplikate vor dem nächsten Update in der alten Version ausdrücklich auflösen (zum Beispiel durch Löschen eines ungenutzten Entwurfs). Der Key-Vergleich folgt der Collation der Key-Spalte.
HTTPS-Zertifikat austauschen
Das Zertifikat lässt sich jederzeit an einer laufenden Installation wechseln — für die Ablösung eines selbstsignierten Setup-Zertifikats durch eines aus der eigenen PKI genauso wie für eine turnusmäßige Erneuerung. Eine Neuinstallation ist dafür nicht nötig; es ist ein Konfigurationswechsel.
# 1. Neues Zertifikat samt privatem Schlüssel importieren
$pfxPassword = Read-Host -AsSecureString "PFX password"
$cert = Import-PfxCertificate -FilePath 'C:\PKI\nodepilot-new.pfx' `
-CertStoreLocation Cert:\LocalMachine\My -Password $pfxPassword
$cert.Thumbprint
2. Dem Dienstkonto Leserecht auf den privaten Schlüssel geben. Das ist der Schritt, den ein Handwechsel übersieht — bei der Installation erledigt ihn der Installer selbst. Ohne ihn startet der Dienst nach dem Neustart nicht mehr, und die Meldung nennt das Zertifikat nicht.
$serviceAccount = 'DOMAIN\svc-nodepilot#x27;
$key = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
if (-not $key) {
$key = [System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions]::GetECDsaPrivateKey($cert)
}
if (-not $key) { throw 'Certificate has no accessible RSA/ECDSA private key.' }
try {
if ($key -is [System.Security.Cryptography.RSACng] -or $key -is [System.Security.Cryptography.ECDsaCng]) {
$keyFile = Join-Path $env:ProgramData "Microsoft\Crypto\Keys\$($key.Key.UniqueName)"
} elseif ($key -is [System.Security.Cryptography.RSACryptoServiceProvider]) {
$keyFile = Join-Path $env:ProgramData "Microsoft\Crypto\RSA\MachineKeys\$($key.CspKeyContainerInfo.UniqueKeyContainerName)"
} else {
throw 'Unsupported key provider: use its documented service-access procedure.'
}
if (-not (Test-Path -LiteralPath $keyFile -PathType Leaf)) { throw "Private key file not found: $keyFile" }
icacls.exe $keyFile /grant "$($serviceAccount):(R)"
if ($LASTEXITCODE -ne 0) { throw 'Private key ACL update failed. Do not switch certificates.' }
} finally { $key.Dispose() }
Setze $serviceAccount auf die installierte Dienstidentität. Der Ausschnitt behandelt
softwarebasierte CNG-RSA-/ECDSA-Schlüssel und ältere CSP-RSA-Schlüssel; bei fehlender Datei oder
gescheiterter ACL-Änderung bricht er ab. Bei hardwaregestützten Schlüsseln muss vor dem Wechsel
das vom Anbieter vorgesehene Verfahren für Dienstzugriff angewendet werden. LocalSystem besitzt
normalerweise bereits Zugriff auf private Maschinenschlüssel.
3. Thumbprint in der Konfiguration setzen. In C:\Program Files\NodePilot\appsettings.Production.json:
"Kestrel": { "Https": { "CertificateThumbprint": "<neuer Thumbprint>" } }
4. Dienst neu starten. Kestrel gehört zum boot-festen Konfigurationskern — ein Hot-Reload
greift dort nicht.
Restart-Service NodePilot
Invoke-WebRequest https://<host>:<port>/healthz/ready -UseBasicParsing
Das alte Zertifikat bis zum erfolgreichen Neustart im Store lassen: Scheitert der Start, ist der Rückweg das Zurücksetzen des Thumbprints in derselben Zeile. Erst danach entfernen.
Deinstallation
.\deploy\Uninstall-NodePilot.ps1
Entfernt werden Dienst, Dienst-Binaries samt der Binär-Sicherungen aus Updates und abgebrochenen
Installationen (<Installationspfad>.backup.* / .rollback.*), Firewall-Regeln, der Installations-Marker und der
Registry-Environment-Eintrag (in dem das Postgres-Passwort liegt). Logs und Konfiguration bleiben
erhalten. Vollständige Entfernung der lokalen Betriebsdaten:
.\deploy\Uninstall-NodePilot.ps1 -PurgeData
Nach einer Installation über das GUI-Setup geht dasselbe über „Apps & Features" oder direkt:
& 'C:\Program Files\NodePilot\unins000.exe' /VERYSILENT /SUPPRESSMSGBOXES /PURGEDATA=1
Lässt sich etwas nicht entfernen, bricht die Deinstallation mit einem Exit-Code ungleich 0 ab und
behält den Installations-Marker, die Deploy-Skripte und den Eintrag unter „Apps & Features". Die
Ursache steht in %TEMP%\nodepilot-server-uninstall.log; sie beheben und dieselbe Deinstallation
erneut ausführen.
Die Datenbank wird nie entfernt, und es gibt dafür keine Option. NodePilot legt sie nicht an — sie wurde in Kapitel 2 separat bereitgestellt, hat oft ein eigenes Backup- und Replikationsregime, und in einem Active/Passive-Cluster teilen sich beide Knoten dieselbe. Aus demselben Grund bleiben das „Log on as a service"-Recht des gMSA und die Lese-ACE auf dem Private Key des TLS-Zertifikats stehen; beide können mit einem anderen Dienst geteilt sein. Der Uninstaller benennt alle drei am Ende seines Laufs.
Backup und Wiederherstellung
Für eine vollständige Sicherung sind zwei Backups erforderlich:
- System-Configuration-Backup: Workflows, Maschinen, Credentials, Benutzer und Runtime-Einstellungen.
- Datenbank-Backup: Ausführungshistorie, Audit-Log, Statistiken und vollständiger Datenbestand.
PostgreSQL verwendet beispielsweise pg_dump; SQL Server verwendet die native SQL-Server-Sicherung. Details enthält Import, Export und Backup.
Hochverfügbarkeit
Active/Passive-Betrieb erfordert:
- mindestens zwei NodePilot-Nodes,
- gemeinsame externe Datenbank,
- identische JWT-Parameter,
Cluster:Enabled=true,- AES-GCM als Secret-Provider,
- HAProxy mit Leader-Probe auf
/healthz/leader, - bei OIDC einen gemeinsamen, zertifikatgeschützten Data-Protection-Keyring.
Die vollständige Einrichtung steht unter High Availability.
Enterprise-Funktionen aktivieren
Empfohlene Reihenfolge:
- SIEM-Logging aktivieren.
- Secret-Provider und gegebenenfalls HA konfigurieren.
- Lokales Break-Glass-Admin-Konto prüfen.
- LDAP- oder Windows-Konfiguration gegen echte Domain Controller testen.
- Dienst neu starten.
/healthz/readyund/healthz/directoryprüfen.- OIDC und SCIM erst nach abgeschlossenem Provider- und Offboarding-Test aktivieren.
LDAP, Windows SSO, OIDC und SCIM bleiben bis zum bestandenen Feldtest als Preview einzuordnen. Details enthält AD SSO Preview.