NSSM(Non-Sucking Service Manager)是一支免費開源工具,可以把幾乎任何 EXE 包裝成 Windows 服務,讓它開機自動啟動、當掉自動重啟、並把 stdout/stderr 導向日誌檔。

版本現況:NSSM 最後一個正式釋出版是 2014 年的 2.24;預覽版 2.25 在官方 git 上持續有提交,但無正式發布日期。功能已相當完整,只是不要期待活躍的版本更新。

NSSM 與 SC 命令的差異

Windows 內建的 sc.exe 可以註冊服務,但它的前提是「被包裝的程式本身已實作 Windows Service API」——也就是說要會回應 SCM 的 Start/Stop/Pause 訊號。一般的 console EXE 沒有這套邏輯,用 sc 直接包會秒停。

NSSM 的做法是自己當服務宿主,在 SCM 和你的 EXE 中間做代理:SCM 叫 NSSM 停,NSSM 再去 terminate 你的 EXE。因此任何 console 程式都能包,不需要改原始碼。

其他 SC 辦不到、NSSM 能辦到的:

  • stdout / stderr 轉存為日誌檔,不用另外掛 wrapper script
  • 崩潰後自動重啟,可設延遲時間與最大重試次數
  • 設定服務專屬的環境變數(AppEnvironmentExtra
  • 依賴其他服務(服務啟動順序)
  • 提供 GUI 介面,不用背 sc 參數語法

什麼時候用 SC 就夠:程式本身已實作 Service API,或只是要設定服務的啟動類型、描述等屬性,sc config 語法就很夠用。

下載與安裝

  1. 前往 nssm.cc/download,下載最新壓縮包
  2. 解壓縮,依系統選 win32/nssm.exewin64/nssm.exe
  3. nssm.exe 放到固定路徑,例如 C:\Tools\NSSM\nssm.exe
  4. 把該目錄加進系統的 PATH 環境變數,之後就能直接輸入 nssm 而不用打完整路徑

所有 NSSM 指令都需要以系統管理員身份執行命令提示字元或 PowerShell。

建立服務

圖形介面方式

1
nssm install <服務名稱>

會開啟 GUI,填入三個必要欄位:

  • Path:EXE 的完整路徑,例如 C:\MyApp\MyProgram.exe
  • Startup directory:EXE 所在目錄(程式找相對路徑的設定檔時會用到)
  • Arguments:命令列參數(選填)

填完按「Install service」。

命令列方式

1
nssm install <服務名稱> <EXE完整路徑> [參數]

範例:

1
nssm install MyService "C:\MyApp\MyProgram.exe" -config "C:\MyApp\config.ini"

路徑含空格記得加引號。

進階設定

建立服務後,用 nssm set 調整各項設定。

顯示名稱與描述

1
2
nssm set MyService DisplayName "My App Service"
nssm set MyService Description "負責處理背景資料同步"

這兩個值會出現在「服務」管理員(services.msc)的清單裡。

啟動類型

1
nssm set MyService Start SERVICE_AUTO_START

可選值:SERVICE_AUTO_START(自動)、SERVICE_DELAYED_AUTO_START(延遲自動)、SERVICE_DEMAND_START(手動)、SERVICE_DISABLED

環境變數

1
nssm set MyService AppEnvironmentExtra "MYVAR=hello" "PATH=C:\MyApp;%PATH%"

務必用 AppEnvironmentExtra,不要用 AppEnvironment AppEnvironment 會完整取代服務的環境區塊,且不展開 %PATH%(NSSM 不做變數展開,%PATH% 會被當成字面值),結果是原本的系統 PATH 全部消失,程式可能找不到相依的 DLL 或工具。AppEnvironmentExtra 是在現有環境基礎上附加,%PATH% 能正常展開。

崩潰重啟

1
nssm set MyService AppExit Default Restart

Default 指任何退出碼都觸發重啟。也可以針對特定退出碼設定不同行為:

1
2
nssm set MyService AppExit 0 Exit
nssm set MyService AppExit Default Restart

這樣程式正常結束(exit code 0)就不重啟,異常退出才重啟。

日誌輸出

1
2
nssm set MyService AppStdout "C:\Logs\MyService.log"
nssm set MyService AppStderr "C:\Logs\MyService.err"

目錄必須存在,NSSM 不會自動建立。

管理服務

1
2
3
4
5
nssm start MyService
nssm stop MyService
nssm restart MyService
nssm status MyService
nssm edit MyService # 開啟 GUI 修改設定

移除服務

移除前先確認服務已停止,否則 SCM 可能需要重開機才能清除:

1
2
nssm stop MyService
nssm remove MyService confirm

常見問題

服務啟動後立即停止

最快的排查順序:

  1. 直接在命令列手動執行 EXE(用相同的使用者帳號、相同的工作目錄),確認程式本身能正常跑
  2. 執行 nssm status MyService,看目前狀態
  3. 開啟「事件檢視器」→「Windows 記錄」→「應用程式」,篩選來源為 NSSM 或你的程式名稱,錯誤訊息通常在這裡
  4. 確認 AppStdout / AppStderr 日誌有沒有寫入內容

「確保所有必要的依賴項都可用」這種說法太籠統——實際上最常見的是工作目錄設錯導致程式找不到相對路徑的設定檔,或服務帳號缺少存取某個資料夾的權限。

無法連線到服務

服務預設以 LocalSystem 帳號執行,這個帳號的網路存取行為與一般使用者不同(例如存取網路共用資料夾、AD 資源)。改用有適當網路權限的 AD 帳號或 NetworkService 往往能解決。

互動式程式的限制

NSSM 雖然支援將需要 stdin/stdout 的程式包成服務,但 Windows Vista 之後的 Session 0 隔離讓服務無法與登入使用者的桌面互動:服務產生的視窗或對話框不會顯示給使用者,鍵盤滑鼠事件也進不去。程式「看起來沒動靜」但實際上可能卡在等待輸入。

如果你的 EXE 需要互動,包成服務之前要先改成從設定檔讀參數,或透過 IPC(named pipe、TCP socket)接受指令,不能靠終端機互動。

最佳實踐

  • 指定專屬的服務帳號,而不是跑在 LocalSystem 下,避免權限過大
  • 設定 AppStdoutAppStderr,問題出現時才有日誌可查
  • 在測試機驗證完整的 install → start → stop → remove 流程,再上生產機
  • 服務名稱用英數字加底線或連字號,避免空格;DisplayName 才是給人看的