NSSM 101 & Caveats
The dreaded `Unexpected status SERVICE_PAUSED in response to START control.`
Overview
You can use NSSM, an open-source tool with both GUI and CLI capabilities to create services on Windows. I am penning down the caveats to look out for while using NSSM.
Usage
GUI
You can create a service via GUI.
The main tabs to configure are Application
and I/O
.
Application
: Configure the command to use, the directory to run from and the arguments to pass to the target commandI/O
: To define the console input/output and error messages output files for logging
CLI
Alternatively, you can create a service via CLI.
Below is a snippet when creating a service to run a PowerShell script:
# Defining the arguments to facilitate the service creation
$powershell = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$scriptFile = "hello-world.ps1"
$arguments = '-ExecutionPolicy Bypass -NoProfile -File {0}' -f $scriptFile
# Install the NSSM service
nssm install $serviceName $powershell $arguments
nssm set $serviceName AppDirectory "$targetDirectory"
## Configure the standard input, output and error logs file location
nssm set $serviceName AppStdin "$stdinPath"
nssm set $serviceName AppStdout "$stdoutPath"
nssm set $serviceName AppStderr "$stderrPath"
## (Optional) Define which user to logon to run as
## Default to local system account
## Useful when you intend to use a service account to run a script/app
#nssm set $serviceName ObjectName $username $password
Caveats
When there is an error, NSSM throws an ambiguous and dreaded error message - Unexpected status SERVICE_PAUSED in response to START control.
๐ฉ It is both frustrating yet interesting in the resolutions
The issues I have encountered that will result in the above error message include but are not limited to
NSSM misconfigurations
Permission issues
Service running too fast (?!)
Unable to retrieve defined global variables
NSSM misconfigurations
The issue arises due to leaving the AppError
path empty. When I was new to NSSM, I interpreted that AppStdin
and AppStdout
would suffice the configuration.
After using NSSM for a while, I realise that it is fine to leave AppStdin or AppStdout empty, but not AppError.
This makes sense since having both AppStdin
and AppStdout
may mean being too verbose. However, with no AppError
, the NSSM service will be unable to capture the errors thrown by the application.
Permission issues
This was a little hard to debug initially. You may need to gather context by reading into the event logs in Windows Event Viewer.
From my experience, you need to ensure that the users have the required permissions to
NSSM and its folder
Target application binary, its folder and other files
To modify the folder and file permission, right-click on the target folder > Properties > Security > Advanced.
Service running too fast
This is one of the weirdest (and mind-blowing) error I have ever encountered, whereby we are penalised for when the service is able to run quickly.
If it wasn't for this Stack Overflow post, I doubt I would be able to solve this.
To resolve this, I included Start-Sleep
in my PowerShell script to make the script pauses for some time when NSSM executes it before ending the service
I suspect it might also be due to the exit method, which I set to be one-shot
mode i.e. manual service. The rationale behind my perspective is:
NSSM is a wrapper on top of the native Windows Service Controller
As the
one-shot
mode will stop the service upon completion of execution, it can be that the underlying Windows service that NSSM interfaces with has stopped way faster than NSSM get to register the service status ofSTART
andSTOP
Hence, resulting in the NSSM service failing to start
Unable to retrieve defined global variables
When I was running a PowerShell script that included some global variables (e.g. $global:ENV_VARIABLE), NSSM seemingly can't retrieve those values.
Some context for PowerShell session
The initial PowerShell session started will be the parent session. Using the global variables (e.g. $global:ENV_VARIABLE), the child session will be able to retrieve those values.
The rationale behind NSSM unable to retrieve global variables?
The key is - the means of invocation matters. This Stack Overflow post's example helped me to better understand PowerShell scopes.
Quoting specific sections from the PowerShell Documentation
From About_Scopes page
Scripts and functions follow the rules of scope. ... When you run a script or function using dot-source notation, it runs in the current scope. Any functions, aliases, and variables in the script or function are added to the current scope.
From About_Operators page
Runs a command, script, or script block. The call operator, also known as the "invocation operator", lets you run commands that are stored in variables and represented by strings or script blocks. The call operator executes in a child scope
The fact that you need to define the Application Path
i.e. the path to PowerShell command, the powershell
command will be invoked each time the NSSM service is started, hence a new parent session will be created with no previous global variables.
Resolution
You can leverage the AppEnvironmentExtra
setting to define the global variables as environment variables (i.e. $env:ENV_VARIABLE) in the session/context managed by NSSM service.
Final Thoughts
I leveraged NSSM due to its convenience of configuring the service to logon as another Windows account. However, it can get frustrating to debug due to its generic error messages.
Considering that NSSM is not actively maintained, I am looking into the NSSM source code to better understand the underlying mechanism of the ObjectName
settings and perhaps finding other means to run the application as a service on Windows.
So keep a look out for my next article! Cheers ๐ป