# 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 on**

- Build: 26100.33650
- Edition: Standard / Datacenter
- Architecture: x64
- Evidence: E1
- Verified: 2026-10-29

## 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

```bat
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=3000
nssm 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 1
nssm start api-a
```

Set `AppDirectory`; Node resolves `require('./...')`, `.env` and relative paths from the working directory.

## WinSW

```xml
<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

- Use a dedicated service account, not LocalSystem.
- Grant read/execute on `C:\node\20` and `C:\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

```powershell
sc.exe qc api-a
Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
  Select-Object ProcessId, ExecutablePath, CommandLine
Invoke-WebRequest http://localhost:3000/ -UseBasicParsing | Select-Object StatusCode
Get-Content C:\logs\api-a\err.log -Tail 50
```

`ExecutablePath` 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.

> **Caution: Do not use PM2 as the Windows service manager by default**
>
> PM2 can run on Windows, but its daemon/startup model adds another layer. For a single Windows service, NSSM/WinSW is easier to audit and restart with the OS.

## Upgrade

1. Install the new Node beside the old.
2. Test with the absolute path.
3. Change the wrapper’s executable.
4. Restart one service; verify `ExecutablePath` and logs.
5. Keep the old version for rollback.

## 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.

## Sources

- [WinSW](https://github.com/winsw/winsw)
- [Node.js — downloads](https://nodejs.org/en/download)

---

Source: https://winbook.fyi/en/server/runtimes/node-service/
