Run Node.js as a Windows service without PATH surprises
Wrap Node with NSSM or WinSW, pin the absolute node.exe, set cwd/env, capture logs and verify the service uses the intended version.
Tested onStandard / Datacenter · Build 26100.33650 · x64
E1- Build
- 26100.33650
- Edition
- Standard / Datacenter
- Architecture
- x64
- Support
- Documentation (E1)
- Evidence
- E1Official documentation
- Verified
- 2026-10-29
- Page type
- Task
The short answer
Section titled “The short answer”Node has no built-in Windows service mode. Use a wrapper:
- NSSM — quick, interactive
nssm install; - WinSW — XML config, good for repeatable deployments.
The service must run the absolute node.exe, not node from PATH.
nssm install api-a "C:\node\20\node.exe" "C:\apps\api-a\server.js"nssm set api-a AppDirectory "C:\apps\api-a"nssm set api-a AppEnvironmentExtra NODE_ENV=production PORT=3000nssm set api-a AppStdout "C:\logs\api-a\out.log"nssm set api-a AppStderr "C:\logs\api-a\err.log"nssm set api-a AppRotateFiles 1nssm start api-aSet AppDirectory; Node resolves require('./...'), .env and relative paths from the working directory.
<service> <id>api-a</id> <name>API A</name> <executable>C:\node\20\node.exe</executable> <arguments>C:\apps\api-a\server.js</arguments> <workingdirectory>C:\apps\api-a</workingdirectory> <env name="NODE_ENV" value="production"/> <env name="PORT" value="3000"/> <logpath>C:\logs\api-a</logpath> <log mode="roll-by-size"/> <onfailure action="restart" delay="10 sec"/></service>Install with winsw install / winsw start. Keep the XML next to the wrapper and version it.
Identity and permissions
Section titled “Identity and permissions”- Use a dedicated service account, not LocalSystem.
- Grant read/execute on
C:\node\20andC:\apps\api-a. - Grant write only on the app’s logs/uploads/temp.
- Do not give the app write access to
C:\node.
Verify the real version
Section titled “Verify the real version”sc.exe qc api-aGet-CimInstance Win32_Process -Filter "Name='node.exe'" | Select-Object ProcessId, ExecutablePath, CommandLineInvoke-WebRequest http://localhost:3000/ -UseBasicParsing | Select-Object StatusCodeGet-Content C:\logs\api-a\err.log -Tail 50ExecutablePath must be the pinned C:\node\20\node.exe. If it is the nvm symlink, an nvm use can change the service on next restart.
Upgrade
Section titled “Upgrade”- Install the new Node beside the old.
- Test with the absolute path.
- Change the wrapper’s executable.
- Restart one service; verify
ExecutablePathand logs. - Keep the old version for rollback.
Lab boundary
Section titled “Lab boundary”E1. Node/nvm and a service wrapper were not installed in this lab; the commands are the service pattern to verify on Server 2025.