NSSM 能做什么
NSSM 是一个类似于 srvany 和 cygrunsrv 的服务辅助程序,
但具备一个真正的服务管理器该有的功能。它可以把任何应用作为 NT 服务启动,
并在应用因任何原因失败时自动重启服务。
任何程序都能变成服务
控制台工具、用 cmd /c 启动的脚本、Java 程序、游戏服务器、Node.js 应用——只要能从命令行启动,NSSM 就能把它作为服务运行。
自动重启
应用崩溃或退出后自动拉起。节流机制和可配置的重启延迟,避免一个坏掉的应用反复空转吃掉 CPU。
优雅关闭
停止服务时依次尝试 Control-C、窗口消息和线程消息,最后才调用 TerminateProcess()。每一级都有可配置的超时,也可以按需禁用。
日志与轮转
把 stdin/stdout/stderr 重定向到文件,支持毫秒级时间戳前缀、按大小或时间的在线轮转,以及针对被其他进程占用文件的 copy-and-truncate 轮转。
环境变量控制
用 AppEnvironment 和 AppEnvironmentExtra 替换或扩展服务环境,完整支持 %VAR% 展开。
优先级与 CPU 亲和
设置进程优先级,用 0-2,4 这样的简单写法把应用绑定到指定 CPU。
事件钩子
在启动、停止、退出、日志轮转、电源事件的前后运行你自己的命令。钩子能读到一整套 NSSM_* 环境变量,了解事件的来龙去脉。
任意账户运行
LocalSystem、虚拟服务账号或任意用户账户。启动类型、显示名称、描述、依赖关系、服务类型都能用命令行或图形界面设置。
图形界面 + 完整命令行
用友好的图形界面安装和编辑服务,也可以全程脚本化操作;用 nssm dump 一键导出任何服务的配置。
关于这个分支
NSSM 由 Iain Patterson 编写并以公共领域(public domain)授权发布,原站为 nssm.cc。上游最后一个版本(2.24)发布于 2017 年。 本项目是持续维护的延续版,包含以下改进,并与原版命令语法、注册表结构 100% 兼容。
持续维护
修复持续进行:卸载服务不再需要重启、日志线程稳定性、钩子与节流的竞态、内存安全等问题都已解决。
删除服务免重启
nssm remove 会先停止服务,遇到 ERROR_SERVICE_MARKED_FOR_DELETE 时自动重试——不用再为了重装一个服务而重启电脑。
安全加固
DLL 加载使用 LOAD_LIBRARY_SEARCH_SYSTEM32,并修复了缓冲区溢出、资源泄漏和空指针崩溃等一系列问题。
经过测试
catch2 单元测试和 Pester 集成测试套件在每次改动时于 CI 中运行,覆盖服务生命周期、参数读写和崩溃恢复。
对老系统友好
发布版二进制使用 Visual Studio 2008 编译,可运行于 Windows XP 及以后的 32 位与 64 位系统。日常开发使用 VS2019+。
sc.exe 风格别名
nssm delete 是 nssm remove 的别名,与 sc.exe 的习惯一致。
HKLM\SYSTEM\CurrentControlSet\Services\<服务名>\Parameters
下。目前暂不支持 WinSW 那样的 XML/YAML 配置文件——脚本化请用
nssm set,备份或迁移服务请用 nssm dump,它会导出一份
可直接执行的批处理脚本。
快速上手
所有命令都需要管理员权限的命令提示符。下面的例子把一个 Node.js 应用安装为
名为 myapp 的服务,请替换成你自己的路径。
-
安装服务
最快的方式——所有参数直接写在命令行上:
nssm install myapp "C:\apps\node\node.exe" "C:\apps\myapp\server.js"
或者不带程序路径运行
nssm install myapp,会打开图形安装界面。路径中有空格必须加引号。 -
调整基本配置
rem 程序的启动目录 nssm set myapp AppDirectory C:\apps\myapp rem 把 stdout 和 stderr 记录到日志文件 nssm set myapp AppStdout C:\logs\myapp.out.log nssm set myapp AppStderr C:\logs\myapp.err.log rem 每 10 MB 轮转一次日志,并在每行前面加时间戳 nssm set myapp AppRotateFiles 1 nssm set myapp AppRotateBytes 10485760 nssm set myapp AppTimestampLog 1 rem 让服务显示为"我的应用"并附上描述 nssm set myapp DisplayName "My Application" nssm set myapp Description "My web application server"
-
启动服务
nssm start myapp
确认它在运行:
nssm status myapp nssm processes myapp
-
日常管理
nssm restart myapp nssm stop myapp nssm list
也可以用
services.msc或sc.exe来控制——NSSM 管理的就是普通的 Windows 服务。 -
备份或迁移
nssm dump myapp
输出一份可重建该服务的批处理脚本。在另一台机器上(先复制过去
nssm.exe)执行即可克隆服务。nssm dump myapp myapp2会展示myapp的配置,但生成的命令针对名为myapp2的服务。 -
卸载服务
nssm remove myapp confirm
NSSM 会先停止服务再删除。即使服务正在运行也不需要重启系统。
nssm install(不带参数)或
nssm edit myapp,即可在对话框里配置一切——路径、日志、轮转、环境变量、
优先级、亲和性、钩子——不必敲命令行。
命令参考
凡是出现 <servicename> 的地方,既可以填服务的注册表键名,
也可以填它的显示名称。不带参数运行 nssm 可以查看内置帮助。
| 命令 | 语法 | 说明 |
|---|---|---|
install | nssm install <服务名> [<程序> [<参数>]] | 安装服务。给了程序路径就直接创建;不给则打开图形安装界面。 |
remove | nssm remove <服务名> [confirm] | 停止并删除服务。不带 confirm 时弹图形确认框。delete 是别名。 |
edit | nssm edit <服务名> | 打开已安装服务的图形编辑界面。 |
get | nssm get <服务名> <参数名> | 打印参数当前值。 |
set | nssm set <服务名> <参数名> <值> | 设置参数。其余所有参数用空格拼接后作为值。 |
reset | nssm reset <服务名> <参数名> | 把参数恢复为默认值。unset 是别名。 |
dump | nssm dump <服务名> [<新名字>] | 输出可重建该服务配置的批处理命令,引号与转义都已处理好。 |
start | nssm start <服务名> | 启动服务。 |
stop | nssm stop <服务名> | 停止服务。 |
restart | nssm restart <服务名> | 先停止再启动服务。 |
pause | nssm pause <服务名> | 暂停服务(同时暂停重启尝试)。 |
continue | nssm continue <服务名> | 恢复已暂停的服务。 |
status | nssm status <服务名> | 输出服务状态文本,如 SERVICE_RUNNING。成功时退出码为 0。 |
statuscode | nssm statuscode <服务名> | 以数字服务状态作为退出码,如 SERVICE_RUNNING 为 4。适合脚本判断。 |
rotate | nssm rotate <服务名> | 触发一次按需日志轮转(需开启在线轮转)。 |
list | nssm list [all] | 列出 NSSM 管理的服务;加 all 则列出系统上所有服务。 |
processes | nssm processes <服务名> | 显示该服务启动的进程 PID 与可执行文件路径。 |
version | nssm version | 打印 NSSM 版本与编译配置。 |
配置参考
所有配置都保存在注册表
HKLM\SYSTEM\CurrentControlSet\Services\<服务名>\Parameters
下,用 nssm get / nssm set / nssm reset 管理。
命令行上的参数名与下面的注册表值名称一致。
应用程序
| 参数 | 类型 | 含义 |
|---|---|---|
Application | 路径 | 要运行的程序的完整路径。相对路径按 NSSM 启动时所在目录解析。 |
AppParameters | 字符串 | 传给应用程序的命令行参数。 |
AppDirectory | 路径 | 应用程序的工作目录。 |
AppExit | 字符串 | 应用退出后的动作,见退出动作。可按退出码单独设置:nssm set <服务> AppExit <退出码> <动作>。 |
进程控制
| 参数 | 类型 | 含义 |
|---|---|---|
AppThrottle | REG_DWORD(毫秒) | 应用运行不足此时长(默认 1500)视为启动失败,重启间隔会逐次拉长,最长 4 分钟。在 services.msc 里发送"继续"可立即重试。 |
AppRestartDelay | REG_DWORD(毫秒) | 重启应用前无条件等待的时长,即使上次是干净退出也生效。与节流时长取较大者。 |
AppPriority | REG_DWORD | 优先级,例如 nssm set <服务> AppPriority NORMAL_PRIORITY_CLASS。可选值:REALTIME、HIGH、ABOVE_NORMAL、NORMAL、BELOW_NORMAL、IDLE(均带 _PRIORITY_CLASS 后缀)。 |
AppAffinity | REG_SZ | 逗号分隔、从 0 起算的 CPU 列表,支持区间,如 0-2,4。32 位 NSSM 最多管理 32 个 CPU。 |
AppNoConsole | REG_DWORD | 设为 1 时不创建默认的控制台窗口。 |
AppKillProcessTree | REG_DWORD | 设为 0 时停止动作只作用于主进程,不再作用于整个进程树。 |
I/O 重定向
| 参数 | 类型 | 含义 |
|---|---|---|
AppStdinAppStdoutAppStderr | 路径 | 分别连接应用 stdin、stdout、stderr 的文件。把 stdout 和 stderr 设为同一路径即可一起捕获。路径必须对服务账户可写。 |
AppStdout/Stderr/Stdin | REG_DWORD | 微调每个流的 CreateFile() 参数,如 AppStdoutShareMode、AppStdoutCreationDisposition、AppStdoutFlagsAndAttributes。缺省时使用合理默认值。 |
AppTimestampLog | REG_DWORD | 设为 1 时给每行重定向输出加毫秒级时间戳前缀,如 2016-09-06 10:17:09.451 消息内容。 |
日志轮转
| 参数 | 类型 | 含义 |
|---|---|---|
AppRotateFiles | REG_DWORD | 非零开启轮转。旧文件会按时间戳重命名,如 nssm-20131221T113939.457.log。 |
AppRotateOnline | REG_DWORD | 非零开启在线轮转(运行中轮转,并支持 nssm rotate)。此时 NSSM 会接管应用的 I/O,实现更复杂;默认关闭。 |
AppRotateSecondsAppRotateBytesAppRotateBytesHigh | REG_DWORD | 文件的最后写入时间不足此秒数 / 文件不足此大小时跳过轮转。两者可组合;AppRotateBytesHigh 把大小上限扩展到 64 位。 |
AppRotateDelay | REG_DWORD(毫秒) | 轮转后暂停片刻,让日志读取程序来得及感知文件变化。 |
AppStdoutCopyAndTruncateAppStderrCopyAndTruncate | REG_DWORD | 以"先复制再清空原文件"的方式轮转,适用于被其他进程占用而无法改名的文件。期间会临时占用双倍磁盘空间。 |
退出动作
NSSM 读取以应用退出码命名的 AppExit 值,找不到时回退到默认值。
退出码可到 Windows 事件日志里查。按退出码设置用
nssm set <服务> AppExit <退出码> <动作>,默认动作用
nssm set <服务> AppExit Default <动作>。
| 取值 | 行为 |
|---|---|
Restart | 重启应用(默认行为)。 |
Ignore | 应用退出但服务继续运行(模拟 srvany,通常不是你想要的)。 |
Exit | 服务干净地停止,让 Windows 的恢复机制接管。 |
Suicide | 模拟崩溃以触发 Vista 之前的恢复机制。仅当显式配置了退出码 0 时才对退出码 0 生效。 |
停止应用的方式
服务停止时 NSSM 按顺序尝试四种方式,每种默认超时 1500 毫秒,作用于整个进程树:
- 在应用的控制台产生 Control-C 事件。
- 向应用的窗口发送
WM_CLOSE。 - 向应用的线程发送
WM_QUIT。 - 调用
TerminateProcess()。
| 参数 | 类型 | 含义 |
|---|---|---|
AppStopMethodSkip | REG_DWORD 位掩码 | 跳过某些方式:1 = 不发 Control-C,2 = 不发 WM_CLOSE,4 = 不发 WM_QUIT,8 = 不调 TerminateProcess()。慎用 8——应用可能因此永远不会退出。 |
AppStopMethodConsoleAppStopMethodWindowAppStopMethodThreads | REG_DWORD(毫秒) | 各方式的超时。计时按进程树里的每个进程分别计算,总停止时间可能超过各超时之和。 |
环境变量
| 参数 | 类型 | 含义 |
|---|---|---|
AppEnvironment | 多字符串 | 整体替换服务环境。已定义过的变量无法再被引用。 |
AppEnvironmentExtra | 多字符串 | 在服务环境后追加。大多数人应该用它。 |
Environment | 多字符串 | Windows 标准服务环境块(对所有服务生效,不限 NSSM)。先按字母排序再并入系统环境。 |
每条是 KEY=VALUE;变量按出现顺序展开,所以
PATH=C:\bin;%PATH% 在 AppEnvironmentExtra 里能正常工作。
命令行下每对键值是一个独立参数,+KEY=VALUE / -KEY
可以增删单个变量:
nssm set myapp AppEnvironmentExtra JAVA_HOME=C:\jdk-17 nssm set myapp AppEnvironmentExtra +PATH=C:\apps\myapp\bin nssm set myapp AppEnvironmentExtra -JAVA_HOME
系统级服务设置
这些对应标准的服务注册表值,对非 NSSM 管理的服务同样可以读写:
| 参数 | 含义 |
|---|---|
DisplayName | services.msc 里显示的友好名称。 |
Description | 服务描述。 |
Start | 启动类型:SERVICE_AUTO_START、SERVICE_DELAYED_START、SERVICE_DEMAND_START、SERVICE_DISABLED。 |
Type | SERVICE_WIN32_OWN_PROCESS(默认)或 SERVICE_INTERACTIVE_PROCESS(仅限 LocalSystem)。 |
ObjectName | 运行账户:nssm set <服务> ObjectName <用户> <密码>。内置账户(LocalSystem、LocalService、NetworkService、NT Service\<服务>)不需要密码。 |
DependOnServiceDependOnGroup | 依赖,每项一个参数;前缀 +/- 可增删单项,如 nssm set <服务> DependOnService +RpcSs。 |
ImagePath | 服务可执行文件路径(即 NSSM 自己)。 |
Name | 只读:服务的注册表键名。只知道显示名时很有用。 |
事件钩子
NSSM 可以在服务事件的前后运行你自己的命令。用
nssm set <服务名> AppEvents <事件>/<动作> <命令>
配置,或直接设置注册表字符串值
Parameters\AppEvents\<事件>\<动作>。
| 事件/动作 | 时机 | 同步 |
|---|---|---|
Start/Pre | 应用启动之前 | 是 |
Start/Post | 应用成功启动之后 | 否 |
Stop/Pre | 应用被结束之前 | 是 |
Exit/Post | 应用退出并清理完毕之后 | 否 |
Rotate/Pre、Rotate/Post | 在线日志轮转前 / 后 | Pre 为同步 |
Power/Change、Power/Resume | 电源状态变化 / 从待机恢复 | 否 |
钩子继承服务的环境变量,并额外获得 %NSSM_EXE%、
%NSSM_SERVICE_NAME%、%NSSM_EVENT%、
%NSSM_EXITCODE% 等上下文变量;使用前先检查
%NSSM_HOOK_VERSION%。例:系统从待机恢复时自动重启服务:
nssm set myapp AppEvents Power/Resume "%NSSM_EXE% restart %NSSM_SERVICE_NAME%"
全部钩子环境变量(NSSM_HOOK_VERSION ≥ 1)
| 变量 | 内容 |
|---|---|
NSSM_EXE | NSSM 自身路径 |
NSSM_CONFIGURATION | 编译信息,如 64-bit debug |
NSSM_VERSION | NSSM 版本 |
NSSM_BUILD_DATE | NSSM 编译日期 |
NSSM_PID | 运行中的 NSSM 进程 PID |
NSSM_DEADLINE | 多少毫秒后钩子会被强制结束 |
NSSM_SERVICE_NAME | 服务注册表键名 |
NSSM_SERVICE_DISPLAYNAME | 服务显示名 |
NSSM_COMMAND_LINE | 启动应用所用的命令行 |
NSSM_APPLICATION_PID | 应用进程 PID(可能为空) |
NSSM_EVENT / NSSM_ACTION | 触发钩子的事件类别与动作 |
NSSM_TRIGGER / NSSM_LAST_CONTROL | 触发钩子的服务控制 / 最近一次处理的服务控制 |
NSSM_START_REQUESTED_COUNT | 累计启动请求数 |
NSSM_START_COUNT | 累计成功启动数 |
NSSM_THROTTLE_COUNT | 距上次成功启动的慢启动次数 |
NSSM_EXIT_COUNT | 累计应用退出次数 |
NSSM_EXITCODE | 最近一次应用退出码(可能为空) |
NSSM_RUNTIME | NSSM 已运行毫秒数 |
NSSM_APPLICATION_RUNTIME | 应用自最近一次启动以来的运行毫秒数 |
Start/Pre 钩子返回退出码 99 时会中止应用启动。把
AppRedirectHooks 设为 1 可让钩子输出进入服务配置的
stdout/stderr。所有钩子超时后都会被强制结束。
常见问题与排障
NSSM 与 sc.exe、srvany 有什么区别?
sc.exe 只是创建服务登记项,要求程序自己实现服务协议;srvany 能承载任意程序但不会重启它。NSSM 既承载任意程序,又对它做监督:失败自动重启、优雅关闭、日志记录、节流和按退出码的策略。
NSSM 支持 WinSW 那样的配置文件吗?
不支持。配置保存在注册表里,与原版 NSSM 完全一致。脚本化用 nssm set;备份或克隆服务用 nssm dump,它输出的批处理脚本可以直接重建全部配置。
服务启动不了
到 Windows 事件日志(应用程序日志,来源为 NSSM)里查具体错误。常见原因:AppDirectory 不对、可执行文件不存在、路径含空格没加引号、服务账户对程序或日志文件没有权限。
端口冲突
应用能启动但一直失败时,检查端口是否被占用:netstat -ano | findstr <端口>,输出里的 PID 就是冲突进程。注意上一个实例可能还在关闭过程中;nssm processes <服务名> 能看到 NSSM 启动了哪些进程。
"服务已标记为删除"
本分支的 nssm remove 会先停止服务并自动重试。如果仍然失败,关掉占用服务句柄的工具(services.msc、事件查看器)再试;2017 年那种"重启电脑解决"的老办法已经不需要了。
用 32 位还是 64 位?
64 位系统用 64 位版。32 位版到处可用,但亲和性最多管理 32 个 CPU,并且在老系统上 nssm processes 读不到 64 位进程的路径。
支持哪些 Windows 版本?
发布版二进制用 Visual Studio 2008 编译,可运行于 Windows XP 及以后的 32 位与 64 位系统。这让 NSSM 很适合继续"养"那些老的 Windows 7 机器。
NSSM 自己的消息输出到哪里?
写到 Windows 事件日志,每类消息有唯一的事件 ID,且跨版本永不改变。你应用的输出则去往 AppStdout/AppStderr 指定的文件。
真的免费吗?
真的——NSSM 是公共领域(public domain)软件。你可以无条件地为任何目的使用它及其源代码。