# Servy - Full Documentation Context
> Auto-generated full documentation context compiled from the Servy Wiki.
> Generated on: 2026-10-10T14:18:15Z
---
# Document: Home
> Source: https://github.com/aelassas/servy/wiki/Home
# Servy
Servy is a Windows tool that lets you run any executable as a Windows service, with full control over configuration, monitoring, management, and recovery, all via a clean graphical interface, CLI, or PowerShell.
The modern build supports **Windows 10 (1809+)**, **Windows 11**, and **Windows Server 2016+**. Legacy systems such as Windows 7 SP1 and Windows Server 2008 R2 are also supported via a dedicated **.NET Framework 4.8 build**. See the [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide#version-comparison) to choose the right version for your OS.
Starting from v10.2, Servy provides enterprise-grade per-service isolation between custom accounts, DPAPI, HKDF & AES-256 HMAC authenticated encryption, and automated vault ACL hardening. All releases feature signed binaries, SBOMs, and continuous vulnerability scanning. See [Security.md](https://github.com/aelassas/servy/wiki/Security) and [Architecture.md](https://github.com/aelassas/servy/wiki/Architecture) for details.
> [!TIP]
> Use the sidebar to browse installation guides, configuration options, and more.
## Why Use a Windows Service Wrapper?
Applications written in Node.js, Python, Java, Go, PHP, Ruby, Rust, or similar environments are usually started as ordinary console or desktop programs. They run inside a user session, which causes problems on servers and other unattended machines:
* **Logoff:** When the user logs off, Windows ends all processes in that session, including the application.
* **Reboot:** After a restart, the application does not run until someone logs in and starts it manually.
* **Crashes and hangs:** If the process exits unexpectedly or stops responding, nothing restarts it.
* **Output growth:** If console output is redirected to a file, the file grows without limit unless something rotates it.
A Windows service wrapper registers an ordinary executable with the Windows Service Control Manager (SCM) and runs it as a service. The application then starts at boot, runs without a logged-in user, and is restarted on failure, without any change to its source code.
See [examples and recipes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes) for real-world usage and configurations across different environments.
## Core Problems Servy Solves
Servy covers the basics of running an executable as a service, plus the operational tasks that usually come with it.
| Problem | How Servy addresses it |
| --- | --- |
| **Application stops at logoff or does not start after reboot** | Runs the application as a Windows service in Session 0. It starts at boot, before any user logs in, on both x64 and ARM64. |
| **Application crashes or hangs** | Monitors the process with health checks and heartbeats, restarts it according to a configurable recovery policy, and sends failure alerts through Windows notifications or email. |
| **Child processes left running after a stop** | Stops the application with a `Ctrl+C` or close-window signal, forwards the stop request to its child processes, and terminates any that remain. |
| **Log files that grow without limit** | Writes stdout and stderr to files and rotates them by size or by date. |
| **Limited visibility into running services** | Servy Manager shows CPU and RAM usage graphs, live console output, the status of service dependencies, and searchable logs. |
| **Setup and cleanup around startup and shutdown** | Runs pre-launch, post-launch, pre-stop, and post-stop programs, with configurable retries, timeouts, and failure handling. |
| **Process and environment settings** | Supports a working directory, startup parameters, environment variables with expansion, process priority, CPU affinity, and service dependencies. |
| **Service accounts and permissions** | Runs services as Local System, a local or domain user, or a Group Managed Service Account (gMSA). |
| **Deployment and management** | Provides a desktop app, a CLI (`servy-cli`), a PowerShell module, and service import and export. It supports current Windows versions as well as Windows 7 SP1 and Windows Server 2008 R2 through the .NET Framework 4.8 build. |
---
# Document: Overview
> Source: https://github.com/aelassas/servy/wiki/Overview
## Table of Contents
1. [Get Started](https://github.com/aelassas/servy/wiki/Overview#get-started)
1. [Quick Example](https://github.com/aelassas/servy/wiki/Overview#quick-example)
1. [Desktop App](https://github.com/aelassas/servy/wiki/Overview#desktop-app)
1. [Service Details](https://github.com/aelassas/servy/wiki/Overview#service-details)
1. [Logging](https://github.com/aelassas/servy/wiki/Overview#logging)
1. [Recovery](https://github.com/aelassas/servy/wiki/Overview#recovery)
1. [Advanced](https://github.com/aelassas/servy/wiki/Overview#advanced)
1. [Log On](https://github.com/aelassas/servy/wiki/Overview#log-on)
1. [Pre-Launch](https://github.com/aelassas/servy/wiki/Overview#pre-launch)
1. [Post-Launch](https://github.com/aelassas/servy/wiki/Overview#post-launch)
1. [Pre-Stop](https://github.com/aelassas/servy/wiki/Overview#pre-stop)
1. [Post-Stop](https://github.com/aelassas/servy/wiki/Overview#post-stop)
1. [Manager App](https://github.com/aelassas/servy/wiki/Overview#manager-app)
1. [Services](https://github.com/aelassas/servy/wiki/Overview#services)
1. [Performance](https://github.com/aelassas/servy/wiki/Overview#performance)
1. [Console](https://github.com/aelassas/servy/wiki/Overview#console)
1. [Dependencies](https://github.com/aelassas/servy/wiki/Overview#dependencies)
1. [Logs](https://github.com/aelassas/servy/wiki/Overview#logs)
1. [CLI / PowerShell](https://github.com/aelassas/servy/wiki/Overview#cli--powershell)
1. [CLI](https://github.com/aelassas/servy/wiki/Overview#cli)
1. [PowerShell](https://github.com/aelassas/servy/wiki/Overview#powershell)
1. [See Also](https://github.com/aelassas/servy/wiki/Overview#see-also)
> [!IMPORTANT]
> **Console UI Compatibility**
> If your app tries to visually update the command prompt (like clearing the screen or moving the cursor), it will crash when running as a background service since services don't have visible windows. To fix this without changing your code, enable the **Enable Console UI** option in the service configuration while installing your service.
## Get Started
Servy lets you run any application as a native Windows service with full control over startup, environment, logging, and lifecycle management.
You can manage services using the desktop app (GUI), the CLI (`servy-cli`), or PowerShell.
> [!IMPORTANT]
> **Desktop Interaction and Session 0 Isolation**
>
> Servy can wrap any executable as a native Windows service, including GUI apps as long as they do not require an interactive desktop session. Like all Windows service wrappers (including NSSM and WinSW), Servy processes execute within **Session 0**, which runs in an isolated environment without interactive desktop access.
>
> If you attempt to run a GUI application that requires an interactive desktop as a service, it will fail to start or crash at runtime, often throwing errors such as Access Violation (`0xC0000005`) or Null Pointer Exception. If your application requires an active desktop session, use an alternative automation tool (such as **Windows Task Scheduler** configured to run only when a user is logged on) instead.
> [!NOTE]
> **Before You Begin**
>
> Servy requires administrator rights to install and manage Windows services. If you are using a modern Windows workstation or server, choose the .NET 10.0+ build. If you need support for older platforms such as Windows 7 SP1 or Windows Server 2008 R2, use the .NET Framework 4.8 build instead.
To get started, download the latest release from [GitHub](https://github.com/aelassas/servy/releases/latest) or install via a package manager:
**WinGet**
```powershell
winget install servy
```
**Chocolatey**
```powershell
choco install -y servy
```
**Scoop**
```powershell
scoop bucket add extras
scoop install servy
```
**Patch My PC**
Servy is available in the official [Patch My PC catalog](https://patchmypc.com/supported-products/) for enterprise automated deployment and updates via Microsoft Intune and ConfigMgr (SCCM).
After a default installation `servy-cli` is on the system **PATH** - see [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide#add-servy-to-path) for the option that controls this and for the portable package.
## Quick Example
You can manage services using the [desktop app (GUI)](https://github.com/aelassas/servy/wiki/Servy-Desktop-App), the [CLI](https://github.com/aelassas/servy/wiki/Servy-CLI), or [PowerShell](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module).
Here's a minimal example using the CLI to run a Node.js app as a Windows service. Run it from an elevated PowerShell prompt: installing, starting and stopping a Windows service all require administrator privileges.
```powershell
servy-cli install `
--name="MyService" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\MyServer" `
--params="server.js" `
--enableHealth
```
This creates a service named `MyService` that runs your Node.js server in the background, starts automatically with Windows, and has [health monitoring](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery) enabled.
Then start the service:
```powershell
servy-cli start --name="MyService"
```
Or from an **elevated** Command Prompt:
```cmd
sc.exe start MyService
```
Explore more [examples and recipes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes) for Python, Java, Go, and other popular frameworks.
## Desktop App
The [Servy Desktop App](https://github.com/aelassas/servy/wiki/Servy-Desktop-App) provides an intuitive graphical interface to configure, install, and manage individual Windows services.
### Service Details
Configure core service properties including name, description, executable path, arguments, working directory, and startup type. See [Servy Desktop App](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#service-details).
For more details on CPU affinity, see the [FAQ](https://github.com/aelassas/servy/wiki/FAQ#how-and-why-should-i-use-cpu-affinity-with-servy).
### Logging
Configure `stdout` and `stderr` log file redirection, size-based and date-based log rotation, and retention policies. See [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation).
> [!NOTE]
> Enabling the debug option will record sensitive information in the local log file at `%ProgramData%\Servy\logs\services\\Servy.Service.log`. This behavior only occurs when local logging is enabled, which is the default setting. Sensitive data is never recorded in the Windows Event Log. The CLI `show` command and `Show-ServyService` mask the encrypted fields by default; `--decrypt` / `-Decrypt` prints the parameters and environment variables in clear text (the stored password always stays masked). See [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI) and [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module).
For detailed information about logging, check out the [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation) documentation.
### Recovery
Configure automated liveness checks, external diagnostic heartbeat pings, and automatic restart behavior. See [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery).
For detailed information about health monitoring and recovery, check out the [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery) documentation.
### Advanced
Set custom process environment variables and Windows service dependencies. See [Environment Variables](https://github.com/aelassas/servy/wiki/Environment-Variables) and [Service Dependencies](https://github.com/aelassas/servy/wiki/Service-Dependencies).
For detailed information about environment variables, check out the [Environment Variables](https://github.com/aelassas/servy/wiki/Environment-Variables) documentation.
For detailed information about service dependencies, check out the [Service Dependencies](https://github.com/aelassas/servy/wiki/Service-Dependencies) documentation.
### Log On
Configure service execution accounts including `LocalSystem`, built-in service accounts, domain accounts, and gMSAs. See [Security](https://github.com/aelassas/servy/wiki/Security).
You can also run the service under:
* `NT AUTHORITY\NetworkService`
* `NT AUTHORITY\LocalService`
* Passwordless accounts
### Pre-Launch
Execute custom initialization scripts or executables before the main service process starts. See [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions).
For detailed information about the pre-launch hook, check out the [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions) documentation.
### Post-Launch
Run auxiliary tasks immediately after the primary process launches successfully. See [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions).
For detailed information about the post-launch hook, check out the [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions) documentation.
### Pre-Stop
Run graceful shutdown tasks or resource draining scripts prior to stopping the main service. See [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions).
For detailed information about the pre-stop hook, check out the [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions) documentation.
### Post-Stop
Execute post-cleanup tasks after the primary service and all child processes have completely exited. See [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions).
For detailed information about the post-stop hook, check out the [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions) documentation.
## Manager App
[Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager) is a central administrative interface to manage, monitor, and inspect all services installed across the system.
### Services
View all registered services, inspect real-time CPU/RAM metrics, and trigger lifecycle actions. See [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager#services).
### Performance
Track real-time CPU and memory utilization using live visual performance graphs. See [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager#performance).
### Console
Stream unified `stdout` and `stderr` console output in real time with live filtering and tailing controls. See [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager#console).
### Dependencies
Inspect full Windows Service Control Manager (SCM) dependency trees with status color coding. See [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager#dependencies).
### Logs
Inspect Windows Event Log entries directly within the application viewer. See [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager#logs).
## CLI / PowerShell
Servy includes a command-line interface (`servy-cli`) and a [PowerShell module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module) for full automation and CI/CD integration.
### CLI
Execute service operations directly via command-line arguments or configuration files. See [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI).
### PowerShell
Manage services natively using PowerShell cmdlets and pipeline objects. See [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module).
## See Also
* [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide)
* [Usage](https://github.com/aelassas/servy/wiki/Usage)
* [Servy Desktop App](https://github.com/aelassas/servy/wiki/Servy-Desktop-App)
* [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager)
* [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI)
* [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
* [Examples & Recipes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes)
* [Export/Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services)
* [Service Event Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications)
---
# Document: Installation Guide
> Source: https://github.com/aelassas/servy/wiki/Installation-Guide
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Installation-Guide#introduction)
1. [System Requirements](https://github.com/aelassas/servy/wiki/Installation-Guide#system-requirements)
1. [Version Comparison](https://github.com/aelassas/servy/wiki/Installation-Guide#version-comparison)
1. [Installation Options](https://github.com/aelassas/servy/wiki/Installation-Guide#installation-options)
1. [Quick Install](https://github.com/aelassas/servy/wiki/Installation-Guide#quick-install)
1. [Add Servy to PATH](https://github.com/aelassas/servy/wiki/Installation-Guide#add-servy-to-path)
1. [Manual Installation](https://github.com/aelassas/servy/wiki/Installation-Guide#manual-installation)
1. [Silent Install/Uninstall](https://github.com/aelassas/servy/wiki/Installation-Guide#silent-installuninstall)
1. [Silent Install](https://github.com/aelassas/servy/wiki/Installation-Guide#silent-install)
1. [Custom Silent Install](https://github.com/aelassas/servy/wiki/Installation-Guide#custom-silent-install)
1. [Custom Installation Directory](https://github.com/aelassas/servy/wiki/Installation-Guide#custom-installation-directory)
1. [Silent Uninstall](https://github.com/aelassas/servy/wiki/Installation-Guide#silent-uninstall)
1. [Switches Explained](https://github.com/aelassas/servy/wiki/Installation-Guide#switches-explained)
1. [Security & Antivirus Setup](https://github.com/aelassas/servy/wiki/Installation-Guide#security--antivirus-setup)
## Introduction
This guide covers the requirements and various methods for installing Servy on Windows systems.
Servy is provided in two distinct builds to ensure compatibility across modern and legacy Windows environments.
* **Modern Build (.NET 10.0+):** Best for Windows 10, 11, and modern Windows Server.
* **Legacy Build (.NET Framework 4.8):** Best for Windows 7, 8, and older Server editions.
## System Requirements
> [!CAUTION]
> **Administrator privileges are required** to install Servy and to manage Windows services.
### Version Comparison
| Feature | .NET 10.0+ Version (x64) | .NET 10.0+ Version (ARM64) | .NET Framework 4.8 Version (Legacy x64) |
| --- | --- | --- | --- |
| **Filename** | `servy-x.x-x64-installer.exe` | `servy-x.x-arm64-installer.exe` | `servy-x.x-net48-x64-installer.exe` |
| **OS Support** | Windows 10 (1809+), 11, Server 2016+ | Windows 11 on ARM, Windows on ARM | Windows 7 SP1, 8.x, Server 2008 R2+ |
| **Dependencies** | None (Self-contained) | None (Self-contained) | [.NET Framework 4.8](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net48) |
| **Performance** | Optimized (Modern Runtime) | Native ARM64 (Optimized Runtime) | Standard (Legacy Runtime) |
**Note on Legacy OS:** While the .NET 10.0 build may run on Windows 7, it is **unsupported**. For Windows 7 SP1 or Windows Server 2008 R2, you must use the **.NET Framework 4.8** build to ensure stability.
## Installation Options
You have two paths to install Servy: use a package manager if your environment supports it, or [download the installer](https://github.com/aelassas/servy/releases/latest) and install it manually when you need full control over the setup process.
### Quick Install
**WinGet**
```powershell
winget install servy
```
**Chocolatey**
```powershell
choco install -y servy
```
**Scoop**
```powershell
scoop bucket add extras
scoop install servy
```
**Patch My PC**
Servy is available in the official [Patch My PC catalog](https://patchmypc.com/supported-products/) for enterprise automated deployment and updates via Microsoft Intune and ConfigMgr (SCCM).
> [!NOTE]
> **Legacy OS Support (Windows 7 SP1 / 8.x / Server 2008 R2):** Package managers carry the self-contained modern build. For older platforms, download `servy-x.x-net48-x64-installer.exe` or `servy-x.x-net48-x64-portable.7z` directly from [GitHub Releases](https://github.com/aelassas/servy/releases/latest) (requires .NET Framework 4.8).
### Add Servy to PATH
The installer's **Add Servy to PATH** option (Additional Options page, enabled by default, requires the CLI component) adds the Servy directory to the system **PATH**, so `servy-cli` runs from any elevated Command Prompt or PowerShell session. Clear it to leave `PATH` untouched; uninstalling removes the entry only if the installer added it. The portable package makes no `PATH` change - add its directory yourself, or use Scoop, which puts `servy-cli` on `PATH` through its own shim.
### Manual Installation
1. [Download the latest release](https://github.com/aelassas/servy/releases/latest).
2. Run the installer (you will be prompted for admin rights).
3. Launch Servy from the Start Menu or desktop shortcut.
### Silent Install/Uninstall
#### Silent Install
You can install Servy silently (without any UI) from an administrator command prompt using the following commands:
```powershell
# For .NET 10.0+ (x64)
.\servy--x64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
# For .NET 10.0+ (ARM64)
.\servy--arm64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
# For .NET Framework 4.8
.\servy--net48-x64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
```
Using PowerShell:
```powershell
# For .NET 10.0+ (x64)
Start-Process -FilePath ".\servy--x64-installer.exe" -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /CLOSEAPPLICATIONS /NOCANCEL' -Verb RunAs -Wait
# For .NET 10.0+ (ARM64)
Start-Process -FilePath ".\servy--arm64-installer.exe" -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /CLOSEAPPLICATIONS /NOCANCEL' -Verb RunAs -Wait
# For .NET Framework 4.8
Start-Process -FilePath ".\servy--net48-x64-installer.exe" -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /CLOSEAPPLICATIONS /NOCANCEL' -Verb RunAs -Wait
# Refresh PATH in current session
$env:Path = (((
[Environment]::GetEnvironmentVariable('Path', 'Machine'),
[Environment]::GetEnvironmentVariable('Path', 'User')
) -join ';').Split(';', [StringSplitOptions]::RemoveEmptyEntries) |
Select-Object -Unique) -join ';'
```
You need to wait a little while for Servy to finish installing.
If you face any issues, enable logging by adding the following option:
```text
/LOG="servy-install.log"
```
#### Custom Silent Install
To install the CLI only, use these options:
```text
/SetupType=custom /Components=install_cli
```
To install the desktop app only, use these options:
```text
/SetupType=custom /Components=install_main_app
```
To install the manager app only, use these options:
```text
/SetupType=custom /Components=install_manager
```
To install the desktop app and CLI only, use these options:
```text
/SetupType=custom /Components=install_main_app,install_cli
```
To install the manager app and CLI only, use these options:
```text
/SetupType=custom /Components=install_manager,install_cli
```
#### Custom Installation Directory
To specify a custom installation directory, use the `/DIR` parameter from the command line:
```powershell
.\servy--x64-installer.exe /VERYSILENT /DIR="C:\Your\Custom\Path" /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
```
Using a custom path will not allow you to bypass the need for administrative rights. The installer executable is compiled with `PrivilegesRequired=admin`, which causes Windows to automatically trigger a UAC prompt and require elevated credentials before execution starts. Beyond writing files, the setup routine performs system-level configuration such as updating `HKLM` registry keys and setting strict ACLs on the `%ProgramData%\Servy` directory via `icacls.exe`.
If you also want to prevent the installer from modifying the system `PATH` environment variable, you can explicitly deselect the `addpath` task using `/MERGETASKS="!addpath"`. If you want to skip all optional tasks entirely, including both the `PATH` update and desktop shortcuts, you can pass an empty task list using `/TASKS=""`.
If you do not have administrative access on the target machine, you will not be able to use the `.exe` installer at all. In that scenario, you should use the portable package instead (`servy--x64-portable.7z`). The portable archive can be extracted and executed directly from any folder where your user account has standard write access, such as `%LOCALAPPDATA%` or a user folder, without requiring elevation or installer scripts.
#### Silent Uninstall
You can uninstall Servy silently (without any UI) from an administrator command prompt using the following command (PowerShell):
```powershell
# Default location; adjust the path if Servy was installed to a custom directory
& "$env:ProgramFiles\Servy\unins000.exe" /VERYSILENT /NORESTART /SUPPRESSMSGBOXES
```
#### Switches Explained
| Switch | Description |
|--------|-------------|
| `/VERYSILENT` | Installs completely silently with no wizard or progress windows. |
| `/NORESTART` | Prevents automatic system restart after installation. |
| `/SUPPRESSMSGBOXES` | Suppresses all message boxes, including errors, during installation. |
| `/SP-` | Disables the "This will install..." startup prompt. |
| `/CLOSEAPPLICATIONS` | Asks running apps to close if they block files. |
| `/NOCANCEL` | Prevents the user from canceling the installation. |
## Security & Antivirus Setup
Servy's executables and installers are digitally signed by SignPath (see [Security](https://github.com/aelassas/servy/wiki/Security#supply-chain-and-trust)). It performs only standard installation tasks and does not contain malware, adware, or unwanted software. Release binaries are scanned on VirusTotal, and false-positive reports are submitted to Microsoft Security Intelligence and affected AV vendors. Servy is published in the Windows Package Manager (WinGet), Chocolatey, Scoop, and Patch My PC enterprise catalog. Before running a downloaded installer, you can check its signature under **Properties > Digital Signatures**.
To ensure smooth operation of the Servy Windows services, it is recommended to add the following folders to your Microsoft Defender or third-party antivirus exclusion list:
```text
%ProgramData%\Servy
%ProgramFiles%\Servy
```
This prevents antivirus software from interfering with the execution of service binaries managed by Servy.
Starting with v9.1, Servy uses a hybrid single-file execution approach. Managed C# assemblies (like `Servy.CLI.dll`, `Servy.dll`, `Servy.Manager.dll`, `Servy.Service.dll`, and `Servy.Restarter.dll`) are **no longer extracted** to `%TEMP%\.net`. Instead, they are loaded directly into memory from the main executables (`servy-cli.exe`, `Servy.Manager.exe`, etc.), **which are all fully Authenticode signed**.
Servy **only** extracts unmanaged native C/C++ binaries to `%TEMP%` (which Windows `LoadLibrary` requires). This prevents security software from intercepting and flagging dynamically extracted DLLs.
---
# Document: Quick Start / Usage
> Source: https://github.com/aelassas/servy/wiki/Usage
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Usage#introduction)
1. [Quick Start](https://github.com/aelassas/servy/wiki/Usage#quick-start)
1. [Service Configuration](https://github.com/aelassas/servy/wiki/Usage#service-configuration)
1. [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Usage#logging--log-rotation)
1. [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Usage#health-monitoring--recovery)
1. [Advanced Configuration](https://github.com/aelassas/servy/wiki/Usage#advanced-configuration)
1. [Logon & Security](https://github.com/aelassas/servy/wiki/Usage#logon--security)
1. [Launch & Stop Hooks](https://github.com/aelassas/servy/wiki/Usage#launch--stop-hooks)
1. [Managing Services](https://github.com/aelassas/servy/wiki/Usage#managing-services)
1. [See Also](https://github.com/aelassas/servy/wiki/Usage#see-also)
## Introduction
This page provides a comprehensive guide on how to configure, install, and manage services using Servy. Whether you are using the Servy (GUI), CLI, or PowerShell, the underlying principles remain the same.
Servy is a service wrapper that transforms any executable, script, or runtime (Node.js, Python, Java, etc.) into a managed Windows Service. It provides enterprise-grade features like automatic recovery, log rotation, CPU and RAM monitoring, and lifecycle hooks that native Windows services lack.
## Quick Start
> [!IMPORTANT]
> Before installing a service, make sure you are running with administrator rights and that the target account has the required access to `%ProgramData%\Servy` and any application folders it needs to use.
1. **Launch** `Servy.exe` (Servy GUI).
2. **Name your service**: Enter a unique service name in the **Service Name** field.
3. **Select your executable**: Provide the full path to your binary in **Process Path**.
4. **Configure optional settings**: Set **Process Parameters**, **Startup Directory**, or **Description** as needed.
5. **Install**: Click the **Install** button to register it with the Windows Service Control Manager (SCM).
6. **Start**: Click **Start** to bring your service online immediately.
## Service Configuration
### Primary Settings
* **Service Name (required)**: The unique service name for Windows.
* **Display Name**: The human-readable name shown in `services.msc`.
* **Description**: A brief summary of the service's purpose.
* **Process Path (required)**: Full path to the executable (e.g., `C:\Program Files\nodejs\node.exe`). Supports environment variable expansion.
* **Startup Directory**: The working directory for the process. Defaults to the executable's folder.
* **Process Parameters**: Command-line arguments passed to the executable.
* **Startup Type**: Choose between `Automatic (default)`, `Automatic (delayed start)`, `Manual`, or `Disabled`.
* **Process Priority**: Choose between `Idle`, `Below Normal`, `Normal (default)`, `Above Normal`, `High`, or `Real Time (use with caution)`.
* **CPU Affinity**: Logical CPUs the process may run on (e.g., `0-3,8` or `0xFF00`). See this [FAQ](https://github.com/aelassas/servy/wiki/FAQ#how-and-why-should-i-use-cpu-affinity-with-servy) for further details.
* **Timeouts**:
* **Start Timeout**: Seconds to wait for a successful start (default: `10s`).
* **Stop Timeout**: Seconds to wait for the process to exit gracefully before killing it (default: `5s`).
* **Enable Console UI**: For interactive console apps; disables stdout/stderr redirection.
## Logging & Log Rotation
Servy captures `stdout` (standard output) and `stderr` (error output) and writes them to files.
* **Size-based Rotation**: Automatically rolls logs when they reach a specific size (e.g., 10MB).
* **Date-based Rotation**: Rolls logs on a `Daily`, `Weekly`, or `Monthly` interval.
* **Retention**: Set **Max Rotations** to limit the number of old log files kept on disk.
For advanced logging configurations, see [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation).
## Health Monitoring & Recovery
Servy can monitor your application's health and automatically take action if it fails.
* **Heartbeat**: Servy checks if the process is still alive at defined intervals.
* **Recovery Actions**: Choose **Restart Service** (default), **Restart Process**, **Restart Computer**, or **None** upon failure.
* **Heartbeat URL**: An absolute HTTP/HTTPS URL (e.g., `https://hc-ping.com/uuid`) used for sending out-of-band diagnostic heartbeat pings to external monitoring services (such as [healthchecks.io](https://healthchecks.io/) or Uptime Kuma).
* **Failure Program**: Specify an external script or app to run specifically when a failure is detected.
For detailed setup, see [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery).
## Advanced Configuration
### Environment Variables
Define process-specific environment variables without polluting the system-wide environment.
* **Format**: `KEY=VALUE` (one per line or separated by `;`).
* **Expansion**: Supports referencing other variables, e.g., `PATH=%PATH%;C:\CustomBin`.
* **Escaping**: Use `\;` for literal semicolons and `\=` for literal equals.
See [Environment Variables](https://github.com/aelassas/servy/wiki/Environment-Variables) for the full resolution order.
### Service Dependencies
Ensure your service only starts after other required services (like `MSSQLSERVER` or `Docker`) are ready. Use the internal **Service Name**, not the Display Name.
See [Service Dependencies](https://github.com/aelassas/servy/wiki/Service-Dependencies).
## Logon & Security
Servy allows you to configure the service identity, supporting:
* **Local Accounts**: `.\username`
* **Domain Accounts**: `DOMAIN\username`
* **Managed Service Accounts**: `DOMAIN\gMSA$`
**Security Note:** Stored passwords are encrypted using **AES-256**. For technical details on how Servy handles credentials, see the [Security](https://github.com/aelassas/servy/wiki/Security) model.
## Launch & Stop Hooks
Servy provides four distinct hook points to manage your service lifecycle:
| Hook | Typical Use Case |
| :--- | :--- |
| **Pre-Launch** | Fetching secrets, generating config files. |
| **Post-Launch** | Sending "Started" notifications, initializing DB migrations. |
| **Pre-Stop** | Graceful drain of connections, flushing buffers. |
| **Post-Stop** | Temporary file cleanup, post-mortem logging. |
* Detailed Startup Guide: [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions)
* Detailed Shutdown Guide: [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions)
## Managing Services
* **Servy Manager**: Use the GUI for real-time status monitoring, log viewing, and configuration updates. See [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager).
* **Servy CLI**: Ideal for automation and remote management. See [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI).
* **PowerShell**: Use the native module for CI/CD integration. See [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module).
* **Portability**: Use the **Export/Import** feature to move service configurations between servers. See [Export/Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services).
**Next Step:** Ready to deploy? Check out the [Examples & Recipes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes) for pre-configured setups for Node.js, Python, and Java applications.
## See Also
* [Servy Desktop App](https://github.com/aelassas/servy/wiki/Servy-Desktop-App)
* [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager)
* [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI)
* [PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
* [Examples & Recipes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes)
* [Shutdown & Teardown](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown)
* [Service Event Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications)
* [Comparison with Alternatives](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives)
* [Troubleshooting](https://github.com/aelassas/servy/wiki/Troubleshooting)
* [FAQ](https://github.com/aelassas/servy/wiki/FAQ)
---
# Document: Servy Desktop App
> Source: https://github.com/aelassas/servy/wiki/Servy-Desktop-App
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#introduction)
1. [Overview](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#overview)
1. [Service Details](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#service-details)
1. [Logging](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#logging)
1. [Recovery](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#recovery)
1. [Advanced](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#advanced)
1. [Log On](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#log-on)
1. [Pre-Launch](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#pre-launch)
1. [Post-Launch](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#post-launch)
1. [Pre-Stop](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#pre-stop)
1. [Post-Stop](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#post-stop)
1. [Features](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#features)
1. [Availability](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#availability)
1. [Usage](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#usage)
1. [See Also](https://github.com/aelassas/servy/wiki/Servy-Desktop-App#see-also)
> [!IMPORTANT]
> **Console UI Compatibility**
> If your app tries to visually update the command prompt (like clearing the screen or moving the cursor), it will crash when running as a background service since services don't have visible windows. To fix this without changing your code, enable the **Enable Console UI** option in the service configuration while installing your service.
## Introduction
Servy is a Windows service wrapper that allows you to transform any executable, script, or batch file into a background service. It bridges the gap between simple console applications and the enterprise-grade management expected from native Windows services.
Servy provides an intuitive interface to run any app as a native Windows service with configuration and management options. By using Servy, you can ensure that your applications start automatically when the system boots, restart following unexpected crashes, and maintain detailed logs without modifying a single line of your application code.
Whether you are hosting a web server, a background worker, or a database, the Servy Desktop App simplifies the process of deployment and maintenance through a clean, tabbed interface.
> [!NOTE]
> If you get a blank screen on remote management tools (like MeshCentral, TeamViewer, or AnyDesk), run the desktop app from an admin command prompt with the following command: `Servy.exe --force-sr`
## Overview
The following sections illustrate the key components of the Desktop App, showing how you can customize every aspect of your service lifecycle.
### Service Details
This tab lets you configure the main service properties such as name, description, executable path, arguments, startup directory, and startup type.
For more details on CPU affinity, see the [FAQ](https://github.com/aelassas/servy/wiki/FAQ#how-and-why-should-i-use-cpu-affinity-with-servy).
### Logging
This tab lets you configure `stdout` and `stderr` logging, including size-based log rotation, date-based log rotation, the maximum number of rotated log files to retain, and additional options such as enabling debug logs.
> [!NOTE]
> Enabling the debug option will record sensitive information in the local log file at `%ProgramData%\Servy\logs\services\\Servy.Service.log`. This behavior only occurs when local logging is enabled, which is the default setting. Sensitive data is never recorded in the Windows Event Log. The CLI `show` command and `Show-ServyService` mask the encrypted fields by default; `--decrypt` / `-Decrypt` prints the parameters and environment variables in clear text (the stored password always stays masked). See [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI) and [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module).
For detailed information about logging, check out the [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation) documentation.
### Recovery
This tab lets you configure automated health-monitoring and failure-handling behavior. It enables continuous internal liveness checks, optional out-of-band diagnostic pings to external monitoring platforms (e.g., [healthchecks.io](https://healthchecks.io) or [Uptime Kuma](https://github.com/louislam/uptime-kuma) Push Monitors), and customizable recovery actions to keep your application running reliably without manual intervention.
For detailed information about health monitoring and recovery, check out the [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery) documentation.
### Advanced
The advanced tab provides additional configuration options such as environment variables and service dependencies.
For detailed information about environment variables, check out the [Environment Variables](https://github.com/aelassas/servy/wiki/Environment-Variables) documentation.
For detailed information about service dependencies, check out the [Service Dependencies](https://github.com/aelassas/servy/wiki/Service-Dependencies) documentation.
### Log On
This tab allows you to configure the service account, including support for local accounts, domain accounts, and gMSA accounts.
Servy securely encrypts stored passwords using AES. For more details, see the [Security](https://github.com/aelassas/servy/wiki/Security) page.
You can also run the service under:
- `NT AUTHORITY\NetworkService`
- `NT AUTHORITY\LocalService`
- Passwordless accounts
### Pre-Launch
Configure an optional pre-launch program that runs before the main service process starts. This can be used to prepare the environment, set up dependencies, or run initialization scripts. By default, the pre-launch hook runs synchronously with a timeout. If the pre-launch script exits with a non-zero exit code or times out, the service will fail to start unless the **Ignore Failure** option is enabled.
Set the timeout to 0 to run the pre-launch hook in fire-and-forget mode. When set to 0, the hook is started and the service is launched immediately without waiting for completion. Use this only for tasks that do not affect the service's ability to start or run correctly. `stdout`/`stderr` redirection and retries are not available in fire-and-forget mode.
Orphaned fire-and-forget pre-launch hooks are cleaned up when the service stops.
For detailed information about the pre-launch hook, check out the [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions) documentation.
### Post-Launch
Configure an optional post-launch program that runs after the process starts successfully.
Orphaned post-launch hooks are cleaned up when the service stops.
For detailed information about the post-launch hook, check out the [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions) documentation.
### Pre-Stop
Configure an optional script or executable to run before the main service stops. This can be used for graceful shutdown tasks such as notifying external systems or draining resources. The pre-stop process runs synchronously and extends the service stop timeout while it is running. Set the timeout to 0 to run the pre-stop process in fire-and-forget mode.
For detailed information about the pre-stop hook, check out the [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions) documentation.
### Post-Stop
Configure an optional script or executable to run after the wrapped process and all of its child processes have exited. The post-stop process is started in fire-and-forget mode and does not block service shutdown.
For detailed information about the post-stop hook, check out the [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions) documentation.
## Features
- **Run Anything:** Wrap executables, scripts, or batch files as native Windows services.
- **Smart Recovery:** Automatically restart services or the entire host machine based on custom health monitoring rules.
- **Flexible Lifecycle Hooks:** Execute custom code at every stage (pre-launch, post-launch, pre-stop, and post-stop).
- **Log Rotation:** Rotate logs by size or date, with real-time tailing in the Manager console.
- **Secure Credentials:** Encrypted storage of service account passwords using industry-standard AES.
- **Real-time Monitoring:** Integrated CPU and RAM performance tracking.
- **Dependency Mapping:** Visual dependency trees to troubleshoot startup sequences.
## Availability
Servy Desktop App is distributed in multiple editions to cover modern and older systems:
- **.NET 10.0+ (Recommended)**
- Self-contained installer
- Does not require any .NET runtime to be pre-installed
- **.NET Framework 4.8 (For older systems)**
- Standard installer package
- Requires .NET Framework 4.8 Runtime
## Usage
1. **Launch:** Run the Servy Desktop App (`Servy.exe`).
1. **Configure:** Enter your application path and service details in the "Service Details" (Main) tab.
1. **Options:** Customize your environment variables, logging preferences, and recovery actions in the respective tabs.
1. **Install:** Click the "Install" button to register the service with the Windows Service Control Manager (SCM).
1. **Manage:** Use Servy Manager to monitor the performance of your new service and browse logs.
## See Also
- [Overview](https://github.com/aelassas/servy/wiki/Overview)
- [Usage](https://github.com/aelassas/servy/wiki/Usage)
- [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager)
- [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI)
- [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
---
# Document: Servy Manager
> Source: https://github.com/aelassas/servy/wiki/Servy-Manager
## Table of Contents
1. [Overview](https://github.com/aelassas/servy/wiki/Servy-Manager#overview)
1. [Services](https://github.com/aelassas/servy/wiki/Servy-Manager#services)
1. [Performance](https://github.com/aelassas/servy/wiki/Servy-Manager#performance)
1. [Console](https://github.com/aelassas/servy/wiki/Servy-Manager#console)
1. [Dependencies](https://github.com/aelassas/servy/wiki/Servy-Manager#dependencies)
1. [Logs](https://github.com/aelassas/servy/wiki/Servy-Manager#logs)
1. [Features](https://github.com/aelassas/servy/wiki/Servy-Manager#features)
1. [Availability](https://github.com/aelassas/servy/wiki/Servy-Manager#availability)
1. [Usage](https://github.com/aelassas/servy/wiki/Servy-Manager#usage)
1. [Keyboard Shortcuts](https://github.com/aelassas/servy/wiki/Servy-Manager#keyboard-shortcuts)
1. [See Also](https://github.com/aelassas/servy/wiki/Servy-Manager#see-also)
> [!NOTE]
> If you get a blank screen on remote management tools (like MeshCentral, TeamViewer, or AnyDesk), run the Manager app from an admin command prompt with the following command: `Servy.Manager.exe --force-sr`
## Overview
Servy Manager is a central administrative application for managing and monitoring services controlled by Servy. It provides real-time visibility into process health, CPU and memory utilization, live stream console output, dependency trees, and system log files; all interacting directly with Servy's local database and runtime state.
### Services
The Manager provides a central place to view all installed services, their status, CPU & RAM usage in real time, and quick actions (start, stop, restart, install, uninstall, remove, edit, copy PID).
### Performance
Monitor CPU & RAM usage in real time from the Performance tab with live graphs.
### Console
Monitor service `stdout` and `stderr` output in real time from a single, unified console.
The Console view automatically loads recent log history, continues tailing live output, and keeps both streams ordered by timestamp for accurate troubleshooting.
You can filter logs instantly, pause updates while selecting or copying text, and resume live streaming without losing context.
### Dependencies
The Dependencies tab provides a visual representation of a service dependency tree retrieved from the Service Control Manager (SCM). Each dependency is displayed with its current status, where running services are shown in green, stopped services in red, and cycles in orange. The tree can be refreshed at any time using the Refresh button or by pressing **F5**.
This view is especially useful for understanding startup and shutdown order, diagnosing why a service fails to start, and quickly identifying stopped or missing dependencies that may impact service availability.
### Logs
Servy writes logs to both the Windows Event Log and log files in `%ProgramData%\Servy\logs`, and its built-in log viewer lets you inspect them in real time directly from the GUI.
## Features
- **Service listing:** View all services installed or imported in Servy.
- **Service control:** Start, stop, and restart services.
- **Service installation:** Install services from configuration files, uninstall/remove services.
- **Export:** Save service configuration in XML or JSON.
- **Import:** Import configurations into Servy's database without immediate installation - install them later from the UI.
- **Configuration editor:** Open and edit service configurations.
- **Search:** Quickly find a specific service.
- **Monitor:** Track real-time CPU and RAM usage for services.
- **Live log tailing:** Stream standard output (`stdout`) and standard error (`stderr`) logs in real time.
- **Dependency tree:** Preview and inspect service dependencies.
- **Logs:** Quickly search logs by log level, date, and keyword.
## Availability
Servy Manager is distributed in multiple editions to cover modern and older systems:
- **.NET 10.0+ (Recommended)**
- Self-contained installer
- Does not require any .NET runtime to be pre-installed
- **.NET Framework 4.8 (For older systems)**
- Standard installer package
- Requires .NET Framework 4.8 Runtime
## Usage
1. Launch Servy Manager (`Servy.Manager.exe`).
1. Browse the list of services already installed or imported.
1. Use the buttons on each row to start, stop or restart a service.
1. Use the row's `⋮` menu to:
- Install, uninstall or remove a service
- Open/edit its configuration
- Export its configuration to XML/JSON
- Copy its PID
1. To add a service, use the **Import** menu (XML or JSON) to import its configuration into the database, then install it from the row's `⋮` menu, or use the Servy Desktop App for interactive setup.
1. Select a service to inspect its detailed management tabs:
- **Performance:** View real-time CPU and RAM usage graphs.
- **Console:** Stream live `stdout` and `stderr` logs in real time.
- **Dependencies:** Inspect service dependency trees and relationships.
- **Logs:** Search and filter Servy event log entries and diagnostic history.
## Keyboard Shortcuts
| Shortcut | Where | What it does |
| --- | --- | --- |
| `F5` | Services | Refreshes the full service list. Statuses also refresh automatically. |
| `F5` | Logs | Re-runs the current log search. |
| `F5` | Dependencies | Refreshes the dependency tree. |
| `Ctrl` + `A` | Services | Selects every service in the list. Works even when the search box has focus, where it replaces the usual select-all-text behaviour. |
`F5` has no effect on the Console tab, which streams continuously.
## See Also
- [Overview](https://github.com/aelassas/servy/wiki/Overview)
- [Usage](https://github.com/aelassas/servy/wiki/Usage)
- [Servy Desktop App](https://github.com/aelassas/servy/wiki/Servy-Desktop-App)
- [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI)
- [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
---
# Document: Servy CLI
> Source: https://github.com/aelassas/servy/wiki/Servy-CLI
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Servy-CLI#introduction)
1. [Basic Usage](https://github.com/aelassas/servy/wiki/Servy-CLI#basic-usage)
1. [Command Help](https://github.com/aelassas/servy/wiki/Servy-CLI#command-help)
1. [Install Command](https://github.com/aelassas/servy/wiki/Servy-CLI#install-command)
1. [Show Command](https://github.com/aelassas/servy/wiki/Servy-CLI#show-command)
1. [Additional Commands](https://github.com/aelassas/servy/wiki/Servy-CLI#additional-commands)
1. [Tips](https://github.com/aelassas/servy/wiki/Servy-CLI#tips)
1. [See Also](https://github.com/aelassas/servy/wiki/Servy-CLI#see-also)
> [!IMPORTANT]
> **Console UI Compatibility**
> If your app tries to visually update the command prompt (like clearing the screen or moving the cursor), it will crash when running as a background service since services don't have visible windows. To fix this without changing your code, enable the `--enableConsoleUI` option in the service configuration while installing your service.
## Introduction
Servy includes a command-line interface (CLI) designed for full scripting, automated deployments, and seamless integration into CI/CD pipelines.
The CLI offers a lightweight, script-friendly alternative to the desktop app, focusing on automation and headless use cases while leveraging the same core service management logic as the desktop application.
After a default installation `servy-cli` is on the system **PATH** - see [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide#add-servy-to-path) for the option that controls this and for the portable package.
> [!NOTE]
> In servy-cli, the equals sign (`=`) is not supported when using single-character shortcuts (like `-n` or `-p`).
## Basic Usage
To get started, open an elevated Command Prompt or PowerShell window and run:
```text
PS> servy-cli help
Servy.CLI +
Copyright © 2026 Akram El Assas. All rights reserved.
install Install a Windows service.
uninstall Uninstall a Windows service.
start Start a Windows service.
stop Stop a Windows service.
status Get the current status of a Windows service. Possible results:
NotInstalled, Stopped, StartPending, StopPending, Running,
ContinuePending, PausePending, Paused, Unknown.
restart Restart a Windows service.
export Export a Servy Windows service configuration to a configuration
file.
import Import a Windows service configuration into the Servy database and
optionally install the service.
show Show a Servy Windows service configuration in a human-readable
form, or list all services when no name is given.
help Display more information on a specific command.
version Display version information.
```
## Command Help
For detailed help on any command, append `--help` after the command name. For example, to get help for the `start` command:
```text
PS> servy-cli start --help
Servy.CLI +
Copyright © 2026 Akram El Assas. All rights reserved.
-n, --name Required. Name of the service to start.
-q, --quiet Suppress spinner and run in non-interactive mode.
--help Display this help screen.
--version Display version information.
```
## Install Command
The main command for installing or updating a Windows service is `install`.
**Quick Example**
The following command installs `MyApp.exe` as a Windows service named `MyService`:
```cmd
servy-cli install --name="MyService" --path="C:\path\to\MyApp.exe"
```
**Detailed Usage**
Here is its detailed usage:
```text
PS> servy-cli install --help
Servy.CLI +
Copyright © 2026 Akram El Assas. All rights reserved.
-n, --name Required. Unique service name to install.
--displayName The human-readable name shown in the Windows
Services console (services.msc). If left empty, the
service name will be used instead.
-d, --description Description of the service.
-p, --path Required. Path to the executable process. Supports
environment variable expansion, example:
%JAVA_HOME%\bin\java.exe
--startupDir Startup directory for the process. Supports
environment variable expansion, example:
%PROGRAMDATA%\MyApp
--params Additional parameters for the process. Supports
environment variable expansion, example:
--params="--data %ProgramData%\MyApp --bin
%MY_VAR%\bin". SECURITY WARNING: Use the
SERVY_PROCESS_PARAMETERS environment variable
instead to avoid exposing sensitive parameters in OS
process listings.
--startupType Service startup type. Options: Automatic,
AutomaticDelayedStart, Manual, Disabled. Defaults to
Automatic.
--priority Process priority level. Options: Idle, BelowNormal,
Normal, AboveNormal, High, RealTime. Defaults to
Normal.
-a, --cpuAffinity Logical CPUs the process may run on (e.g., '0-3,8'
or '0xFF00').
--startTimeout Timeout in seconds to wait for the process to start
successfully before considering the startup as
failed. Must be between 1 and 86400 seconds.
Defaults to 10 seconds.
--stopTimeout Timeout in seconds to wait for the process to exit.
Must be between 1 and 86400 seconds. Defaults to 5
seconds.
--enableConsoleUI Enable console user interface for the service. When
enabled, stdout/stderr redirection is disabled.
--stdout Path to stdout log file.
--stderr Path to stderr log file.
--enableRotation Deprecated. Enable size-based log rotation. This
option is kept only for backward compatibility. Use
--enableSizeRotation instead.
--enableSizeRotation Enable size-based log rotation.
--rotationSize Log rotation size in Megabytes (MB). Must be between
1 and 10240 MB.
--enableDateRotation Enable date-based log rotation based on the date
interval specified by --dateRotationType. When both
size-based and date-based rotation are enabled, size
rotation takes precedence.
--dateRotationType Date rotation type. Options: Daily, Weekly, Monthly,
None (None disables date-based rotation; use when
only size rotation is desired).
--maxRotations Maximum rotated log files to keep. Must be between 0
and 10000. Set to 0 or leave empty for unlimited.
--useLocalTimeForRotation Use local server time for log rotation instead of
UTC. Default is false.
--debug Whether debug logs are enabled. When enabled,
environment variables and process parameters are
recorded in the Servy.Service.log file. Not
recommended for production environments, as these
logs may contain sensitive information.
--enableHealth Enable health monitoring.
--heartbeatInterval Heartbeat interval in seconds. Must be between 5 and
86400 seconds. Only used when health monitoring is
enabled.
--maxFailedChecks Maximum allowed failed health checks. Must be
between 1 and 100000. Only used when health
monitoring is enabled.
--recoveryAction Recovery action on failure. Options: None,
RestartService, RestartProcess, RestartComputer.
RestartService and RestartComputer actions are not
available if the service runs under NT
AUTHORITY\LocalService, NT AUTHORITY\NetworkService,
an IIS AppPool identity (IIS APPPOOL\...), or a user
account without the required privileges. Only used
when health monitoring is enabled.
--recoveryOnCleanExit Enable running recovery action even if the process
exits successfully. Default is false. Only used when
health monitoring is enabled.
--maxRestartAttempts Maximum restart attempts on failure. Must be between
0 and 100000. Set to 0 for unlimited restart
attempts. Only used when health monitoring is
enabled.
--heartbeatUrl Absolute URL for out-of-band diagnostic heartbeat
pings. Only used when health monitoring is enabled.
SECURITY WARNING: Use the SERVY_HEARTBEAT_URL
environment variable instead to avoid exposing
sensitive parameters in OS process listings.
--heartbeatUrlTimeoutSeconds Timeout in seconds for external heartbeat URL
requests. Must be between 2 and 30 seconds. Defaults
to 10 seconds. Only used when health monitoring is
enabled.
--enableHeartbeatUrlFlags Append /start and /fail to the heartbeat URL on
service start and failure. Only used when health
monitoring is enabled.
--failureProgramPath The failure program path. Configure a script or
executable to run when the wrapped process exits
with a non-zero exit code (recovery disabled) or
after all recovery action retries have failed
(recovery enabled). It is not run when the process
fails to start; that path stops the service.
Supports environment variable expansion, example:
%JAVA_HOME%\bin\java.exe
--failureProgramStartupDir Specifies the directory in which the failure program
will start. If not set, defaults to the service
working directory. Supports environment variable
expansion, example: %PROGRAMDATA%\MyApp
--failureProgramParams Additional parameters for the failure program.
SECURITY WARNING: Use the
SERVY_FAILURE_PROGRAM_PARAMETERS environment
variable instead to avoid exposing sensitive
parameters in OS process listings.
--envVars Environment variables for the process. Enter
variables in the format varName=varValue separated
by semicolons (;). Use \= to escape '=', \" to
escape '"', \; to escape ';', \\ to escape '\', and
%% to escape '%' (collapses to a single '%').
Supports environment variable expansion, example:
VAR1=%ProgramData%\MyApp; VAR2=%VAR1%\bin. SECURITY
WARNING: Use the SERVY_ENVIRONMENT_VARIABLES
environment variable instead to avoid exposing
sensitive parameters in OS process listings.
--allowOverriddenRuntimeVars Allow overriding of protected runtime variables
(e.g., JAVA_HOME, JAVA_OPTS, CATALINA_OPTS) by the
service's environment variables.
--deps Specify one or more Windows service names (not
display names) that this service depends on
separated with semicolons (;). Each service name
must contain only letters, digits, hyphens,
underscores, periods, spaces, and dollar signs ($),
optionally preceded by '+' to reference a load-order
group, and must not exceed 256 characters. Windows
starts stopped dependencies automatically when this
service starts; if a dependency is disabled or fails
to start, this service will not start.
--user The service account username (e.g., .\username,
DOMAIN\username, DOMAIN\gMSA$, or a built-in
identity such as NT AUTHORITY\LocalService, NT
AUTHORITY\NetworkService, NT SERVICE\MyService or
IIS APPPOOL\MyPool). Leave the --password option or
SERVY_PASSWORD environment variable empty for
built-in, virtual and gMSA accounts. If this option
is not set, the service runs under Local System.
--password The service account password. SECURITY WARNING: Use
the SERVY_PASSWORD environment variable instead to
avoid exposing credentials in OS process listings.
--preLaunchPath The pre-launch executable path. Configure an
optional script or executable to run before the main
service starts. This is useful for preparing
configurations, fetching secrets, or other setup
tasks. If the pre-launch script fails, the service
will not start unless you enable
--preLaunchIgnoreFailure. Supports environment
variable expansion, example:
%JAVA_HOME%\bin\java.exe
--preLaunchStartupDir Specifies the directory in which the pre-launch
executable will start. If not set, defaults to the
service working directory. Supports environment
variable expansion, example: %PROGRAMDATA%\MyApp
--preLaunchParams Additional parameters for the pre-launch executable.
SECURITY WARNING: Use the
SERVY_PRE_LAUNCH_PARAMETERS environment variable
instead to avoid exposing sensitive parameters in OS
process listings.
--preLaunchEnv Environment variables for the pre-launch executable.
Enter variables in the format varName=varValue
separated by semicolons (;). Use \= to escape '=',
\" to escape '"', \; to escape ';', \\ to escape
'\', and %% to escape '%' (collapses to a single
'%'). Supports environment variable expansion,
example: VAR1=%ProgramData%\MyApp; VAR2=%VAR1%\bin.
SECURITY WARNING: Use the
SERVY_PRE_LAUNCH_ENVIRONMENT_VARIABLES environment
variable instead to avoid exposing sensitive
parameters in OS process listings.
--preLaunchStdout Path to stdout log file of the pre-launch
executable.
--preLaunchStderr Path to stderr log file of the pre-launch
executable.
--preLaunchTimeout Timeout for the pre-launch executable. Must be
between 0 and 86400 seconds. Set the timeout to 0 to
run the pre-launch hook in fire-and-forget mode.
When set to 0, the hook is started and the service
is launched immediately without waiting for
completion. Use this only for tasks that do not
affect the service's ability to start or run
correctly. Stdout/Stderr redirection and retries are
not available in fire-and-forget mode.
--preLaunchRetryAttempts Number of retry attempts for the pre-launch
executable if it fails. Must be between 0 and
100000.
--preLaunchIgnoreFailure Ignore failure and start service even if pre-launch
executable fails.
--postLaunchPath The post-launch executable path. Configure an
optional script or executable to run after the
process starts successfully. Supports environment
variable expansion, example:
%JAVA_HOME%\bin\java.exe
--postLaunchStartupDir Specifies the directory in which the post-launch
executable will start. If not set, defaults to the
service working directory. Supports environment
variable expansion, example: %PROGRAMDATA%\MyApp
--postLaunchParams Additional parameters for the post-launch
executable. SECURITY WARNING: Use the
SERVY_POST_LAUNCH_PARAMETERS environment variable
instead to avoid exposing sensitive parameters in OS
process listings.
--preStopPath The pre-stop executable path. Configure an optional
script or executable to run before the main service
stops. This can be used for graceful shutdown tasks
such as notifying external systems or draining
resources. The pre-stop process runs synchronously
and extends the service stop timeout while it is
running. Set the timeout to 0 to run the pre-stop
process in fire-and-forget mode. Supports
environment variable expansion, example:
%JAVA_HOME%\bin\java.exe
--preStopStartupDir Specifies the directory in which the pre-stop
executable will start. If not set, defaults to the
service working directory. Supports environment
variable expansion, example: %PROGRAMDATA%\MyApp
--preStopParams Additional parameters for the pre-stop executable.
SECURITY WARNING: Use the SERVY_PRE_STOP_PARAMETERS
environment variable instead to avoid exposing
sensitive parameters in OS process listings.
--preStopTimeout Timeout for the pre-stop executable. Set the timeout
to 0 to run the pre-stop process in fire-and-forget
mode. Must be between 0 and 86400 seconds.
--preStopLogAsError Log pre-stop failure as error.
--postStopPath The post-stop executable path. Configure an optional
script or executable to run after the wrapped
process and all of its child processes have exited.
The post-stop process is started in fire-and-forget
mode and does not block service shutdown. Supports
environment variable expansion, example:
%JAVA_HOME%\bin\java.exe
--postStopStartupDir Specifies the directory in which the post-stop
executable will start. If not set, defaults to the
service working directory. Supports environment
variable expansion, example: %PROGRAMDATA%\MyApp
--postStopParams Additional parameters for the post-stop executable.
SECURITY WARNING: Use the SERVY_POST_STOP_PARAMETERS
environment variable instead to avoid exposing
sensitive parameters in OS process listings.
-q, --quiet Suppress spinner and run in non-interactive mode.
--help Display this help screen.
--version Display version information.
```
> [!NOTE]
> If the value of `--params` in `install` command includes arguments that start with `--`, use an equals sign (=) to prevent parsing issues. Example: `--params="--mode=production --port=7008"` Without the equals sign, the CLI might interpret `--mode` or `--port` as its own options instead of part of the service parameters.
> [!IMPORTANT]
> **Security Best Practice:** Avoid using sensitive flags (e.g., `--password`, `--params`, `--envVars`, `--preLaunchEnv`) in production or scripts. Passing these values as command-line arguments makes them visible to any user or process with access to the Windows Process List or shell history files. Instead, set the corresponding environment variables (e.g., `SERVY_PASSWORD`, `SERVY_PROCESS_PARAMETERS`, `SERVY_ENVIRONMENT_VARIABLES`) before running the install command. See [Security](https://github.com/aelassas/servy/wiki/Security#6-sensitive-command-line-arguments--service-account-credentials) page for more information.
Below is the **recommended** way to install a service using the secure environment variable fallback pattern:
```powershell
# 1. Set sensitive values in the current process environment
$env:SERVY_PASSWORD = "your_secret_password"
$env:SERVY_PROCESS_PARAMETERS = "C:\Apps\App\index.js"
$env:SERVY_ENVIRONMENT_VARIABLES = "ENV_VAR1=VAL1; ENV_VAR2=VAL2;"
# 2. Run the install command without sensitive CLI flags
servy-cli install `
--name="My NodeJS Service" `
--description="My NodeJS Server" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\Apps\App" `
--startupType="Automatic" `
--priority="Normal" `
--stdout="C:\Apps\App\stdout.log" `
--stderr="C:\Apps\App\stderr.log" `
--enableSizeRotation `
--rotationSize=10 `
--enableHealth `
--heartbeatInterval=10 `
--maxFailedChecks=3 `
--recoveryAction="RestartService" `
--maxRestartAttempts=5 `
--deps="MongoDB; MySQL80" `
--user=".\serviceuser"
# 3. Clear sensitive variables from memory immediately after use
Remove-Item Env:SERVY_PASSWORD
Remove-Item Env:SERVY_PROCESS_PARAMETERS
Remove-Item Env:SERVY_ENVIRONMENT_VARIABLES
```
Below is an example usage of the `install` command:
```powershell
servy-cli install `
--name="My NodeJS Service" `
--description="My NodeJS Server" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\Apps\App" `
--params="C:\Apps\App\index.js" `
--startupType="Automatic" `
--priority="Normal" `
--stdout="C:\Apps\App\stdout.log" `
--stderr="C:\Apps\App\stderr.log" `
--enableSizeRotation `
--rotationSize=10 `
--enableHealth `
--heartbeatInterval=10 `
--maxFailedChecks=3 `
--recoveryAction="RestartService" `
--maxRestartAttempts=5 `
--envVars="ENV_VAR1=VAL1; ENV_VAR2=VAL2;" `
--deps="MongoDB; MySQL80"
```
Below is an example usage of the `install` command with a pre-launch script:
```powershell
servy-cli install `
--name="MyLegacyService" `
--description="Runs legacy app with dynamic config" `
--path="C:\Apps\LegacyApp\LegacyApp.exe" `
--startupDir="C:\Apps\LegacyApp" `
--params="--mode=production" `
--preLaunchPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--preLaunchStartupDir="C:\Scripts" `
--preLaunchParams="-File C:\Scripts\GenerateConfig.ps1 -VaultUrl https://vault.example.com -SecretName AppSecrets" `
--preLaunchEnv="ENV=production;API_KEY=abcdef123" `
--preLaunchStdout="C:\Logs\prelaunch_stdout.log" `
--preLaunchStderr="C:\Logs\prelaunch_stderr.log" `
--preLaunchTimeout=60 `
--preLaunchRetryAttempts=2 `
--preLaunchIgnoreFailure `
--enableHealth `
--heartbeatInterval=30 `
--maxFailedChecks=3 `
--recoveryAction="RestartService" `
--maxRestartAttempts=5 `
--stdout="C:\Logs\service_stdout.log" `
--stderr="C:\Logs\service_stderr.log" `
--enableSizeRotation `
--rotationSize=10
```
For more details on CPU affinity, see the [FAQ](https://github.com/aelassas/servy/wiki/FAQ#how-and-why-should-i-use-cpu-affinity-with-servy).
## Show Command
The `show` command prints the service configuration held in Servy's database in a form meant to be read rather than parsed. It is the human-readable counterpart of [`export`](https://github.com/aelassas/servy/wiki/Export-Import-Services), which writes the same records as XML or JSON.
`show` requires Administrator privileges: the records live under `%ProgramData%\Servy` and describe how privileged processes are launched.
> [!NOTE]
> **Encrypted fields are masked by default.** Servy encrypts nine columns at rest - the four parameter fields, both pre/post-stop parameter fields, both environment-variable fields, and the password. `show` prints each of them as `********` unless you ask for the values, and without `--decrypt` it does not even decrypt them, so the plaintext is never produced. A masked row still tells you the field is set: an unset column is left out entirely.
>
> **`--decrypt` (`-d`) reveals eight of the nine.** The stored password is the exception and stays masked even with the flag, exactly as an exported XML or JSON file never contains it.
>
> `--decrypt` requires `--name`. The all-services list renders none of the encrypted columns, so there is nothing there to reveal.
```text
PS> servy-cli show --help
Servy.CLI +
Copyright © 2026 Akram El Assas. All rights reserved.
-n, --name Name of the service to show. When omitted, all services are
listed.
-s, --search Filter the service list by a keyword matched against the
service name or description. Cannot be combined with --name.
-d, --decrypt Show the encrypted fields (parameters and environment
variables) in clear text instead of masked. Requires --name.
The stored password stays masked.
-q, --quiet Suppress spinner and run in non-interactive mode.
--help Display this help screen.
--version Display version information.
```
### One service
Pass `--name` (or `-n`) to print every stored setting of a single service, grouped by category. The service name comes first, then its live status and process id, then the rest. A category whose section trigger or path is unconfigured is omitted entirely, so features or hooks you never configured cost no screen space. Categories with active feature triggers (such as `Logs`, `Recovery`, and the hook sections) render all of their rows, displaying `-` for unset fields. The categories built only from optional paths and values: `Logs` (when stdout/stderr are blank), `Recovery` (when health monitoring is disabled), `Failure Program`, `Pre-Launch`, `Post-Launch`, `Pre-Stop` and `Post-Stop`, are omitted when inactive. Every on/off setting reads `Yes` or `No`; `Status`, `Startup Type`, `Priority`, `Rotation Period` and `Recovery` are printed as invariant enum names that are never localized, so they stay safe to parse. `Status` uses the same tokens the [`status`](https://github.com/aelassas/servy/wiki/Servy-CLI#additional-commands) command prints.
```text
PS> servy-cli show -n telegraf
Name : telegraf
Status : Running
Pid : 7312
Display Name : Telegraf Agent
Description : Metrics collection agent
Startup Type : AutomaticDelayedStart
Priority : Normal
Executable : C:\Program Files\telegraf\telegraf.exe
Startup Dir : C:\Program Files\telegraf
Parameters : ********
Account
Local System : No
User Account : .\telegraf-svc
Password : ********
Logs
Stdout : C:\Program Files\telegraf\log\out.log
Stderr : C:\Program Files\telegraf\log\err.log
Active Stdout : -
Active Stderr : -
Size Rotation : Yes
Rotation Size : 10 MB
Date Rotation : Yes
Rotation Period : Daily
Max Files : 7
Local Time : No
Timeouts
Start : 10s
Stop : 5s
Environment
Environment Variables : ********
Allow Overridden Runtime Vars : Yes
Other
Console UI : No
Debug Logs : No
```
Pass `--decrypt` (or `-d`) to read the masked fields. Only run it when you actually need the values, and treat the output the way you would treat an export file:
```text
PS> servy-cli show -n telegraf --decrypt
Name : telegraf
Status : Running
Pid : 7312
Display Name : Telegraf Agent
Description : Metrics collection agent
Startup Type : AutomaticDelayedStart
Priority : Normal
Executable : C:\Program Files\telegraf\telegraf.exe
Startup Dir : C:\Program Files\telegraf
Parameters : --config telegraf.conf --config-directory telegraf.d
Account
Local System : No
User Account : .\telegraf-svc
Password : ********
Logs
Stdout : C:\Program Files\telegraf\log\out.log
Stderr : C:\Program Files\telegraf\log\err.log
Active Stdout : -
Active Stderr : -
Size Rotation : Yes
Rotation Size : 10 MB
Date Rotation : Yes
Rotation Period : Daily
Max Files : 7
Local Time : No
Timeouts
Start : 10s
Stop : 5s
Environment
Environment Variables : API_TOKEN=9f3c1a; API_HOST=example.internal
Allow Overridden Runtime Vars : Yes
Other
Console UI : No
Debug Logs : No
```
The categories are `Account`, `Logs`, `Timeouts`, `Recovery`, `Failure Program`, `Environment`, `Pre-Launch`, `Post-Launch`, `Pre-Stop`, `Post-Stop` and `Other`.
When the named service is not in the database the command fails with `The specified service was not found.` and a non-zero exit code.
### All services
With no `--name`, `show` lists every service in the database, one line each:
```text
PS> servy-cli show
NAME DISPLAY NAME DESCRIPTION STARTUP TYPE STATUS PID
------------ ---------------- ------------------------ --------------------- ------- ----
backup-agent Backup Agent Nightly backup job Manual Stopped -
nginx Nginx Web Server Reverse proxy Automatic Running 4188
telegraf Telegraf Agent Metrics collection agent AutomaticDelayedStart Running 7312
3 service(s).
```
Use `--search` (or `-s`) to narrow the list by a keyword matched against the service name or description - the same search the [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager) list uses:
```text
PS> servy-cli show --search backup
NAME DISPLAY NAME DESCRIPTION STARTUP TYPE STATUS PID
------------ ------------ ------------------ ------------ ------- ---
backup-agent Backup Agent Nightly backup job Manual Stopped -
1 service(s).
```
`--search` narrows the list, so it cannot be combined with `--name`. When nothing matches, the command prints `No services found.` and succeeds.
> [!NOTE]
> A dash (`-`) in any field means the value is not set. The `STATUS` column reports `NotInstalled` for a service that is in Servy's database but no longer registered with the Windows Service Control Manager, and `Unknown` for one that is registered but whose status cannot be read. Status and startup-type values are invariant enum names that are never localized, so they stay safe to parse in scripts; the status values are the same tokens the [`status`](https://github.com/aelassas/servy/wiki/Servy-CLI#additional-commands) command prints.
## Additional Commands
* `uninstall`: Uninstall an existing service by name.
* `start`: Start a Windows service by name.
* `stop`: Stop a Windows service by name.
* `restart`: Restart a Windows service by name.
* `status`: Get a Windows service status by name.
* `export`: Export a Servy Windows service configuration to a configuration file.
* `import`: Import a Windows service configuration into Servy's database and optionally install it.
* `show`: Show a Servy Windows service configuration in a human-readable form, or list all services when no name is given. Encrypted fields are masked unless `--decrypt` is passed.
* `--version`: Show CLI version (it's a global flag).
## Tips
* Always run the CLI with Administrator privileges when executing commands that modify Windows services.
* Use the `--help` flag with any command to display detailed usage information and available options.
* When automating with scripts or CI/CD pipelines, rely on the CLI's exit codes: `0` indicates success, any other value indicates failure.
* Ensure that log file paths are writable by the user account under which the service runs to avoid permission issues.
## See Also
* [PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
* [Servy Automation & CI/CD](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD)
* [Examples & Recipes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes)
* [Export/Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services)
---
# Document: Servy PowerShell Module
> Source: https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#introduction)
1. [Compatibility & Runtime Requirements](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#compatibility--runtime-requirements)
1. [Installation](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#installation)
1. [Usage Examples](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#usage-examples)
1. [Why use parameter splatting?](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#why-use-parameter-splatting)
1. [Install a New Service](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#install-a-new-service)
1. [Show Service Configuration](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#show-service-configuration)
1. [Export Service Configuration](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#export-service-configuration)
1. [Import Service Configuration](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#import-service-configuration)
1. [Start, Stop, Restart, and Check Status](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#start-stop-restart-and-check-status)
1. [Uninstall a Service](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#uninstall-a-service)
1. [Cmdlets Reference](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#cmdlets-reference)
1. [Install-ServyService Parameter Reference](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#install-servyservice-parameter-reference)
1. [Troubleshooting](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#troubleshooting)
1. [See Also](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#see-also)
1. [References](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#references)
> [!IMPORTANT]
> **Console UI Compatibility**
> If your app tries to visually update the command prompt (like clearing the screen or moving the cursor), it will crash when running as a background service since services don't have visible windows. To fix this without changing your code, enable the `-EnableConsoleUI` option in the service configuration while installing your service.
## Introduction
The Servy PowerShell Module provides a lightweight, scriptable interface for managing background services. It is designed to be highly portable, allowing sysadmins to automate deployments across diverse Windows environments.
### Compatibility & Runtime Requirements
The PowerShell module (`Servy.psm1`) is authored to be compatible with **PowerShell 2.0 and later**. However, its ability to run on older operating systems depends on which version of the Servy CLI is present:
* **For Windows 10 / 11 / Server 2016+**: Use the **Modern CLI (.NET 10.0+)**. This version offers the best performance and utilizes the latest Windows security features.
* **For Windows 7 SP1 / 8 / Server 2008 R2**: Use the **Legacy CLI (.NET Framework 4.8)**. When paired with this build, the PowerShell module is fully functional on older distributions.
> [!NOTE]
> The Task Scheduler hooks shipped under `taskschd/`
> (`ServyFailureEmail.ps1`, `ServyFailureNotification.ps1`, `Get-ServyLastErrors.ps1`,
> `Servy-Watermark.psm1`) have stricter requirements:
> - `Get-WinEvent`-based scripts need **PowerShell 5.1+** (Windows 7 SP1 / Server 2008 R2 SP1+).
> - Toast notifications need **PowerShell 5.1+** (Windows 10 1607+).
> Only `Servy.psm1` itself is fully PS 2.0 compatible.
## Installation
Import the module in your PowerShell session:
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
```
Display the version:
```powershell
Get-ServyVersion
```
Display help:
```powershell
# View general module help and available commands
Get-ServyHelp
# Get help for other supported commands
Get-ServyHelp -Command "install"
Get-ServyHelp -Command "start"
Get-ServyHelp -Command "stop"
```
## Usage Examples
**Pro Tip:** In PowerShell, switch parameters (like `-Quiet`, `-Install`, or `-EnableHealth`) are toggle flags.
When calling a command **inline**, do not pass values like `$true` or `$false`; including the flag enables it, and omitting it leaves it disabled. When using **parameter splatting**, it is correct and idiomatic to set switch parameters to `$true` in the splat hashtable.
### Why use parameter splatting?
Parameter splatting makes PowerShell commands easier to read, maintain, and extend.
Instead of long command lines with many parameters, options are grouped into a single hashtable that clearly shows intent and defaults. This is especially useful for Servy commands, which support many optional parameters and advanced scenarios such as hooks, logging, and recovery configuration.
Splatting also:
* Avoids line continuation backticks
* Makes it easier to add or remove parameters
* Keeps examples readable as the API evolves
### Install a New Service
```powershell
# CORRECT: Switch flags are included by presence or set to $true in the splat hashtable
$installParams = @{
Name = "WexflowServer"
Description = "Wexflow Workflow Engine"
Path = "C:\Program Files\dotnet\dotnet.exe"
StartupDir = "C:\Program Files\Wexflow Server\Wexflow.Server"
Params = "Wexflow.Server.dll"
StartupType = "Automatic"
EnableHealth = $true
RecoveryAction = "RestartService"
HeartbeatInterval = 30
MaxFailedChecks = 3
}
Install-ServyService @installParams
# INCORRECT: Do not do this with inline switches
# Install-ServyService -Quiet $true -EnableHealth $true
# CORRECT: Use inline switches without values, or use splatting
# Inline: Install-ServyService -Quiet -EnableHealth
# Splatting:
# $params = @{ Quiet = $true; EnableHealth = $true }
# Install-ServyService @params
```
### Show Service Configuration
`Show-ServyService` prints every stored setting of one service in a readable, category-grouped
form - the name first, then the live status and process id, then the rest. `Show-ServyServices`
lists all services instead, one line each, and takes an optional `-Search` keyword matched
against the service name or description.
Both require an elevated session, and neither ever prints the stored password.
> [!NOTE]
> **Encrypted fields are masked by default.** `Show-ServyService` prints the parameter and
> environment-variable fields as `********`, and without `-Decrypt` it does not decrypt them at all.
> Pass `-Decrypt` to read the values. The stored password stays masked either way.
> `Show-ServyServices` renders no encrypted column, so it has nothing to mask.
```powershell
# One service, full configuration - encrypted fields masked
Show-ServyService -Name "WexflowServer"
# Same, with the parameters and environment variables in clear text
Show-ServyService -Name "WexflowServer" -Decrypt
# Every service in the Servy database
Show-ServyServices
# Only the services whose name or description matches a keyword
Show-ServyServices -Search "wexflow"
```
```text
PS> Show-ServyServices
NAME DISPLAY NAME DESCRIPTION STARTUP TYPE STATUS PID
------------ ---------------- ------------------------ --------------------- ------- ----
backup-agent Backup Agent Nightly backup job Manual Stopped -
nginx Nginx Web Server Reverse proxy Automatic Running 4188
telegraf Telegraf Agent Metrics collection agent AutomaticDelayedStart Running 7312
3 service(s).
```
> [!NOTE]
> These two cmdlets are for reading configuration at a console. To consume a service
> configuration from a script, use `Export-ServyServiceConfig` and parse the XML or JSON,
> which is a stable contract; the `show` layout is not.
### Export Service Configuration
```powershell
$exportXmlParams = @{
Name = "WexflowServer"
ConfigFileType = "xml"
Path = "C:\WexflowServer.xml"
}
Export-ServyServiceConfig @exportXmlParams
$exportJsonParams = @{
Name = "WexflowServer"
ConfigFileType = "json"
Path = "C:\WexflowServer.json"
}
Export-ServyServiceConfig @exportJsonParams
```
### Import Service Configuration
```powershell
# CORRECT: Switch flags are included by presence or set to $true in the splat hashtable
$importXmlParams = @{
ConfigFileType = "xml"
Path = "C:\WexflowServer.xml"
Install = $true
}
Import-ServyServiceConfig @importXmlParams
$importJsonParams = @{
ConfigFileType = "json"
Path = "C:\WexflowServer.json"
}
Import-ServyServiceConfig @importJsonParams
```
### Start, Stop, Restart, and Check Status
Servy installs a **standard Windows service** that is fully registered with the Service Control Manager (SCM). Once installed, the service can be managed using **any normal Windows service control mechanism** such as `Start-Service`, `sc.exe`, `services.msc`, or third-party tools.
The Servy PowerShell cmdlets shown below are provided as **convenience wrappers** for scripting consistency. They are **not required** to start or stop the service.
```powershell
$serviceParams = @{
Name = "WexflowServer"
}
Start-ServyService @serviceParams
Get-ServyServiceStatus @serviceParams
Stop-ServyService @serviceParams
Restart-ServyService @serviceParams
```
#### Using standard Windows service commands
The same service can be controlled using built-in PowerShell and Windows tools:
```powershell
Start-Service -Name WexflowServer
Get-Service -Name WexflowServer
Stop-Service -Name WexflowServer
```
Or from an **elevated** Command Prompt:
```cmd
sc.exe start WexflowServer
sc.exe stop WexflowServer
```
**Note:** When using PowerShell, invoke `sc.exe` explicitly (for example, `sc.exe start ServiceName`) from an **elevated PowerShell session**. PowerShell defines `sc` as an alias, so omitting the `.exe` may result in unexpected behavior. This is standard PowerShell behavior and not related to Servy.
Once installed, the service behaves like any other native Windows service and does not depend on Servy-specific commands to run.
### Uninstall a Service
```powershell
$uninstallParams = @{
Name = "WexflowServer"
}
Uninstall-ServyService @uninstallParams
```
## Cmdlets Reference
| Cmdlet | Parameters | Description |
| --- | --- | --- |
| `Set-ServyConfig` | `-TimeoutSeconds` (int, optional, Default: 600)
`-MaxBufferChars` (int, optional, **no longer used** since 9.9; accepted for compatibility) | **Configures module-level execution settings for the Servy CLI.**
Sets the execution timeout used by every cmdlet. This is useful for exceptionally long-running operations.
**Example:**
- `Set-ServyConfig -TimeoutSeconds 1200` |
| `Install-ServyService` | `-Name` (string, **required**)
`-Path` (string, **required**)
*(See [Install-ServyService Parameters](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module#install-servyservice-parameter-reference) for the complete parameter list)* | **Installs a new Windows service with advanced configuration.**
Wraps the Servy CLI `install` command to turn any executable into a managed Windows service. It supports complex lifecycle management, logging, and self-healing features.
**Key Features:**
- **Logging:** Redirect output to files with rotation by size or date.
- **Health:** Automated recovery actions (e.g., `RestartService`) based on failed heartbeats.
- **Lifecycle:** Execute tasks *before* (`PreLaunch`) or *after* (`PostLaunch`) startup.
**Examples:**
- `Install-ServyService -Name "MyApp" -Path "C:\App\app.exe"`
- `Install-ServyService -Name "MyApp" -DisplayName "My App" -Path "C:\App\app.exe"`
- `Install-ServyService -Name "LogApp" -Path "C:\App\app.exe" -Stdout "C:\App\stdout.log" -EnableSizeRotation -RotationSize 10`
- `Install-ServyService -Name "SecureApp" -Path "C:\App\app.exe" -EnvVars "API_KEY=12345;DB_PORT=5432"` |
| `Uninstall-ServyService` | `-Name` (string, **required**)
`-Quiet` (switch, optional) | **Uninstalls a Windows service by name.**
Completely removes the service entry from the Windows Service Control Manager (SCM) and the Servy internal database.
**Example:**
- `Uninstall-ServyService -Name "MyApp" -Quiet` |
| `Start-ServyService` | `-Name` (string, **required**)
`-Quiet` (switch, optional) | **Starts a Windows service.**
Triggers the service start signal. If any `PreLaunch` settings were defined during installation, the pre-launch process will be executed and must succeed before the main service starts (unless `PreLaunchIgnoreFailure` was used).
**Example:**
- `Start-ServyService -Name "MyApp"` |
| `Stop-ServyService` | `-Name` (string, **required**)
`-Quiet` (switch, optional) | **Stops a Windows service.**
Sends a termination signal to the service process. It respects the `StopTimeout` value set during installation, allowing the application to shut down gracefully before forcing termination.
**Example:**
- `Stop-ServyService -Name "MyApp" -Quiet` |
| `Restart-ServyService` | `-Name` (string, **required**)
`-Quiet` (switch, optional) | **Restarts a Windows service.**
Performs a full stop operation followed by a start operation. This is the recommended way to apply configuration changes after an import.
**Example:**
- `Restart-ServyService -Name "MyApp"` |
| `Get-ServyServiceStatus` | `-Name` (string, **required**)
`-Quiet` (switch, optional) | **Retrieves the current status of the service.**
Queries the SCM for the real-time state of the process.
**Possible Results:** `NotInstalled`, `Stopped`, `StartPending`, `StopPending`, `Running`, `ContinuePending`, `PausePending`, `Paused`, `Unknown`.
**Example:**
- `Get-ServyServiceStatus -Name "MyApp"` |
| `Show-ServyService` | `-Name` (string, **required**)
`-Decrypt` (switch, optional)
`-Quiet` (switch, optional) | **Shows the full configuration of one service in a human-readable form.**
Wraps the Servy CLI `show` command. Prints the service name, its live status and process id first, then every stored setting grouped by category (`Account`, `Logs`, `Timeouts`, `Recovery`, `Failure Program`, `Environment`, `Pre-Launch`, `Post-Launch`, `Pre-Stop`, `Post-Stop`, `Other`). Categories with nothing configured are omitted. The fields Servy encrypts at rest - parameters and environment variables - are masked as `********` unless `-Decrypt` is passed, and without it they are not decrypted at all. The stored password stays masked either way and is never printed. Requires an elevated session.
**Examples:**
- `Show-ServyService -Name "MyApp"`
- `Show-ServyService -Name "MyApp" -Decrypt` |
| `Show-ServyServices` | `-Search` (string, optional)
`-Quiet` (switch, optional) | **Lists every service stored in the Servy database.**
Wraps the Servy CLI `show` command with no service name, printing one line per service with its name, display name, description, startup type, status and process id. Use `-Search` to narrow the list by a keyword matched against the service name or description, the same way the Servy Manager search box does. Requires an elevated session.
**Examples:**
- `Show-ServyServices`
- `Show-ServyServices -Search "web"` |
| `Export-ServyServiceConfig` | `-Name` (string, **required**)
`-ConfigFileType` (string, **required**: `xml`, `json`)
`-Path` (string, **required**)
`-Quiet` (switch, optional) | **Exports the service configuration to a file.**
Saves all metadata (paths, timeouts, health checks, etc.) to an external file for backup or template creation.
**Examples:**
- `Export-ServyServiceConfig -Name "MyApp" -ConfigFileType "json" -Path "C:\Backups\MyApp.json"`
- `Export-ServyServiceConfig -Name "MyApp" -ConfigFileType "xml" -Path "C:\Backups\MyApp.xml"` |
| `Import-ServyServiceConfig` | `-ConfigFileType` (string, **required**: `xml`, `json`)
`-Path` (string, **required**)
`-Install` (switch, optional)
`-Quiet` (switch, optional) | **Imports a configuration from a file.**
Loads settings from a previously exported file into the Servy database. Use the `-Install` switch to register the service with Windows immediately after import.
**Examples:**
- `Import-ServyServiceConfig -ConfigFileType "json" -Path "C:\Configs\NewApp.json" -Install`
- `Import-ServyServiceConfig -ConfigFileType "xml" -Path "C:\Configs\NewApp.xml"` |
| `Get-ServyHelp` | `-Command` (string, optional)
`-Quiet` (switch, optional) | **Displays the Servy CLI help manual.**
Provides global usage instructions or detailed parameter explanations for a specific command if requested.
**Examples:**
- `Get-ServyHelp`
- `Get-ServyHelp -Command "install"` |
| `Get-ServyVersion` | `-Quiet` (switch, optional) | **Displays the version of the Servy binary.**
Outputs the version string of the `servy-cli.exe` file being utilized by the module.
**Example:**
- `Get-ServyVersion -Quiet` |
### Install-ServyService Parameter Reference
#### Core Configuration
| Parameter | Type | Required | Values / Range / Notes |
| --- | --- | --- | --- |
| `-Name` | string | **Yes** | Service unique identifier name |
| `-Path` | string | **Yes** | Path to the executable process |
| `-DisplayName` | string | No | Display name in Windows Services (`services.msc`) |
| `-Description` | string | No | Descriptive text about the service |
| `-StartupDir` | string | No | Working directory for the service process |
| `-Params` | string | No | Additional parameters passed to the executable |
| `-StartupType` | string | No | Options: `Automatic`, `AutomaticDelayedStart`, `Manual`, `Disabled` |
| `-Priority` | string | No | Options: `Idle`, `BelowNormal`, `Normal`, `AboveNormal`, `High`, `RealTime` |
| `-CpuAffinity` | string | No | Logical CPUs allowed (e.g., `'0-3,8'` or `'0xFF00'`) |
| `-User` | string | No | Service account username (`.\username` or `DOMAIN\username`) |
| `-Password` | SecureString | No | Password for the service account |
| `-EnvVars` | string | No | Environment variables (`Name=Value;Name=Value`) |
| `-AllowOverriddenRuntimeVars` | switch | No | Allow the service's environment variables to override protected runtime variables (e.g., `JAVA_HOME`, `JAVA_OPTS`, `CATALINA_OPTS`) |
| `-Deps` | string | No | Windows service dependencies (by service name) |
| `-StartTimeout` | int | No | Timeout to wait for successful start (range: `1`-`86400` seconds) |
| `-StopTimeout` | int | No | Timeout to wait for process exit (range: `1`-`86400` seconds) |
| `-EnableConsoleUI` | switch | No | Enable console UI (disables stdout/stderr redirection) |
| `-Quiet` | switch | No | Suppress spinner and run non-interactively |
#### Logging
| Parameter | Type | Required | Values / Range / Notes |
| --- | --- | --- | --- |
| `-Stdout` | string | Optional | Log file path for capturing stdout |
| `-Stderr` | string | Optional | Log file path for capturing stderr |
| `-EnableRotation` | switch | Optional | *Deprecated*: use `-EnableSizeRotation` |
| `-EnableSizeRotation` | switch | Optional | Enable size-based log rotation |
| `-RotationSize` | int | Optional | Max log file size before rotation (range: `1`-`10240` MB) |
| `-EnableDateRotation` | switch | Optional | Enable date-based log rotation |
| `-DateRotationType` | string | Optional | Options: `Daily`, `Weekly`, `Monthly`, `None` |
| `-MaxRotations` | int | Optional | Rotated logs to keep (range: `0`-`10000`; `0` = unlimited) |
| `-UseLocalTimeForRotation` | switch | Optional | Calculate rotation using local server time instead of UTC |
| `-EnableDebugLogs` | switch | Optional | Enable debug logging to `Servy.Service.log` |
#### Health Monitoring & Self-Healing
| Parameter | Type | Required | Values / Range / Notes |
| --- | --- | --- | --- |
| `-EnableHealth` | switch | Optional | Enable automated health monitoring |
| `-HeartbeatInterval` | int | Optional | Heartbeat interval in seconds (range: `5`-`86400` seconds) |
| `-MaxFailedChecks` | int | Optional | Failed checks before triggering recovery (range: `1`-`100000`) |
| `-RecoveryAction` | string | Optional | Options: `None`, `RestartService`, `RestartProcess`, `RestartComputer` |
| `-RecoveryOnCleanExit` | switch | Optional | Run recovery action even if process exits with code 0 |
| `-MaxRestartAttempts` | int | Optional | Max restart attempts (range: `0`-`100000`; `0` = unlimited) |
| `-HeartbeatUrl` | string | Optional | Out-of-band diagnostic ping URL (e.g., healthchecks.io) |
| `-HeartbeatUrlTimeoutSeconds` | int | Optional | Ping response timeout (range: `2`-`30` seconds) |
| `-EnableHeartbeatUrlFlags` | switch | Optional | Include heartbeat URL flags (`/start`, `/fail`) |
| `-FailureProgramPath` | string | Optional | Path to program/script run upon service failure |
| `-FailureProgramStartupDir` | string | Optional | Working directory for failure program |
| `-FailureProgramParams` | string | Optional | Parameters for failure program |
#### Lifecycle Hooks (Pre/Post Launch & Stop)
| Parameter | Type | Required | Values / Range / Notes |
| --- | --- | --- | --- |
| `-PreLaunchPath` | string | Optional | Executable/script run before service launch |
| `-PreLaunchStartupDir` | string | Optional | Working directory for PreLaunch script |
| `-PreLaunchParams` | string | Optional | Additional parameters for PreLaunch executable |
| `-PreLaunchEnv` | string | Optional | Environment variables for PreLaunch executable |
| `-PreLaunchStdout` | string | Optional | File path for PreLaunch stdout log |
| `-PreLaunchStderr` | string | Optional | File path for PreLaunch stderr log |
| `-PreLaunchTimeout` | int | Optional | PreLaunch timeout (range: `0`-`86400`s; `0` = fire-and-forget) |
| `-PreLaunchRetryAttempts` | int | Optional | Retry attempts for PreLaunch executable (range: `0`-`100000`) |
| `-PreLaunchIgnoreFailure` | switch | Optional | Proceed with service start even if PreLaunch fails |
| `-PostLaunchPath` | string | Optional | Executable/script run after service launch (fire-and-forget) |
| `-PostLaunchStartupDir` | string | Optional | Working directory for PostLaunch script |
| `-PostLaunchParams` | string | Optional | Additional parameters for PostLaunch executable |
| `-PreStopPath` | string | Optional | Executable/script run before service stops |
| `-PreStopStartupDir` | string | Optional | Working directory for PreStop script |
| `-PreStopParams` | string | Optional | Additional parameters for PreStop executable |
| `-PreStopTimeout` | int | Optional | PreStop timeout (range: `0`-`86400`s; `0` = fire-and-forget) |
| `-PreStopLogAsError` | switch | Optional | Treat PreStop failures as errors |
| `-PostStopPath` | string | Optional | Executable/script run after service stops |
| `-PostStopStartupDir` | string | Optional | Working directory for PostStop script |
| `-PostStopParams` | string | Optional | Additional parameters for PostStop executable |
For more details on CPU affinity, see the [FAQ](https://github.com/aelassas/servy/wiki/FAQ#how-and-why-should-i-use-cpu-affinity-with-servy).
## Troubleshooting
1. Installation Fails When Passing `$true` or `$false`
A common mistake in PowerShell is attempting to pass a boolean value to a switch parameter (e.g., `-Quiet $true`).
* The Symptom: The command fails with a "Parameter cannot be found" error or, more commonly, PowerShell interprets `$true` as the next positional argument. In `Install-ServyService`, this often results in `$true` being mistakenly assigned to the `-Path` or `-Name` parameters, causing the underlying CLI call to fail.
* The Fix: Remove the `$true` or `$false` reference. Use `-Quiet` to turn it on, and omit it to keep it off.
1. "Access Denied" Errors
Most Servy operations (install, uninstall, start, stop) interact directly with the Windows Service Control Manager.
* Solution: Ensure your PowerShell session is running with Administrator privileges. If you are using an IDE like VS Code, restart it as an Administrator.
1. Service Fails to Start
If `Start-ServyService` returns a success message but the service status remains Stopped, the issue is likely within the application executable or the PreLaunch configuration.
* Solution: Check your `stdout` and `stderr` logs if you configured them during installation.
* Validation: Run the command defined in `-Path` and `-Params` manually in a command prompt to see if it crashes immediately.
1. CLI Executable Not Found
In portable mode, the module expects `servy-cli.exe` to be in the same folder as `Servy.psm1`.
* Solution: Verify that the files haven't been separated. If you are using the installed version, ensure `%ProgramFiles%\Servy\` is in your System **PATH** or that the files exist in that directory.
1. Issues in Automated Environments (Ansible/CI/CD)
Automated runners often hang if a process attempts to draw an interactive progress bar or spinner.
* Solution: Always use the `-Quiet` switch in non-interactive scripts. This forces the module to output plain text logs instead of interactive UI elements.
1. Environment Variable Formatting
The `-EnvVars` and `-PreLaunchEnv` parameters require a specific string format.
* Requirement: Use the `Key=Value` format, separated by semicolons.
* Example: `-EnvVars "NODE_ENV=production;PORT=3000"`
## See Also
* [Export/Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services)
## References
* [Servy.psm1](https://github.com/aelassas/servy/blob/main/src/Servy.CLI/Servy.psm1)
* [servy-module-examples.ps1](https://github.com/aelassas/servy/blob/main/src/Servy.CLI/servy-module-examples.ps1)
---
# Document: Advanced Configuration
> Source: https://github.com/aelassas/servy/wiki/Advanced-Configuration
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Advanced-Configuration#introduction)
1. [File Locations & Prerequisites](https://github.com/aelassas/servy/wiki/Advanced-Configuration#file-locations--prerequisites)
1. [Prerequisites](https://github.com/aelassas/servy/wiki/Advanced-Configuration#prerequisites)
1. [Where to Find Settings](https://github.com/aelassas/servy/wiki/Advanced-Configuration#where-to-find-settings)
1. [Logging Configuration](https://github.com/aelassas/servy/wiki/Advanced-Configuration#logging-configuration)
1. [Logging Levels (`LogLevel`)](https://github.com/aelassas/servy/wiki/Advanced-Configuration#logging-levels-loglevel)
1. [Log Rolling Interval (`LogRollingInterval`)](https://github.com/aelassas/servy/wiki/Advanced-Configuration#log-rolling-interval-logrollinginterval)
1. [Settings Reference](https://github.com/aelassas/servy/wiki/Advanced-Configuration#settings-reference)
1. [Shared Core Settings](https://github.com/aelassas/servy/wiki/Advanced-Configuration#shared-core-settings)
1. [General Logging](https://github.com/aelassas/servy/wiki/Advanced-Configuration#general-logging)
1. [Servy Windows Service](https://github.com/aelassas/servy/wiki/Advanced-Configuration#servy-windows-service)
1. [Servy Host Service](https://github.com/aelassas/servy/wiki/Advanced-Configuration#servy-host-service)
1. [Restarter Settings](https://github.com/aelassas/servy/wiki/Advanced-Configuration#restarter-settings)
1. [Desktop App Settings](https://github.com/aelassas/servy/wiki/Advanced-Configuration#desktop-app-settings)
1. [Manager App Settings](https://github.com/aelassas/servy/wiki/Advanced-Configuration#manager-app-settings)
1. [CLI Settings](https://github.com/aelassas/servy/wiki/Advanced-Configuration#cli-settings)
1. [Build Configuration Examples](https://github.com/aelassas/servy/wiki/Advanced-Configuration#build-configuration-examples)
1. [Modern Build (.NET 10.0+)](https://github.com/aelassas/servy/wiki/Advanced-Configuration#modern-build-net-100)
1. [Legacy Build (.NET Framework 4.8)](https://github.com/aelassas/servy/wiki/Advanced-Configuration#legacy-build-net-framework-48)
1. [See Also](https://github.com/aelassas/servy/wiki/Advanced-Configuration#see-also)
## Introduction
This guide explains how to fine-tune the Servy ecosystem, from application refresh rates to advanced log rotation strategies.
## File Locations & Prerequisites
### Prerequisites
* **Administrator Privileges**: You must run your text editor (e.g., VS Code, Notepad++) as an **Administrator** to save changes to `%ProgramFiles%` or `%ProgramData%`.
* **Restart Required**: Changes to configuration files do not take effect dynamically. You must restart the associated application (Desktop/Manager) or the Windows Service (via SCM or CLI) for changes to apply.
### Where to Find Settings
| Component | Modern Build (.NET 10.0+) | Legacy Build (.NET 4.8) |
| :--- | :--- | :--- |
| **Windows Service** | `%ProgramData%\Servy\appsettings.service.json` | `...\Servy.Service.Net48.exe.config` |
| **Windows Service (CLI)** | `%ProgramData%\Servy\appsettings.service.json` | `...\Servy.Service.CLI.Net48.exe.config` |
| **Servy Host Service** | `%ProgramData%\Servy\appsettings.host.json` | `...\Servy.Host.Net48.exe.config` |
| **Restarter** | `%ProgramData%\Servy\appsettings.restarter.json` | `...\Servy.Restarter.Net48.exe.config` |
| **Desktop App** | `%ProgramFiles%\Servy\appsettings.desktop.json` | `...\Servy.exe.config` |
| **Manager App** | `%ProgramFiles%\Servy\appsettings.manager.json` | `...\Servy.Manager.exe.config` |
| **CLI** | `%ProgramFiles%\Servy\appsettings.cli.json` | `...\servy-cli.exe.config` |
> [!NOTE]
> For Servy v8.7 and below, the settings file for the desktop app and service is named `appsettings.json`. For Servy v8.8+, refer to the table above.
## Logging Configuration
Servy uses a dual-channel logging engine that writes to both the **Windows Event Log** (for high-level monitoring) and **local flat files** (for detailed diagnostics).
**Log Directory:** `%ProgramData%\Servy\logs\`
### Logging Levels (`LogLevel`)
Determines output verbosity. Values are case-insensitive.
| Level | Target | Description |
| :--- | :--- | :--- |
| **DEBUG** | **File Only** | Most verbose; logs internal state and heartbeats. Not recommended for production. |
| **INFO** | **Both** | **(Default)** Logs major milestones (starts, stops, configuration changes). |
| **WARN** | **Both** | Logs non-fatal issues like slow shutdowns or configuration typos. |
| **ERROR** | **Both** | Logs critical failures, crashes, or access denied errors. |
| **NONE** | **None** | Disables the logging engine entirely. |
### Log Rolling Interval (`LogRollingInterval`)
Determines how often a new log file is created. Available from Servy v7.8+.
> [!IMPORTANT]
> **Size Rotation Precedence**: The `LogRotationSizeMB` limit always takes precedence. If a log reaches the size limit (e.g., 10MB) before the time interval (e.g., `Monthly`), it will rotate immediately to prevent disk overflow.
* `Daily`: Rotates at the start of each new calendar day. **Defaults to UTC**; set
`UseLocalTimeForRotation: true` to rotate at midnight in the server's local time.
* `Weekly`: Rotates when the week number changes, with weeks starting on Monday and week 1
being the first week with at least four days in the new year (.NET `FirstFourDayWeek`).
This is close to ISO 8601 but not identical: the last days of December keep week 52 or 53,
so a week that spans 1 January also rotates on 1 January. **Defaults to UTC**; set
`UseLocalTimeForRotation: true` to use local time.
* `Monthly`: Rotates on the first day of every calendar month. **Defaults to UTC**;
set `UseLocalTimeForRotation: true` to use local time.
* `None`: **(Default)** Files only rotate when they hit the size limit.
## Settings Reference
The settings below apply to the Windows service wrapper, restarter, desktop app, manager app, and CLI. Use them when you need to fine-tune logging, refresh behavior, or operational limits for a specific component.
### Shared Core Settings
Every Servy component (the Windows service wrapper, the restarter, the desktop app, the manager app and the CLI) uses the same configuration database and the same encryption key. Starting from v10.2 their locations are **fixed and read-only**: they always live in the Servy vault, `%ProgramData%\Servy`, which is the only folder whose permissions Servy hardens (see [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening)). A database or key stored anywhere else would not get those permissions.
The keys below were configurable up to v10.1. Since v10.2 Servy **ignores** them if they are present in a settings file (`appsettings.*.json` in the .NET 10 build, `*.exe.config` in the .NET Framework 4.8 build), and logs a warning naming the key and the settings file when one of them is still set. Remove the entry to silence the warning.
| Setting (ignored since v10.2) | Fixed value | Description |
| :--- | :--- | :--- |
| `ConnectionStrings:DefaultConnection` (`DefaultConnection` in `.exe.config`) | `Data Source=%ProgramData%\Servy\db\Servy.db;Busy Timeout=5000;Journal Mode=WAL;Pooling=True;` | SQLite connection string of the configuration database. |
| `Security:AESKeyFilePath` | `%ProgramData%\Servy\security\aes_key.dat` | AES encryption key file used to protect sensitive service settings such as passwords. |
| `Security:AESIVFilePath` | `%ProgramData%\Servy\security\aes_iv.dat` | **(Legacy)** Static AES Initialization Vector (IV) file used by the v1 cipher format (Servy < 6.5). Unused in current builds (`AllowLegacyV1Decryption = false`). |
> [!IMPORTANT]
> **Upgrading from v10.1 or earlier with a custom location:** if you moved the database or the key with one of these settings, move the files back before you upgrade. Stop every Servy-managed service, close the desktop and manager apps, copy the database to `%ProgramData%\Servy\db\Servy.db` and the key to `%ProgramData%\Servy\security\aes_key.dat` (keep `aes_iv.dat` next to it if you have one), then upgrade. Otherwise v10.2 creates a new, empty database and a new key in the vault and does not see the services recorded in the old database. The key is bound to the machine, so this works only on the machine that created it; to move services to another machine, use [Backup, Restore & VM Cloning](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning).
### General Logging
| Setting | Default | Description |
| :--- | :--- | :--- |
| `LogLevel` | `INFO` | Verbosity of the output. |
| `EnableSizeRotation` | `true` | Enables size-based log rotation for internal log files. When disabled, `LogRotationSizeMB` has no effect (available from v9.4+). |
| `LogRotationSizeMB` | `10` | Maximum log file size in MB before a file is archived. Requires `EnableSizeRotation` to be `true`. Must be greater than `0`; `0` or a negative value is silently replaced by the `10` MB default. To disable size-based rotation, set `EnableSizeRotation` to `false` instead. |
| `MaxBackupLogFiles` | `10` | Number of archived logs retained. Set to `0` for unlimited. |
| `EnableEventLog` | `true` | Controls whether Servy and the Windows Service infrastructure write to the Windows Event Log. |
| `UseLocalTimeForRotation` | `false` | Uses local system time instead of UTC when determining date-based log rotation boundaries. |
| `LogRollingInterval` | `None` | Date-based rotation interval: `Daily`, `Weekly`, `Monthly`, or `None`. See the Log Rolling Interval section above. Available from Servy v7.8+. |
> [!NOTE]
> The `EnableEventLog` setting is primarily respected by the **Servy Windows Service** wrapper and **Servy Windows Service Restarter** utility. Setting this in the **CLI**, **Desktop App**, or **Manager App** configuration has no effect.
> [!WARNING]
> **Side Effect of `EnableEventLog: false` on Service Failure Diagnostics**
>
> Setting `EnableEventLog` to `false` disables both Servy's event logger and the framework's automatic service state logging (`AutoLog = false`).
>
> When set to `false`, startup or shutdown failures (such as `SR.StartFailed`, `SR.StopFailed`, or `SR.ShutdownFailed`) will **not** generate entry logs in the Windows Event Viewer. In this state, critical diagnostic entries will only be captured in the local file logs under `%ProgramData%\Servy\logs\`.
### Servy Windows Service
| Setting | Default | Description |
| :--- | :--- | :--- |
| `Timing:WaitChunkMs` | `5000` | Granularity, in milliseconds, of the wait loop used while running synchronous **pre-launch / pre-stop hooks**. It controls how often the wait is sliced to check for cancellation or timeout, and does not control health-check frequency. |
| `Timing:ScmAdditionalTimeMs` | `15000` | Additional buffer time, in milliseconds, added to Service Control Manager (SCM) operations to prevent premature timeouts. |
### Servy Host Service
The `Servy` service (`Servy.Host.exe`, or `Servy.Host.Net48.exe` in the .NET Framework 4.8 build, see [Architecture](https://github.com/aelassas/servy/wiki/Architecture#servyhost-servy-service)) reads its settings from the file next to its executable, `%ProgramData%\Servy`. The file is optional: when it is missing, every setting takes its default. It has no settings of its own beyond logging; the `Timing:*` settings above belong to the service wrapper and the host ignores them.
| Setting | Default | Description |
| :--- | :--- | :--- |
| `General Logging` | - | Uses the settings from the **General Logging** section above. The host writes `Servy.Host.log` to `%ProgramData%\Servy\logs\`. |
### Restarter Settings
| Setting | Default | Description |
| :--- | :--- | :--- |
| `RestartTimeoutSeconds` | `120` | Maximum time, in seconds, that the Restarter waits for the service to stop and start again before giving up. Bounded to `[1, 86400]`. **Values above 240 are not reachable in practice:** the Servy host service force-kills `Servy.Restarter.exe` after 240 seconds (4 minutes), and the Restarter logs a warning at startup when the configured value exceeds it. |
| `General Logging` | - | Uses the settings from the **General Logging** section above. |
### Desktop App Settings
| Setting | Default | Description |
| :--- | :--- | :--- |
| `ManagerAppPublishPath` | `.\Servy.Manager.exe` | Path to the Servy Manager app launched from the Desktop app. Resolved relative to the Desktop app's base directory if not absolute. |
| `General Logging` | - | Uses the settings from the **General Logging** section above. |
### Manager App Settings
| Setting | Default | Description |
| :--- | :--- | :--- |
| `RefreshIntervalInSeconds` | `4` | Refresh interval, in seconds, for the main service list. Bounded to `[1, 3600]`. |
| `PerformanceRefreshIntervalInMs` | `800` | Frequency, in milliseconds, of CPU/RAM graph updates. Bounded to `[100, 300000]`. |
| `ConsoleRefreshIntervalInMs` | `800` | Polling rate, in milliseconds, for `stdout`/`stderr` updates in the Console tab. Bounded to `[100, 300000]`. |
| `ConsoleMaxLines` | `20000` | Maximum number of `stdout`/`stderr` lines retained in the Console tab buffer. Bounded to `[100, 40000]`. |
| `DependenciesRefreshIntervalInMs` | `800` | Polling rate, in milliseconds, for the Dependencies tab. Bounded to `[100, 300000]`. |
| `MaxBulkOperationParallelism` | `8` | Maximum concurrent SCM operations during bulk tasks. Bounded to `[1, 64]`. |
| `SearchDebounceDelayMs` | `300` | Debounce window, in milliseconds, before the search box re-runs the filter in the Console tab. Bounded to `[100, 2000]`. |
| `LogsWindowDays` | `3` | Number of days of Windows Event Log history shown in the Logs tab. Bounded to `[1, 30]`. |
| `DesktopAppPublishPath` | `.\Servy.exe` | Path to the Servy desktop app launched from the Manager. Resolved relative to the Manager's base directory if not absolute. |
| `ServicesHiddenColumns` | `""` | Comma-separated list of services-grid column headers hidden in the Manager's Services tab, for example `Description,Log On As`. The Manager rewrites it when a column is shown or hidden from the column-header context menu; an empty value shows every column. Entries are matched case-insensitively against the column header text (Description, Status, Startup Type, Log On As, PID, CPU, RAM). |
| `LogsHiddenColumns` | `""` | Comma-separated list of logs-grid column headers hidden in the Manager's Logs tab, for example `Event ID,Level`. The Manager rewrites it when a column is shown or hidden from the column-header context menu; an empty value shows every column. Entries are matched case-insensitively against the column header text (`Time`, `Level`, `Event ID`). |
| `General Logging` | - | Uses the settings from the **General Logging** section above. |
> [!NOTE]
> To prevent SCM contention, the actual bulk-operation parallelism is capped by: `Math.Max(1, Math.Min(Environment.ProcessorCount * 2, MaxBulkOperationParallelism))`.
### CLI Settings
| Setting | Default | Description |
| :--- | :--- | :--- |
| `General Logging` | - | Uses the settings from the **General Logging** section above. |
## Build Configuration Examples
### Modern Build (.NET 10.0+)
**File:** `%ProgramData%\Servy\appsettings.service.json` (Servy Windows Service Wrapper)
```json
{
"Timing": {
"WaitChunkMs": 5000,
"ScmAdditionalTimeMs": 15000
},
"LogLevel": "INFO",
"EnableSizeRotation": true,
"LogRotationSizeMB": 10,
"LogRollingInterval": "Daily",
"MaxBackupLogFiles": 10,
"UseLocalTimeForRotation": true,
"EnableEventLog": true
}
```
> [!TIP]
> If you only want to disable logging to the Windows Event Log while keeping local file logging for all Servy services, create a `%ProgramData%\Servy\appsettings.service.json` file with this content:
> ```json
> {
> "EnableEventLog": false
> }
> ```
### Legacy Build (.NET Framework 4.8)
**File:** `%ProgramData%\Servy\Servy.Service.Net48.exe.config` (Servy Windows Service Wrapper)
```xml
```
### Servy Host Service (.NET 10.0+ and .NET Framework 4.8)
**Modern build file:** `%ProgramData%\Servy\appsettings.host.json`
```json
{
"LogLevel": "INFO",
"EnableSizeRotation": true,
"LogRotationSizeMB": 10,
"LogRollingInterval": "None",
"MaxBackupLogFiles": 10,
"UseLocalTimeForRotation": false,
"EnableEventLog": true
}
```
**Legacy build file:** `%ProgramData%\Servy\Servy.Host.Net48.exe.config`
```xml
```
## See Also
* [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation)
* [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager)
* [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide)
---
# Document: Logging & Log Rotation
> Source: https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#introduction)
1. [Logs in Servy Manager](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#logs-in-servy-manager)
1. [Event IDs](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#event-ids)
1. [Logging Settings](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#logging-settings)
1. [CLI Options](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#cli-options)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#cli-example)
1. [PowerShell Options](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#powershell-options)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#powershell-example)
1. [Internal Servy Logs](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation#internal-servy-logs)
## Introduction
Servy provides flexible logging to monitor your services in real time, capturing important events in Windows Event Logs, local log files at `%ProgramData%\Servy\logs\` as well as standard output and error streams (`stdout`/`stderr`). Logs can be viewed in Servy Manager, filtered by level, date, or keyword, and optionally rotated to prevent disk space issues.
## Logs in Servy Manager
- Important events are logged in **Windows Event Viewer** and can be browsed from **Servy Manager**.
- **Logs Tab**: Quickly search logs by log level, date, and keyword for easy monitoring and troubleshooting.
- **Message Format**: Each log entry starts with `[ServiceName]` followed by the message.
- Example: `[MyService] Health monitoring started.`
## Event IDs
Servy assigns specific **Event IDs** to distinguish between types of service logs. These IDs are recorded in **Windows Event Viewer** and are useful for filtering, searching, and automation.
| Level | Event ID Range | Purpose |
|-------|----------------|---------|
| Info | 1000-1099 | Lifecycle milestones (start, stop, recovery success) |
| Warning | 2000-2099 | Recoverable degradations (retries, fallback paths) |
| Error | 3000-3099 | Core errors (DPAPI failures, DB init failures, ...) |
| Error | 3100-3199 | Script / scheduled-task errors (`ServyFailureEmail.ps1` etc.) |
- 2002 = transient migration warning (pre-escalation; auto-clears on next successful read)
- 3001 = key unprotect failed
- 3003 = persistent migration failure
- 3103 = scheduled-task script error
- 3104 = scheduled-task script dependency error
> [!NOTE]
> Event IDs are per event type, not unique per entry.
## Logging Settings
The "Logging" tab in the desktop app lets you configure `stdout` and `stderr` logging, including size-based log rotation, date-based log rotation, the maximum number of rotated log files to retain, and additional options such as enabling debug logs.
> [!NOTE]
> If both size-based and date-based rotation are enabled, size-based rotation takes precedence.
> [!NOTE]
> Enabling the debug option will record sensitive information in the local log file at `%ProgramData%\Servy\logs\services\\Servy.Service.log`. This behavior only occurs when local logging is enabled, which is the default setting. Sensitive data is never recorded in the Windows Event Log. The CLI `show` command and `Show-ServyService` mask the encrypted fields by default; `--decrypt` / `-Decrypt` prints the parameters and environment variables in clear text (the stored password always stays masked). See [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI) and [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module).
> [!NOTE]
> Enabling **Console UI** (`--enableConsoleUI`) disables stdout/stderr redirection at the OS level - the child process keeps an attached console handle instead of pipes. Servy cannot capture or rotate the output in that mode, so the configured `--stdout` / `--stderr` paths will remain empty. Use one or the other, not both.
## CLI Options
- `--stdout` - stdout log path
- `--stderr` - stderr log path
- `--enableSizeRotation`
- `--rotationSize` - size in MB (1-10240)
- `--enableDateRotation`
- `--dateRotationType` - Daily, Weekly, Monthly, None (None disables date-based rotation; use when only size rotation is desired)
- `--useLocalTimeForRotation` - use local time instead of UTC (default: false)
- `--maxRotations` - number of files, 0 = unlimited (maximum 10000)
- `--debug` - Enable debug logs. Records environment variables and process parameters in `%ProgramData%\Servy\logs\services\\Servy.Service.log`. Not recommended for production.
## CLI Example
```powershell
servy-cli install `
--name="My NodeJS Service" `
--description="My NodeJS Server" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\Apps\App" `
--params="C:\Apps\App\index.js" `
--startupType="Automatic" `
--priority="Normal" `
--stdout="C:\Apps\App\stdout.log" `
--stderr="C:\Apps\App\stderr.log" `
--enableSizeRotation `
--rotationSize="10" `
--maxRotations="5" `
--debug
```
## PowerShell Options
- `-Stdout` (string)
- `-Stderr` (string)
- `-EnableSizeRotation` (switch)
- `-RotationSize` (int)
- `-EnableDateRotation` (switch)
- `-DateRotationType` (Daily, Weekly, Monthly, None - None disables date-based rotation; use when only size rotation is desired)
- `-UseLocalTimeForRotation` (switch)
- `-MaxRotations` (int, 0 = unlimited)
- `-EnableDebugLogs` - Enable debug logs. Records environment variables and process parameters in `%ProgramData%\Servy\logs\services\\Servy.Service.log`. Not recommended for production.
## PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Quiet = $true
Name = "My NodeJS Service"
Description = "My NodeJS Server"
Path = "C:\Program Files\nodejs\node.exe"
StartupDir = "C:\Apps\App"
Params = "C:\Apps\App\index.js"
StartupType = "Automatic"
Priority = "Normal"
Stdout = "C:\Apps\App\stdout.log"
Stderr = "C:\Apps\App\stderr.log"
EnableSizeRotation = $true
RotationSize = 10
MaxRotations = 5
EnableDebugLogs = $true
}
Install-ServyService @installParams
```
## Internal Servy Logs
Servy's own log files are under `%ProgramData%\Servy\logs\`:
- `logs\` - the logs of the Desktop App, Servy Manager, `servy-cli` and the `Servy` host service (`Servy.Host.log`). Only `SYSTEM` and `Administrators` can read them.
- `logs\services\\` (v10.2+) - one folder per service, holding `Servy.Service.log` (the service wrapper) and `Servy.Restarter.log`. Only that service's account, `SYSTEM` and `Administrators` can reach it. A service name that holds a character a folder name cannot hold is percent-encoded (`My:Service` logs to `logs\services\My%3AService\`); see [Security](https://github.com/aelassas/servy/wiki/Security) for the full rules.
Before v10.2, every service wrote to a single shared `logs\Servy.Service.log`. An upgrade leaves that file where it is, untouched; no service writes to it any more, and it can be deleted once it is no longer needed.
See [Advanced Configuration](https://github.com/aelassas/servy/wiki/Advanced-Configuration).
> [!TIP]
> Combine log rotation with health monitoring and pre-launch scripts to avoid disk space issues.
---
# Document: Health Monitoring & Recovery
> Source: https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#introduction)
1. [Health Monitoring Settings](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#health-monitoring-settings)
1. [CLI Options](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#cli-options)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#cli-example)
1. [PowerShell Options](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#powershell-options)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#powershell-example)
1. [How Health Monitoring Works](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#how-health-monitoring-works)
1. [Transient Detection (Memory-Based)](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#transient-detection-memory-based)
1. [Recovery Orchestration (The Gatekeeper)](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#recovery-orchestration-the-gatekeeper)
1. [Stability Verification (Persistence-Based)](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#stability-verification-persistence-based)
1. [Reboot Detection (Session Persistence)](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#reboot-detection-session-persistence)
1. [The Adaptive Stability Window](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#the-adaptive-stability-window)
1. [Heartbeat Ping URL Logic](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#heartbeat-ping-url-logic)
1. [Logic Examples & Threshold Scenarios](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#logic-examples--threshold-scenarios)
1. [Health Monitoring Logic Flow](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#health-monitoring-logic-flow)
1. [Reboot Detection Example](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#reboot-detection-example)
1. [Service Restart Detection Example](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#service-restart-detection-example)
## Introduction
Servy includes **built-in health monitoring** to ensure that your services are running correctly and recover automatically from failures.
If you are getting started, here are the core behaviors of the Servy health monitor:
- **Heartbeats are Liveness Checks:** Servy checks if your process is running and responding every **30 seconds** (default).
- **Consecutive Failures Only:** Recovery is only triggered if the service fails $N$ times **in a row**. A single successful heartbeat resets the transient failure counter to zero.
- **The "Flapping" Protection:** Servy doesn't forget failures immediately. The persistent restart counter only resets to zero after the service has been stable (running without issue) for a specific **Probationary Period**.
- **Reboot Persistence:** If Servy restarts the computer to fix a service, it **remembers** how many times it has tried to fix it across reboots. This prevents your machine from entering an infinite reboot loop.
- **Safety Stop:** Once the `MaxRestartAttempts` limit is reached, Servy stops trying, runs your optional `FailureProgram`, and leaves the service **Stopped** for manual inspection.
## Health Monitoring Settings
The "Recovery" tab in the desktop app lets you configure automated health-monitoring and failure-handling behavior. It enables continuous internal liveness checks, optional out-of-band diagnostic pings to external monitoring platforms (e.g., [healthchecks.io](https://healthchecks.io) or [Uptime Kuma](https://github.com/louislam/uptime-kuma) Push Monitors), and customizable recovery actions to keep your application running reliably without manual intervention.
Here are the available recovery options:
- **Heartbeat Interval**: optional, default is 30 seconds. This is the frequency at which Servy checks if the service is responsive. A heartbeat is an internal liveness check (process existence and state), not an application-level health probe. Minimum is 5 seconds, maximum is 86400 seconds (24 hours).
- **Max Failed Checks**: optional, default is 3. The number of consecutive failed checks allowed before triggering a recovery action. Minimum is 1, maximum is 100000.
- **Recovery Action**: optional. Defines what happens when health checks fail. Options:
- `RestartService` (default): restart the service
- `RestartProcess`: restart the process without full service restart
- `RestartComputer`: restart the host machine
- `None`: take no action
- **Recovery On Clean Exit**: optional. Triggers the defined recovery action even if the child process exits gracefully with a clean exit code (0). By default, a clean exit stops the service intentionally without triggering recovery.
- **Max Restart Attempts**: optional, default is 3. The maximum number of recovery attempts before giving up. Minimum is 0 (set to 0 for unlimited restart attempts), maximum is 100000.
- **Heartbeat URL**: optional string. An absolute HTTP/HTTPS URL (e.g., `https://hc-ping.com/uuid`) used for sending out-of-band diagnostic heartbeat pings to external monitoring services (such as healthchecks.io or Uptime Kuma).
- **Heartbeat URL Timeout**: optional integer, default is 10 seconds (range: 2-30 seconds). Timeout for the heartbeat HTTP GET request.
- **Heartbeat URL Flags**: optional switch/boolean. Appends `/start` when the service starts and `/fail` when recovery fails, to the Heartbeat URL (e.g., transforming `https://hc-ping.com/your-uuid` into `https://hc-ping.com/your-uuid/start` or `https://hc-ping.com/your-uuid/fail`).
- **Failure Program Path**: optional failure program to run after all recovery attempts have failed (and, when health-monitoring is disabled, when the child process exits with a non-zero code).
- **Failure Startup Directory**: optional failure program startup directory.
- **Failure Program Parameters**: optional failure program parameters.
If health-monitoring is disabled, the failure program will run when the **child process exits with a non-zero code**. If health-monitoring is enabled, the failure program will only run after all recovery action retries have failed. Note: **start-up failures (e.g. a missing executable path) currently do not trigger the failure program** - check Logs tab in Servy Manager or the Windows Event Log under Source `Servy` for those.
**Environment Variables:** the failure program inherits the **main service's** `EnvironmentVariables`. There is no separate `Post-Launch / Pre-Stop / Post-Stop / Failure-Program EnvironmentVariables` setting - only `Pre-Launch` supports a per-hook override.
> [!NOTE]
> `RestartService` and `RestartComputer` are not available if the service runs under `NT AUTHORITY\LocalService`, `NT AUTHORITY\NetworkService`, an IIS AppPool identity (`IIS APPPOOL\...`), or a user account without the required privileges.
> [!WARNING]
> Setting Max Restart Attempts to 0 (unlimited retries) bypasses the FailureProgram path, because the "quota reached" condition is never met.
## CLI Options
- `--enableHealth`: Enable health monitoring.
- `--heartbeatInterval`: Heartbeat interval in seconds.
- `--maxFailedChecks`: Maximum allowed failed health checks.
- `--recoveryAction`: Recovery action on failure. Options: `None`, `RestartService`, `RestartProcess`, `RestartComputer`.
- `--recoveryOnCleanExit`: Enable recovery actions even if the process exits with a clean exit code (0). (*Available starting from Servy 8.4*)
- `--maxRestartAttempts`: Maximum restart attempts on failure.
- `--heartbeatUrl`: Absolute URL for out-of-band diagnostic heartbeat pings. This is a sensitive option: prefer the `SERVY_HEARTBEAT_URL` environment variable (v10.4+), which takes precedence over `--heartbeatUrl` when both are set (see [Security](https://github.com/aelassas/servy/wiki/Security) for the full list of sensitive fields).
- `--heartbeatUrlTimeoutSeconds`: Timeout in seconds for the heartbeat URL request.
- `--enableHeartbeatUrlFlags`: Appends `/start` when the service starts and `/fail` when recovery fails, to the Heartbeat URL.
- `--failureProgramPath`: Path to an optional program to execute if all recovery attempts fail.
- `--failureProgramStartupDir`: Optional working directory for the failure program.
- `--failureProgramParams`: Optional parameters to pass to the failure program.
> [!NOTE]
> Health monitoring (`--enableHealth`) must be enabled to use `--heartbeatUrl` or recovery options. Setting recovery parameters without `--enableHealth` has no effect.
## CLI Example
```powershell
servy-cli install `
--name="MyNodeService" `
--description="My NodeJS Server" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\Apps\App" `
--params="C:\Apps\App\index.js" `
--startupType="Automatic" `
--enableHealth `
--heartbeatInterval="10" `
--maxFailedChecks="3" `
--recoveryAction="RestartProcess" `
--recoveryOnCleanExit `
--maxRestartAttempts="3" `
--heartbeatUrl="https://hc-ping.com/your-uuid-here" `
--heartbeatUrlTimeoutSeconds="5" `
--enableHeartbeatUrlFlags `
--failureProgramPath="C:\Apps\FailureHandler.exe" `
--failureProgramStartupDir="C:\Apps" `
--failureProgramParams="-log C:\Logs\failure.log"
```
## PowerShell Options
The following parameters are the PowerShell equivalents of the CLI health monitoring options when using `Install-ServyService` with splatting.
- `-EnableHealth`: Enables health monitoring for the service. This is a switch parameter.
- `-HeartbeatInterval`: Heartbeat interval in seconds.
- `-MaxFailedChecks`: Maximum number of consecutive failed health checks allowed before triggering recovery.
- `-RecoveryAction`: Recovery action on failure. Valid values: `None`, `RestartService`, `RestartProcess`, `RestartComputer`.
- `-RecoveryOnCleanExit`: Switch parameter to trigger recovery actions even on a clean process exit (exit code 0). (*Available starting from Servy 8.4*)
- `-MaxRestartAttempts`: Maximum number of recovery attempts before the service is stopped.
- `-HeartbeatUrl`: String, optional. Absolute HTTP/HTTPS URL (e.g. `https://hc-ping.com/uuid`) for heartbeat pings. `Install-ServyService` passes it to `servy-cli` (v10.4+) through the `SERVY_HEARTBEAT_URL` environment variable of the child process, not on the command line, so it is not visible in the process list. If `-HeartbeatUrl` is omitted, a `SERVY_HEARTBEAT_URL` already set in the PowerShell session is passed on.
- `-HeartbeatUrlTimeoutSeconds`: Timeout in seconds for the heartbeat URL request.
- `-EnableHeartbeatUrlFlags`: Switch parameter, optional. Appends `/start` when the service starts and `/fail` when recovery fails, to the Heartbeat URL.
- `-FailureProgramPath`: Path to an optional program to execute after all recovery attempts have failed.
- `-FailureProgramStartupDir`: Optional working directory for the failure program.
- `-FailureProgramParams`: Optional parameters passed to the failure program.
> [!NOTE]
> Health monitoring (`-EnableHealth`) must be enabled to use `-HeartbeatUrl` or recovery options. Setting recovery parameters without `-EnableHealth` has no effect.
## PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyNodeService"
Description = "My NodeJS Server"
Path = "C:\Program Files\nodejs\node.exe"
StartupDir = "C:\Apps\App"
Params = "C:\Apps\App\index.js"
StartupType = "Automatic"
EnableHealth = $true
HeartbeatInterval = 10
MaxFailedChecks = 3
RecoveryAction = "RestartService"
RecoveryOnCleanExit = $true
MaxRestartAttempts = 3
HeartbeatUrl = "https://hc-ping.com/your-uuid-here"
HeartbeatUrlTimeoutSeconds = 5
EnableHeartbeatUrlFlags = $true
FailureProgramPath = "C:\Apps\FailureHandler.exe"
FailureProgramStartupDir = "C:\Apps"
FailureProgramParams = "-log C:\Logs\failure.log"
}
Install-ServyService @installParams
```
## How Health Monitoring Works
Servy implements a **Tiered Recovery Architecture** designed to maximize uptime while protecting the host system from an infinite reboot loop. The logic is divided into three primary layers: **Transient Detection (Memory-Based)**, **Recovery Orchestration (The Gatekeeper)**, and **Stability Verification (Persistence-Based)**.
### Transient Detection (Memory-Based)
The service monitors the child process using a periodic heartbeat timer to filter out **noise** and transient glitches.
- **Failed Checks:** If the process is missing, has crashed, or exited cleanly while `RecoveryOnCleanExit` is enabled, a memory-based counter (`_failedChecks`) increments. (By default, a clean exit code of 0 stops the service intentionally).
- **Transient Reset:** If the process returns to a healthy state and the current failure count is greater than 0, the memory counter is immediately reset to `0`. This ensures that occasional missed heartbeats do not accumulate over long periods; recovery is only triggered by **consecutive** failures.
- **Persistence Sync:** When the process is healthy, the persistent `restartAttempts` counter is reset to `0` only if it is determined to be **expired**. A counter is considered expired if the time it was last written (the last recorded failure) is longer ago than the calculated **Adaptive Stability Window**. This ensures that the failure history is only cleared after a proven period of consistent uptime, rather than being wiped by a single "lucky" heartbeat from a "flapping" service.
- **Threshold:** Recovery is only triggered after the number of consecutive failures reaches `MaxFailedChecks`. This prevents unnecessary restarts due to momentary OS hiccups or slow process responses.
- **Purpose:** This dual-reset strategy ensures the watchdog is sensitive to immediate crashes but forgiving of intermittent, non-critical hiccups.
### Recovery Orchestration (The Gatekeeper)
When the failure threshold is reached, the system enters a managed **Recovery State**.
- **The Gatekeeper Pattern:** A thread-safe flag (`_isRecovering`) blocks any further health checks until the recovery action (Restart Process, Service, or Computer) has completely finished.
- **Quota Management:** Before executing recovery, the service checks a persistent counter kept by the Servy host service. If `MaxRestartAttempts` is reached, the service executes the `FailureProgram` and stops the service to allow for manual intervention.
The failure program is executed only after the persistent restart counter reaches `MaxRestartAttempts`, never during intermediate recovery attempts.
### Stability Verification (Persistence-Based)
This is the most critical layer. It manages the persistent `restartAttempts` counter for each service, which the Servy host service stores in `Servy.db` (the `RestartAttempts` and `RestartAttemptsUpdatedAtTicks` columns). Unlike the transient counter, this is **not** reset immediately upon a successful heartbeat.
- **Stability Reset:** The persistent counter is only reset to `0` once the service has been running successfully for longer than the **Adaptive Stability Window**.
- **Logic:** If the time the counter was last written is older than the calculated threshold, Servy considers the service **stable** and clears the failure history.
#### Reboot Detection (Session Persistence)
The watchdog compares the timestamp of the last restart attempt against the **System Boot Time**.
- **Logic:** If the counter was last written before the current OS session, the reset is skipped for that evaluation and its timestamp is re-anchored to the current session. The failure count survives the reboot, and the Adaptive Stability Window then starts counting fresh from boot - the counter clears only after the service stays healthy for the full window within the new session.
- **Purpose:** This ensures that if the service triggered a `RestartComputer` action, it "remembers" that attempt when the machine boots back up. This prevents a reboot from being used to bypass recovery quotas.
#### The Adaptive Stability Window
To prevent "Flapping" (where a process crashes shortly after starting), the persistent counter is only reset to `0` after the service survives a calculated **Probationary Period**.
The watchdog calculates the required stable uptime before the persistent restart counter is reset to zero. This formula balances high-frequency "flapping" protection with long-term operational sanity.
$$Threshold =
\begin{cases}
D + PreLaunchTimeout & \text{if } D > 3600 \\
\max(\min(D + \max(D, 30), 3600), D) + PreLaunchTimeout & \text{if } D \le 3600
\end{cases}$$
Where:
- **Detection Window** ($D$): `HeartbeatInterval` × `MaxFailedChecks`. The minimum time required to detect a service failure.
- **Buffer:** $\max(D, 30s)$. An additional safety margin to ensure the service has stabilized past its detection horizon.
- **$3600$ (Operator Sanity Cap):** A 1-hour limit (3600 seconds) to ensure administrators do not have to wait indefinitely for a counter reset on slow-pulse services.
- **Safety Floor** ($D$): Ensures that the final reset threshold is never shorter than the detection window itself, guaranteeing the service survives at least one full health cycle before clearing its fault history.
- **Pre-Launch Timeout:** If pre-launch is enabled, the threshold is extended by `PreLaunchTimeoutInSeconds` to prevent slow initialization cycles from consuming the stability evaluation budget.
### Heartbeat Ping URL Logic
When `HeartbeatUrl` is configured and health monitoring is enabled, Servy transmits asynchronous HTTP GET pings to the configured external monitoring endpoint according to the following rules:
- **Service Lifecycle Start:** Inside the startup sequence, if extended flags are enabled (`EnableHeartbeatUrlFlags`), Servy appends `/start` to the `HeartbeatUrl` (e.g., `https://hc-ping.com/your-uuid/start`).
- **Health Check Loop Pass (steady state):** When a health check finds the process healthy and the consecutive-failure counter is already `0`, Servy pings the exact base `HeartbeatUrl` (e.g., `https://hc-ping.com/your-uuid`).
- **Health Check Loop Pass (recovered):** When a health check finds the process healthy after one or more failed checks and extended flags are enabled (`EnableHeartbeatUrlFlags`), Servy sends `/start` instead of the bare URL, signalling that the service came back. With flags disabled, no ping is sent on that check; the bare ping resumes on the next healthy check.
- **Health Check Loop Fail / Process Crash:** Right before triggering configured recovery actions, upon process crash, or when all restart attempt quotas have been exhausted, if extended flags are enabled (`EnableHeartbeatUrlFlags`), Servy appends `/fail` to the `HeartbeatUrl` and sends the ping (e.g., `https://hc-ping.com/your-uuid/fail`).
### Logic Examples & Threshold Scenarios
The following table demonstrates how the watchdog adapts to different heartbeat configurations:
| Heartbeat Interval | Max Failed Checks | Detection Window ($D$) | Buffer (max(D, 30s)) | Final Reset Threshold | Strategy Applied |
| --- | --- | --- | --- | --- | --- |
| **5 Seconds** | 3 | 15 Seconds | 30 Seconds | **45 Seconds** | **Buffered:** $D$ + 30s minimum safety margin. |
| **5 Seconds** | 1 | 5 Seconds | 30 Seconds | **35 Seconds** | **Buffered:** $D$ + 30s minimum safety margin. |
| **10 Seconds** | 3 | 30 Seconds | 30 Seconds | **60 Seconds** | **Proportional:** $D$ + 30s minimum safety margin. |
| **1 Minute** | 3 | 3 Minutes ($180s$) | 3 Minutes ($180s$) | **6 Minutes** ($360s$) | **Proportional:** Standard $2 \times D$ window. |
| **15 Minutes** | 3 | 45 Minutes ($2700s$) | 45 Minutes ($2700s$) | **60 Minutes** ($3600s$) | **Capped:** 1-hour limit for operator sanity. |
| **1 Hour** | 3 | 3 Hours ($10800s$) | 3 Hours ($10800s$) | **3 Hours** ($10800s$) | **Floored (not recommended):** Detection window exceeds cap; overrides to $D$. |
| **1 Day** | 3 | 3 Days ($259200s$) | 3 Days ($259200s$) | **3 Days** ($259200s$) | **Floored (not recommended):** Detection window exceeds cap; overrides to $D$. |
> [!WARNING]
> **Floored Configurations:** If `HeartbeatInterval × MaxFailedChecks` exceeds 3600 seconds (1 hour), the detection window itself exceeds the reset cap. Servy falls back to using $D$ as the threshold and logs a warning on every stability evaluation. Keep `HeartbeatInterval × MaxFailedChecks` at or below 3600 seconds to stay within the recommended operational range.
**Pre-Launch Considerations:**
If a pre-launch script is configured (`PreLaunchExecutablePath` is set), the stability timer does not effectively start until the process has completed its initialization phase.
For example, if a service has a **1-minute detection window** ($60\text{s}$), its base stability requirement is **2 minutes** ($120\text{s}$). If it takes **5 minutes** ($300\text{s}$) to load its database dependencies (`PreLaunchTimeout`), the final reset threshold will be **7 minutes** ($420\text{s}$). This configuration architecture insulates the execution paths, ensuring warm-up latency never penalizes stability tracking.
### Health Monitoring Logic Flow
For every heartbeat cycle, Servy follows this decision-making process:
1. **The Heartbeat Check**
- Servy checks the liveness of the child process (process existence and responsiveness).
2. **IF HEALTHY:**
- **Transient Reset:** The memory-based `_failedChecks` counter is immediately reset to `0`.
- **Ping URL:** If `HeartbeatUrl` is configured, Servy pings the bare URL when the failure counter was already `0`. When the process has just recovered from one or more failed checks, it sends `/start` instead if `EnableHeartbeatUrlFlags` is enabled, and no ping otherwise (see [Heartbeat Ping URL Logic](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#heartbeat-ping-url-logic)).
- **Stability Check:** Servy compares the current time against the time the `restartAttempts` counter was last written.
- **Persistence Reset:** If the duration exceeds the **Adaptive Stability Window**, the persistent `restartAttempts` counter is reset to `0`.
3. **IF UNHEALTHY:**
- **Transient Increment:** The memory-based `_failedChecks` counter increments.
- **Threshold Check:** If `_failedChecks < MaxFailedChecks`, the cycle ends (waiting for the next heartbeat).
- **Gatekeeper Check:** If the threshold is met, Servy checks if a recovery is already in progress (`_isRecovering`). If true, the cycle ends to prevent overlapping recovery actions.
4. **RECOVERY ORCHESTRATION:**
- **Failure Signal:** If `EnableHeartbeatUrlFlags` is enabled, Servy sends a `/fail` HTTP GET ping to `HeartbeatUrl`.
- **Quota Check:** Servy reads the persistent `restartAttempts` counter from the Servy host service.
- **Failure State:** If `restartAttempts >= MaxRestartAttempts`, the recovery limit is reached. Servy executes the `FailureProgram` (if configured) and **stops the service** to allow for manual intervention.
- **Action State:** If the limit is not reached:
- Increment the persistent `restartAttempts` counter. The host stamps the write with the current UTC time.
- Set `_isRecovering = true`.
- Execute the configured `RecoveryAction` (Restart Process, Service, or Computer).
- **Reboot Persistence:** If the action was `RestartComputer`, the persistent timestamp is kept in `Servy.db`. Upon system reboot, the "Stability Check" (Step 2) will see the recent timestamp and preserve the failure count, preventing a reboot loop.
### Reboot Detection Example
Consider a service configured with:
- **Heartbeat Interval**: 10 seconds
- **Max Failed Checks**: 3
- **Max Restart Attempts**: 3
- **Recovery Action**: `RestartComputer`
- **Failure Program**: `C:\Apps\FailureHandler.exe`
#### Timeline
**Session 1: First crash sequence**
1. **12:00** Service starts normally. `restartAttempts = 0`
2. **12:05** Service crashes repeatedly and exceeds `MaxFailedChecks`
- Servy triggers `RestartComputer`
- `restartAttempts` is incremented to 1 and persisted
- `restartAttempts` increments before attempting the recovery action
3. **12:06** System reboots
**Session 2: Second crash sequence**
4. **12:10** Windows finishes reboot. Servy starts automatically
- Loads `restartAttempts = 1`
- The counter was last written before the current OS boot time, reboot detection prevents reset
5. **12:15** Service crashes again
- Servy triggers `RestartComputer`
- `restartAttempts` increments to 2
6. **12:16** System reboots again
**Session 3: Third crash sequence**
7. **12:20** Windows finishes reboot. Servy starts automatically
- Loads `restartAttempts = 2`
8. **12:25** Service crashes
- Servy triggers `RestartComputer`
- `restartAttempts` increments to 3
9. **12:26** System reboots again
**Session 4: Fourth crash sequence**
10. **12:30** Windows finishes reboot. Servy starts automatically
- Loads `restartAttempts = 3` (already reached `MaxRestartAttempts`)
11. **12:35** Service crashes again
- Servy sees that `restartAttempts` has reached the `MaxRestartAttempts` limit
- Recovery action is not executed
- Configured failure program is executed (`C:\Apps\FailureHandler.exe`)
- Servy stops the service to prevent infinite reboot loops
#### Key Points
- `restartAttempts` persists across reboots. Each reboot increments it only once per recovery action
- `MaxRestartAttempts` limit prevents endless reboot loops
- After reaching the limit, Servy runs the failure program if configured and stops the service
- Operators can manually intervene after the fourth crash
### Service Restart Detection Example
Consider a service configured with:
- **Heartbeat Interval**: 10 seconds
- **Max Failed Checks**: 3
- **Max Restart Attempts**: 3
- **Recovery Action**: `RestartService`
- **Failure Program**: `C:\Apps\FailureHandler.exe`
#### Timeline
**Session 1: First crash sequence**
1. **12:00** Service starts normally. `restartAttempts = 0`
2. **12:05** Service crashes repeatedly and exceeds `MaxFailedChecks`
- Servy triggers `RestartService`
- `restartAttempts` is incremented to 1 and persisted
- `restartAttempts` increments before attempting the recovery action
3. **12:06** Service successfully restarts
**Session 2: Second crash sequence**
4. **12:10** Service crashes again
- Servy triggers `RestartService`
- `restartAttempts` increments to 2
5. **12:11** Service successfully restarts
**Session 3: Third crash sequence**
6. **12:15** Service crashes again
- Servy triggers `RestartService`
- `restartAttempts` increments to 3
7. **12:16** Service successfully restarts
**Session 4: Fourth crash sequence**
8. **12:20** Service crashes again
- Servy sees that `restartAttempts` has reached the `MaxRestartAttempts` limit
- Recovery action is **not** executed
- Configured failure program is executed (`C:\Apps\FailureHandler.exe`)
- Servy stops the service to prevent endless restart loops
---
# Document: Environment Variables
> Source: https://github.com/aelassas/servy/wiki/Environment-Variables
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Environment-Variables#introduction)
1. [Environment Variable Expansion](https://github.com/aelassas/servy/wiki/Environment-Variables#environment-variable-expansion)
1. [GUI](https://github.com/aelassas/servy/wiki/Environment-Variables#gui)
1. [CLI Option: `--envVars`](https://github.com/aelassas/servy/wiki/Environment-Variables#cli-option---envvars)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Environment-Variables#cli-example)
1. [PowerShell Option: `-EnvVars`](https://github.com/aelassas/servy/wiki/Environment-Variables#powershell-option--envvars)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Environment-Variables#powershell-example)
1. [Tips](https://github.com/aelassas/servy/wiki/Environment-Variables#tips)
## Introduction
Servy allows you to define **environment variables** for the service process, enabling fine-grained control over the runtime context.
**Environment variable expansion** is supported in multiple areas of Servy configuration:
- Service **binary paths** (`--path` / `-Path`)
- **Startup directories** (`--startupDir` / `-StartupDir`)
- **Process parameters** (`--params` / `-Params`)
- **Environment variables** (`--envVars` / `-EnvVars`)
This makes it possible to reference existing system variables, user-defined variables, or paths dynamically in your configuration.
> [!NOTE]
> All hooks inherit the **main service's** `EnvironmentVariables` except `Pre-Launch` hook. There is no separate `Post-Launch / Pre-Stop / Post-Stop / Failure-Program EnvironmentVariables` setting - only `Pre-Launch` supports a per-hook override.
## Environment Variable Expansion
Servy uses a sophisticated expansion engine that allows variables to reference each other. The resolution follows this specific order:
1. **System Environment:** All current system and process environment variables are loaded first. Since v10.2, the service first brings them up to date with the System variables in the registry (`HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment`), so a System variable **added, changed or removed** in Windows is seen without restarting the machine - see the note below.
2. **Custom Overrides:** Variables defined in the **Environment Variables** field (or `--envVars`) are added next. If a custom variable has the same name as a system variable, the **custom value wins**.
3. **Cross-Reference Expansion:** Finally, all variables are scanned for `%VAR%` placeholders. These placeholders are resolved using the final merged set of variables.
> [!NOTE]
> **New, changed and removed System variables (v10.2+).** Windows gives a process its environment when it starts, and a Windows service inherits the environment of the Service Control Manager, which is only built when the machine starts. Since v10.2, whenever Servy expands a `%VAR%` placeholder - in the paths, startup directories, parameters and environment variables above - it first reads the System variables from the registry and applies what changed. The read is read-only and needs no administrator rights, so it works for the service under any account and for the Desktop App, Servy Manager and the CLI alike.
>
> - **The service** (`Servy.Service.exe`): every System variable takes its current registry value, so an added, changed or removed variable reaches the service and its process the next time the service (or a hook) starts its process - restarting the service is enough, the machine does not need a restart. A variable also defined as a **User** variable of the service account keeps the User value, as in Windows, and `Path` is the System `Path` followed by the user's.
> - **The Desktop App, Servy Manager and the CLI**: the System variables are recorded when the app starts. A variable added later is picked up; a variable changed or removed later is picked up as long as the app still holds the value it started with. A value the app got from a **User** variable, or from the program or console that started it, is kept.
> - Variables Windows sets per account (such as `USERNAME`, `USERPROFILE`, `APPDATA`) are never taken from the System key. User variables changed after start are not picked up; restart the app for those.
### Protected Variables
For security reasons, Servy restricts `--envVars` / `-EnvVars` and `--preLaunchEnv` / `-PreLaunchEnv` from overriding a core set of variables that would otherwise enable privilege escalation, runtime injection (DLL/JIT profiler hijacking, debugger probes, search-path attacks, etc.), or process hijacking.
While a user with sufficient privileges to install services already has full system access, blocking overrides of protected variables provides an additional layer of safety, improves auditability, and helps prevent unintended side effects. If an attacker has enough privileges to compromise the system, they are forced to make changes through standard Windows administration tools that trigger standard system logs and EDR alerts. **They cannot use Servy as a silent, stealthy mechanism for local process injection or persistence.**
Variables are divided into two distinct security tiers:
1. **Immutable Core System Variables (Tier 1):** Essential operating system integrity boundaries that **can never be overridden** under any circumstances.
2. **Overridable Runtime Variables (Tier 2):** Application, framework, and diagnostic variables blocked by default to prevent injection, but which can be overridden on a per-service basis if explicitly permitted by configuration policy (`AllowOverriddenRuntimeVars`).
The setting applies to the main process and to every hook (pre-launch, post-launch, pre-stop, post-stop and the failure program), including the pre-launch environment variables.
Any attempt to override a blocked variable is ignored **each time the service starts** (during environment-variable expansion), and a warning is recorded in `%ProgramData%\Servy\logs\services\\Servy.Service.log`:
```text
Security: Blocked an attempt to override protected runtime variable 'JAVA_OPTS'. Custom values for this variable are ignored to prevent runtime injection. Enable 'AllowOverriddenRuntimeVars' in service settings to allow this override.
```
A Tier 1 variable is always ignored, with this warning:
```text
Security: Blocked an attempt to override immutable system variable 'PATH'. Custom values for this variable are ignored to prevent core OS instability or privilege escalation.
```
#### Tier 1: Immutable Core System Variables (Never Overridable)
These variables protect core Windows operating system paths, shell binaries, profile identity scopes, and security policy boundaries. They remain strictly untouchable regardless of configuration settings:
| Category | Environment Variables | Security / Integrity Purpose |
| --- | --- | --- |
| **System Integrity** | `PATH`, `COMSPEC`, `SYSTEMROOT`, `WINDIR`, `SYSTEMDRIVE`, `TEMP`, `TMP`, `PATHEXT`, `PROGRAMFILES`, `PROGRAMFILES(X86)`, `PROGRAMW6432`, `COMMONPROGRAMFILES`, `COMMONPROGRAMFILES(X86)`, `COMMONPROGRAMW6432` | Protects core operating system paths, shell execution binaries, executable extension resolution rules, and system-wide temporary layout storage areas from redirection or manipulation. |
| **Profile & Identity Redirection** | `APPDATA`, `LOCALAPPDATA`, `PUBLIC`, `HOMEDRIVE`, `HOMEPATH`, `HOME`, `USERDOMAIN`, `USERDOMAIN_ROAMINGPROFILE`, `LOGONSERVER` | Prevents the malicious redirection of active user profile locations, local cache repositories, home path scopes, or domain network logons which could allow credential harvesting or state poisoning. |
| **User & Profile Integrity** | `USERNAME`, `USERPROFILE`, `ALLUSERSPROFILE`, `PROGRAMDATA`, `PSMODULEPATH` | Safeguards system configuration and initialization boundaries, tracking contexts, and default PowerShell utility module path discovery scopes from arbitrary interference. |
| **Windows AppCompat & Symbol Debugging** | `__COMPAT_LAYER`, `SHIM_FILE_LOG`, `SHIM_DEBUG_LEVEL`, `_NT_SYMBOL_PATH`, `_NT_ALT_SYMBOL_PATH`, `_NT_SOURCE_PATH`, `MICROSOFT_TELEMETRY_ENV_OVERRIDE` | Hardens execution trees against debugging diagnostics vectors, system symbol repository redirections, and application compatibility shim injection layer techniques. |
| **Global / Unix Compatibility** | `LD_PRELOAD`, `LD_LIBRARY_PATH`, `LD_AUDIT` | Controls dynamic tracking or link-editor behaviors within Unix compatibility frameworks, MinGW subsystems, WSL boundaries, or Cygwin sandboxes to stop arbitrary binary instrumentation. |
| **PowerShell Hardening Bypass** | `__PSLockDownPolicy`, `PSExecutionPolicyPreference` | Mitigates unauthorized administrative policy changes at process initialization by blocking programmatic overrides targeting Windows execution restrictions or the system LanguageMode. |
#### Tier 2: Overridable Runtime Variables (Blocked by Default)
These variables control framework execution options, classpaths, diagnostic attaches, and runtime environments. They are blocked by default to prevent runtime injection, but can be unlocked on a per-service basis:
| Category | Environment Variables | Security / Integrity Purpose |
| --- | --- | --- |
| **.NET Runtime Injection & Diagnostics** | `COR_ENABLE_PROFILING`, `COR_PROFILER`, `COR_PROFILER_PATH`, `CORECLR_ENABLE_PROFILING`, `CORECLR_PROFILER`, `CORECLR_PROFILER_PATH`, `DOTNET_STARTUP_HOOKS`, `DOTNET_ROOT`, `DOTNET_ROOT(x86)`, `DOTNET_HOST_PATH`, `DOTNET_BUNDLE_EXTRACT_BASE_DIR`, `DOTNET_ADDITIONAL_DEPS`, `DOTNET_SHARED_STORE`, `DOTNET_DiagnosticPorts`, `COMPlus_DiagnosticPorts`, `DOTNET_EnableDiagnostics`, `COMPlus_EnableDiagnostics`, `DOTNET_EnableDiagnostics_IPC`, `COMPlus_EnableDiagnostics_IPC`, `DOTNET_EnableDiagnostics_Profiler`, `COMPlus_EnableDiagnostics_Profiler`, `DOTNET_EnableEventPipe`, `COMPlus_EnableEventPipe`, `DOTNET_GCName`, `COMPlus_GCName`, `DOTNET_GCPath`, `COMPlus_GCPath`, `DOTNET_LegacyHostPolicy`, `COMPlus_LegacyHostPolicy`, `DOTNET_LegacyTransform`, `COMPlus_LegacyTransform`, `DOTNET_PerfMapEnabled`, `COMPlus_PerfMapEnabled`, `DOTNET_ZapDisable`, `COMPlus_ZapDisable`, `DOTNET_DbgEnableMiniDump`, `COMPlus_DbgEnableMiniDump`, `DOTNET_DbgMiniDumpName`, `COMPlus_DbgMiniDumpName`, `DOTNET_DbgMiniDumpType`, `COMPlus_DbgMiniDumpType` | Shuts down execution hijacking and process attachment pathways targeting legacy, modern, and cross-compiled `.NET`/`CoreCLR` runtimes. This blocks unauthorized remote diagnostic socket endpoint exposure (`DiagnosticPorts`), runtime profiler/assembly DLL injection setups, custom Garbage Collector (`GCPath`) hijacking vectors, and the redirection of sensitive process memory dumps (`DbgMiniDumpName`) to insecure disk areas. |
| **Java Injection** | `JAVA_TOOL_OPTIONS`, `_JAVA_OPTIONS`, `JDK_JAVA_OPTIONS`, `JAVA_OPTS`, `JAVA_OPTIONS`, `CATALINA_OPTS`, `CATALINA_JAVA_OPTS`, `MAVEN_OPTS`, `M2_OPTS`, `GRADLE_OPTS`, `ANT_OPTS`, `JBOSS_JAVA_OPTS`, `WILDFLY_OPTS`, `CLASSPATH`, `JAVA_HOME`, `JRE_HOME`, `JDK_HOME` | Blocks execution exploits (such as malicious `-javaagent` arguments) loaded through native JVM diagnostics (`java.exe` launcher flags) or shell-wrapper scripts commonly deployed in enterprise application frameworks like Tomcat, JBoss, Maven, and Gradle. |
| **Node.js & NPM Injection** | `NODE_OPTIONS`, `NODE_PATH`, `NODE_EXTRA_CA_CERTS`, `NPM_CONFIG_PREFIX`, `NPM_CONFIG_USERCONFIG`, `NPM_CONFIG_GLOBALCONFIG` | Defends Node.js runtime instances against preload option injections, local dependency resolution adjustments, rogue Certificate Authority additions (preventing Man-In-The-Middle traffic decryption), and npm registry config tampering. |
| **TLS Trust Store & OpenSSL Configuration** | `OPENSSL_CONF`, `OPENSSL_MODULES`, `SSL_CERT_FILE`, `SSL_CERT_DIR`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, `NO_PROXY` | Prevents arbitrary code execution via OpenSSL dynamic engine/provider module loading (`OPENSSL_CONF`, `OPENSSL_MODULES`), blocks silent Man-In-The-Middle (MITM) traffic decryption by preventing malicious overriding or swapping of trusted CA certificate bundles, and stops outbound HTTP/HTTPS network traffic redirection through attacker-controlled proxies. |
| **Python Injection** | `PYTHONSTARTUP`, `PYTHONPATH`, `PYTHONHOME`, `PYTHONUSERBASE`, `PYTHONEXECUTABLE` | Prevents arbitrary script parsing or site-package source location hijacking through specialized system initialization hooks, site library layout directories, or custom interpreter path redirections. |
| **Ruby & Perl Injection** | `RUBYOPT`, `RUBYLIB`, `PERL5OPT`, `PERL5LIB`, `PERLLIB` | Disallows option mapping switches and internal code inclusion path manipulations within local Ruby and Perl execution setups. |
| **PHP Injection** | `PHPRC`, `PHP_INI_SCAN_DIR` | Blocks attackers from supplying custom configuration directives or dynamically scanning arbitrary directories for malicious extension modules via modified initialization file search paths. |
#### Overriding Tier 2 Runtime Variables
If your application requires custom runtime options (such as custom heap memory limits in `JAVA_OPTS` or `CATALINA_OPTS` for Apache Tomcat), you can explicitly permit Tier 2 overrides for the service:
- **Desktop Configurator UI:** Navigate to the **Advanced** tab -> check **Allow Overridden Runtime Environment Variables (e.g., JAVA_HOME, JAVA_OPTS)**. The **See list of runtime variables.** link below it opens this section.
- **CLI:** Pass the `--allowOverriddenRuntimeVars` flag when installing the service (to change the setting on an existing service, run `install` again):
```cmd
servy-cli install --name="TomcatService" --path="cmd.exe" --params="/c C:\Tomcat\bin\catalina.bat run" --envVars="JAVA_HOME=C:\JDK21;CATALINA_OPTS=-Xmx2048m" --allowOverriddenRuntimeVars
```
- **PowerShell:** Pass the `-AllowOverriddenRuntimeVars` switch to `Install-ServyService`.
- **Import/Export:** Set `AllowOverriddenRuntimeVars` to `true` in the XML or JSON configuration (see [Export/Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services)).
When enabled, Servy logs an audit record (Info level) for each overridden variable every time the service's process or one of its hooks is started, confirming that the runtime variable override was explicitly authorized:
```text
Security Audit: Override for protected runtime variable 'CATALINA_OPTS' permitted by service policy configuration.
```
> [!NOTE]
> Tier 1 Core System Variables (such as `PATH`, `SYSTEMROOT`, or `COMSPEC`) remain strictly protected and cannot be overridden even when `AllowOverriddenRuntimeVars` is enabled. If you need to extend `PATH` for a service, modify the System environment variables or reference custom paths directly in process parameters.
### Example of Expansion Logic
If your system has `TEMP=C:\Windows\Temp` and you define:
- `MY_ROOT=C:\ServyApp`
- `MY_LOGS=%MY_ROOT%\logs`
- `APP_TEMP=%TEMP%`
The final environment seen by your process will be:
- `MY_ROOT`: `C:\ServyApp`
- `MY_LOGS`: `C:\ServyApp\logs`
- `APP_TEMP`: `C:\Windows\Temp`
> [!NOTE]
> **Variable Ordering and Circular References:**
> Variables are resolved using a multi-pass fixed-point algorithm, so the **order in which you define them does not matter** - `MY_LOGS=%MY_ROOT%\logs` resolves correctly even if `MY_ROOT` is defined later in the list. The expansion engine runs up to 5 passes (`AppConfig.MaxEnvVarExpansionPasses`); chains deeper than that will leave unresolved `%VAR%` placeholders with **no warning logged** - the loop exits silently once the pass cap is reached while values are still changing.
>
> **Avoid circular references:**
> - **Self-reference** (`A=%A%`): caught as soon as the expansion engine encounters a value that contains its own `%NAME%` token. Logged as `Direct cycle detected for variable 'A'; leaving literal placeholder.` and the placeholder is left literal (unless the OS already exports a value for `A`, in which case the inherited value is substituted to mimic Windows `PATH`-append semantics).
> - **Multi-variable cycles** (`A=%B%`, `B=%A%`, or longer chains like `A=%B%`, `B=%C%`, `C=%A%`): not detected by a dedicated check. Two-variable or indirect cycles typically degenerate into a direct self-reference (`%A%` inside `A`) during intermediate resolution and surface as `Direct cycle detected for variable 'A'` on a subsequent pass. If a cycle does not degenerate within 5 passes, resolution halts silently when the pass limit is reached, leaving the remaining `%VAR%` placeholders intact in the expanded environment.
## GUI
The advanced tab in Servy allows setting environment variables for the service process:
## CLI Option: `--envVars`
The `--envVars` command-line option lets you specify environment variables for the service process.
- **Syntax:** `--envVars="VAR1=value1; VAR2=value2"`
- Separate multiple variables with **semicolons (;)** - Special characters can be escaped:
- `\=` to escape `=`
- `\"` to escape `"`
- `\;` to escape `;`
- `\\` to escape `\`
- `%%` to escape `%` (collapses to a single `%`, matching `cmd.exe` behaviors) (starting from v8.5+)
- Supports environment variable expansion. Example:
`--envVars="VAR1=%ProgramData%\MyApp; VAR2=%VAR1%\bin; CHANCE=100%%"`
- Useful for setting runtime context without changing system-wide environment variables.
## CLI Example
```powershell
servy-cli install `
--name="MyNodeService" `
--description="My NodeJS Server" `
--path="%ProgramFiles%\nodejs\node.exe" `
--startupDir="C:\Apps\App" `
--params="C:\Apps\App\index.js" `
--startupType="Automatic" `
--envVars="NODE_ENV=production; APP_CONFIG=C:\Apps\App\config.json"
```
## PowerShell Option: `-EnvVars`
The `-EnvVars` parameter lets you define environment variables when installing a service via PowerShell.
- **Type:** `string` (semicolon-separated list, optional)
- Apply the same escaping rules as the CLI.
- Multiple variables are separated with **semicolons (;)**
- Variables are applied **only to the service process**, not system-wide.
## PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyNodeService"
Description = "My NodeJS Server"
Path = "C:\Program Files\nodejs\node.exe"
StartupDir = "C:\Apps\App"
Params = "C:\Apps\App\index.js"
StartupType = "Automatic"
EnvVars = "NODE_ENV=production; APP_CONFIG=C:\Apps\App\config.json"
}
Install-ServyService @installParams
```
## Tips
- **Case Insensitivity:** Environment variable names are case-insensitive. Defining `node_env` will correctly override an existing `NODE_ENV`.
- **Expansion Order:** You can reference both existing system variables (like `%ProgramData%`) and other custom variables defined in the same list.
- **Safe Percent Escaping:** To pass a literal percent character into your environment safely and prevent it from being processed as an expansion block, use a double percent sign (`%%`). For example, defining `ALERT_MSG=Battery at 100%%` will expand correctly to `Battery at 100%` inside the service process.
- **Verification:** To troubleshoot, if you aren't sure if your variables are applying correctly, run this to dump the environment to a file (PowerShell Admin):
```powershell
servy-cli install --name="EnvTest" --startupType="Manual" --path="C:\Windows\System32\cmd.exe" --params="/c set > C:\servy_env.txt && timeout /t 3600 /nobreak > nul" --envVars="MY_ROOT=C:\ServyApp; MY_LOGS=%MY_ROOT%\logs"
servy-cli restart --name="EnvTest"
Start-Sleep -Seconds 3
Get-Content C:\servy_env.txt | Select-String "MY_LOGS"
# Clean up when you are done
servy-cli uninstall --name="EnvTest"
```
---
# Document: Service Dependencies
> Source: https://github.com/aelassas/servy/wiki/Service-Dependencies
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Service-Dependencies#introduction)
1. [GUI](https://github.com/aelassas/servy/wiki/Service-Dependencies#gui)
1. [CLI Option: `--deps`](https://github.com/aelassas/servy/wiki/Service-Dependencies#cli-option---deps)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Service-Dependencies#cli-example)
1. [PowerShell Option: `-Deps`](https://github.com/aelassas/servy/wiki/Service-Dependencies#powershell-option--deps)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Service-Dependencies#powershell-example)
1. [Dependency Tree Visualization](https://github.com/aelassas/servy/wiki/Service-Dependencies#dependency-tree-visualization)
## Introduction
Servy supports optional service dependencies, allowing a service to start only after one or more other Windows services are running.
Enter each service this service depends on by its service name (not the display name) on a new line or separate them with semicolons (`;`).
This ensures that your service starts in the correct order and avoids errors if required services are not yet running.
## GUI
The advanced tab provides additional configuration options such as service dependencies.
## CLI Option: `--deps`
The `--deps` command-line option lets you specify one or more Windows service dependencies when installing a service via the CLI.
* **Syntax:** `--deps="Service1; Service2; Service3"`
* Windows starts the listed services first (automatic-start dependencies are started on demand); if any dependency fails to start or is disabled, the service will not start.
* Multiple dependencies can be separated with **semicolons**.
* This is especially useful for applications that rely on databases, message brokers, or other background services.
## CLI Example
```powershell
servy-cli install `
--name="MyNodeService" `
--description="My NodeJS Server" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\Apps\App" `
--params="C:\Apps\App\index.js" `
--startupType="Automatic" `
--deps="MongoDB; MySQL80"
```
## PowerShell Option: `-Deps`
The `-Deps` parameter lets you specify one or more Windows service dependencies when installing a service via PowerShell.
* **Type:** `string` (semicolon-separated list, optional)
* Windows starts the listed services first (Automatic-start dependencies are started on demand); if any dependency fails to start or is disabled, the service will not start.
* Multiple dependencies can be separated with **semicolons**.
* Useful for services that rely on databases, message brokers, or other background services.
## PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyNodeService"
Description = "My NodeJS Server"
Path = "C:\Program Files\nodejs\node.exe"
StartupDir = "C:\Apps\App"
Params = "C:\Apps\App\index.js"
StartupType = "Automatic"
Deps = "MongoDB; MySQL80"
}
Install-ServyService @installParams
```
## Dependency Tree Visualization
The Dependencies tab in Servy Manager provides a visual representation of a service dependency tree retrieved from the Service Control Manager (SCM). Each dependency is displayed with its current status: a green gear for a running service, a red gear for a service that is not running, an orange circular-arrows icon for a dependency that already appears higher in the same branch (a cycle, where the branch stops), and an orange warning icon for a dependency that could not be resolved or accessed, for example a missing service or one the current account cannot query. An unavailable node shows the error in place of the service's display name. The tree can be refreshed at any time using the Refresh button or by pressing **F5**.
This view is especially useful for understanding startup and shutdown order, diagnosing why a service fails to start, and quickly identifying stopped or missing dependencies that may impact service availability.
> [!TIP]
> You can combine service dependencies with pre-launch scripts and health checks to ensure that your service starts reliably in complex environments.
---
# Document: Export / Import Services
> Source: https://github.com/aelassas/servy/wiki/Export-Import-Services
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Export-Import-Services#introduction)
1. [Export](https://github.com/aelassas/servy/wiki/Export-Import-Services#export)
1. [GUI](https://github.com/aelassas/servy/wiki/Export-Import-Services#gui)
1. [CLI](https://github.com/aelassas/servy/wiki/Export-Import-Services#cli)
1. [PowerShell](https://github.com/aelassas/servy/wiki/Export-Import-Services#powershell)
1. [Import](https://github.com/aelassas/servy/wiki/Export-Import-Services#import)
1. [Security Note](https://github.com/aelassas/servy/wiki/Export-Import-Services#security-note)
1. [Format](https://github.com/aelassas/servy/wiki/Export-Import-Services#format)
1. [XML Sample](https://github.com/aelassas/servy/wiki/Export-Import-Services#xml-sample)
1. [JSON Sample](https://github.com/aelassas/servy/wiki/Export-Import-Services#json-sample)
1. [GUI](https://github.com/aelassas/servy/wiki/Export-Import-Services#gui-1)
1. [CLI](https://github.com/aelassas/servy/wiki/Export-Import-Services#cli-1)
1. [PowerShell](https://github.com/aelassas/servy/wiki/Export-Import-Services#powershell-1)
## Introduction
Servy supports exporting and importing service configurations through the GUI, the CLI, and the PowerShell module.
All services created via Servy, whether through the GUI, CLI, or PowerShell, are stored in a dedicated SQLite database.
The GUI can export both registered and non-registered services.
The CLI and PowerShell module can export only services already registered in the Servy database.
The database is located at:
```text
%ProgramData%\Servy\db\Servy.db
```
Passwords are securely encrypted using AES with a key protected via Windows DPAPI. You can find more details on the [Security](https://github.com/aelassas/servy/wiki/Security) page.
> [!IMPORTANT]
> Passwords are not included in exports, but other sensitive fields like Parameters and EnvironmentVariables may still be present. Always handle exported files securely. Do not share them publicly, commit them to source control, or leave them in unprotected locations. Consider encrypting these files or restricting access to ensure they remain confidential.
## Export
Password and UserAccount fields are not exported; re-enter them after import for security reasons.
### GUI
* Open Servy
* Fill your service configuration
* Click on **Export** menu on the top left
* Choose your export format (XML or JSON)
* Specify the export file path and confirm
### CLI
* Run the following command to export in XML format:
```cmd
servy-cli export --name="MyRegisteredService" --config="xml" --path="C:\MyRegisteredService.xml"
```
* Run the following command to export in JSON format:
```cmd
servy-cli export --name="MyRegisteredService" --config="json" --path="C:\MyRegisteredService.json"
```
If the specified service is not found in Servy's database, the CLI will terminate with an exit code of 1.
### PowerShell
* Import Servy PowerShell module:
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
```
* Run the following script to export in XML format:
```powershell
$exportParamsXml = @{
Name = "MyRegisteredService"
ConfigFileType = "Xml"
Path = "C:\MyRegisteredService.xml"
}
Export-ServyServiceConfig @exportParamsXml
```
* Run the following script to export in JSON format:
```powershell
$exportParamsJson = @{
Name = "MyRegisteredService"
ConfigFileType = "Json"
Path = "C:\MyRegisteredService.json"
}
Export-ServyServiceConfig @exportParamsJson
```
If the specified service is not found in Servy's database, the PowerShell cmdlet will **throw an error**. You can handle this using `try/catch` to check for failure, similar to how the CLI exits with exit code 1.
## Import
> [!NOTE]
> If a password and user are needed, they must be re-entered after import.
### Security Note
> [!CAUTION]
> **Infiltration Guard: Local Import Enforcement**
>
> Importing service configurations from Universal Naming Convention (UNC) paths or through redirected paths (such as symbolic links, junctions, or mapped network drives) poses severe security risks, including attacker-controlled configuration injection, path redirection exploits, and privilege escalation.
>
> To preserve system integrity, the import pipeline explicitly blocks non-local paths using a multi-layered validation chain that inspects explicit UNC prefixes, queries drive interfaces, walks reparse point ancestors, evaluates file-level symlinks, blocks reserved device names, enforces protected directory boundaries, and verifies the canonical handle via `GetFinalPathNameByHandle`.
>
> For full technical details on these attack vectors and the defensive validation pipeline, see [Infiltration Guard: Local Import Enforcement](https://github.com/aelassas/servy/wiki/Security#infiltration-guard-local-import-enforcement) on the Security page.
### Format
You can import service configurations in XML or JSON formats.
Only the **Name** and **ExecutablePath** fields are required; all others are optional.
#### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| Name | string | **Yes** | The unique name of the service. |
| DisplayName | string? | No | The human-readable name shown in the Windows Services console. If empty, the service name is used. |
| Description | string? | No | Optional description of the service. |
| ExecutablePath | string | **Yes** | Path to the executable of the service. |
| StartupDirectory | string? | No | Optional startup directory for the service executable. |
| Parameters | string? | No | Optional parameters to pass to the service executable. |
| StartupType | int? | No | Startup type of the service (integer value from [StartupType](https://github.com/aelassas/servy/wiki/Export-Import-Services#startuptype) table). |
| Priority | int? | No | Process priority of the service (integer value from [Priority](https://github.com/aelassas/servy/wiki/Export-Import-Services#priority) table). |
| CpuAffinity | string? | No | Logical CPUs the process may run on, as a core list/range or hex mask (e.g. `0-3,8` or `0xFF00`). |
| StdoutPath | string? | No | Optional path for the standard output log. |
| StderrPath | string? | No | Optional path for the standard error log. |
| StartTimeout | int? | No | The timeout in seconds to wait for the process to start successfully before considering the startup as failed (range: `1`-`86400`). |
| StopTimeout | int? | No | The timeout in seconds to wait for the process to exit (range: `1`-`86400`). |
| EnableConsoleUI | bool? | No | When true, the wrapped process runs with a visible console window; stdout/stderr redirection is disabled. Default is false. |
| EnableSizeRotation | bool? | No | Whether size-based log rotation is enabled. |
| RotationSize | int? | No | Maximum size of the log file in Megabytes (MB) before rotation (range: `1`-`10240`). |
| EnableDateRotation | bool? | No | Whether date-based log rotation is enabled. |
| DateRotationType | int? | No | Date rotation type. (integer value from [DateRotationType](https://github.com/aelassas/servy/wiki/Export-Import-Services#daterotationtype) table). |
| UseLocalTimeForRotation | bool? | No | Whether to use local server time for log rotation instead of UTC. Default is false (UTC). |
| MaxRotations | int? | No | Max log files to keep (range: `0`-`10000`; `0` = unlimited). |
| EnableDebugLogs | bool? | No | Whether to enable debug logs in the local log file. Not recommended on production environments, as these logs may contain sensitive data. |
| EnableHealthMonitoring | bool? | No | Whether health monitoring is enabled. |
| HeartbeatInterval | int? | No | Heartbeat interval in seconds for health monitoring (range: `5`-`86400`). |
| MaxFailedChecks | int? | No | Maximum number of consecutive failed health checks before triggering recovery (range: `1`-`100000`). |
| RecoveryAction | int? | No | Recovery action (integer value from [RecoveryAction](https://github.com/aelassas/servy/wiki/Export-Import-Services#recoveryaction) table). |
| RecoveryOnCleanExit | bool? | No | Whether to trigger the recovery action even when the wrapped process exits with a clean (zero) exit code. Default is `false`. (Available since 8.4) |
| MaxRestartAttempts | int? | No | Maximum number of restart attempts if the service fails (range: `0`-`100000`; `0` = unlimited, which bypasses the failure program - see [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery)). |
| HeartbeatUrl | string? | No | Absolute URL for out-of-band diagnostic heartbeat pings. |
| HeartbeatUrlTimeoutSeconds | int? | No | Timeout in seconds for the heartbeat URL request (range: `2`-`30`). |
| EnableHeartbeatUrlFlags | bool? | No | Append /start and /fail lifecycle suffixes to heartbeat URL pings. |
| FailureProgramPath | string? | No | Optional path to the process to run on failure. |
| FailureProgramStartupDirectory | string? | No | Optional working directory for the failure program. |
| FailureProgramParameters | string? | No | Optional command-line parameters for the failure program. |
| EnvironmentVariables | string? | No | Optional environment variables, in `key=value` format separated by semicolons. |
| AllowOverriddenRuntimeVars | bool? | No | Whether the environment variables may override protected runtime (Tier 2) variables such as `JAVA_HOME` or `JAVA_OPTS` (see [Protected Variables](https://github.com/aelassas/servy/wiki/Environment-Variables#protected-variables)). Defaults to `false`. |
| ServiceDependencies | string? | No | Optional names of the services this service depends on, separated by semicolons. |
| PreLaunchExecutablePath | string? | No | Optional path to an executable that runs before the service starts. |
| PreLaunchStartupDirectory | string? | No | Optional startup directory for the pre-launch executable. |
| PreLaunchParameters | string? | No | Optional parameters for the pre-launch executable. |
| PreLaunchEnvironmentVariables | string? | No | Optional environment variables for the pre-launch executable, in `key=value` format. |
| PreLaunchStdoutPath | string? | No | Optional path for the pre-launch executable's standard output log. |
| PreLaunchStderrPath | string? | No | Optional path for the pre-launch executable's standard error log. |
| PreLaunchTimeoutSeconds | int? | No | Maximum time in seconds to wait for the pre-launch executable to complete (range: `0`-`86400`; `0` = fire-and-forget). |
| PreLaunchRetryAttempts | int? | No | Maximum number of retry attempts for the pre-launch executable (range: `0`-`100000`). |
| PreLaunchIgnoreFailure | bool? | No | Whether to ignore failure of the pre-launch executable. |
| PostLaunchExecutablePath | string? | No | Optional path to an executable that runs after the process starts successfully. |
| PostLaunchStartupDirectory | string? | No | Optional startup directory for the post-launch executable. |
| PostLaunchParameters | string? | No | Optional parameters for the post-launch executable. |
| PreStopExecutablePath | string? | No | Optional path to an executable that runs before the service stops. |
| PreStopStartupDirectory | string? | No | Optional startup directory for the pre-stop executable. |
| PreStopParameters | string? | No | Optional parameters for the pre-stop executable. |
| PreStopTimeoutSeconds | int? | No | Maximum time in seconds to wait for the pre-stop executable to complete (range: `0`-`86400`; `0` = fire-and-forget). |
| PreStopLogAsError | bool? | No | Whether to log pre-stop failure as error. |
| PostStopExecutablePath | string? | No | Optional path to an executable that runs after the service stops. |
| PostStopStartupDirectory | string? | No | Optional startup directory for the post-stop executable. |
| PostStopParameters | string? | No | Optional parameters for the post-stop executable. |
> [!IMPORTANT]
> `UserAccount`, `Password`, and `RunAsLocalSystem` are never exported. On import, any custom identity in the file is **discarded** (the service is reset to a password-less **LocalSystem** baseline) and an informational notice is logged; re-enter the account and password manually after import. `Pid`, `PreviousStopTimeout`, `ActiveStdoutPath`, `ActiveStderrPath`, `RestartAttempts` and `RestartAttemptsUpdatedAtTicks` are silently ignored and cannot be set through an import file.
#### StartupType
| Value | Name | Description |
|-------|-----------|-------------|
| 2 | Automatic | The service starts automatically by the Service Control Manager during system startup. |
| 3 | Manual | The service must be started manually by the user or an application. |
| 4 | Disabled | The service is disabled and cannot be started. |
| 5 | AutomaticDelayedStart | The service starts automatically, but with a delay after other auto-start services. |
#### Priority
| Value | Name | Description |
|-------|-------------|-------------|
| 0 | Idle | The process runs only when the system is idle and other processes are not using the CPU. This is the lowest priority level. |
| 1 | BelowNormal | The process has below normal priority, less than normal but higher than idle. |
| 2 | Normal | The process has normal priority, which is the default priority for processes. |
| 3 | AboveNormal | The process has above normal priority, higher than normal but lower than high. |
| 4 | High | The process has high priority, it receives more CPU time compared to normal priority. |
| 5 | RealTime | The process has real-time priority, the highest priority. Use with caution as it can monopolize CPU resources and starve other processes. |
#### DateRotationType
| Value | Name | Description |
|-------|---------|-------------|
| 0 | Daily | Rotates the log file once per calendar day (UTC by default; local if `UseLocalTimeForRotation` is true). |
| 1 | Weekly | Rotates the log file once per calendar week (UTC by default; local if `UseLocalTimeForRotation` is true; FirstFourDayWeek, Monday as first day; also rotates on 1 January). |
| 2 | Monthly | Rotates the log file once per calendar month (UTC by default; local if `UseLocalTimeForRotation` is true). |
| 3 | None | Disables date-based rotation. Use this when only size-based rotation is desired. |
#### RecoveryAction
| Value | Name | Description |
|-------|------------------|-------------|
| 0 | None | No action will be taken. |
| 1 | RestartService | Restart the service. |
| 2 | RestartProcess | Restart the process. |
| 3 | RestartComputer | Restart the computer. |
### XML Sample
```xml
MyTestService
My Test Service
Sample service for testing import
C:\Program Files\TestService\TestService.exe
C:\Program Files\TestService
-arg1 -arg2
2
1
0-3,8
C:\Logs\TestService_out.log
C:\Logs\TestService_err.log
10
5
true
10
false
0
0
false
false
false
true
30
3
1
5
https://hc-ping.com/your-uuid
5
true
false
C:\Program Files\nodejs\node.exe
C:\Apps\Notify
C:\Apps\Notify\index.js
APP_ENV=production;APP_CONFIG=C:\Apps\App\config.json
false
ServiceA;ServiceB
C:\Program Files\TestService\PreLaunch.exe
C:\Program Files\TestService
-preArg1 -preArg2
CONFIG=C:\Config;LOGS=C:\Logs
C:\Logs\PreLaunch_out.log
C:\Logs\PreLaunch_err.log
60
2
true
C:\Program Files\TestService\PostLaunch.exe
C:\Program Files\TestService
-postArg1 -postArg2
C:\Program Files\TestService\PreStop.exe
C:\Program Files\TestService
-stopArg1 -stopArg2
30
true
C:\Program Files\TestService\PostStop.exe
C:\Program Files\TestService
-stopArg1 -stopArg2
```
### JSON Sample
```json
{
"Name": "MyTestService",
"DisplayName": "My Test Service",
"Description": "Sample service for testing import",
"ExecutablePath": "C:\\Program Files\\TestService\\TestService.exe",
"StartupDirectory": "C:\\Program Files\\TestService",
"Parameters": "-arg1 -arg2",
"StartupType": 2,
"Priority": 1,
"CpuAffinity": "0-3,8",
"StdoutPath": "C:\\Logs\\TestService_out.log",
"StderrPath": "C:\\Logs\\TestService_err.log",
"StartTimeout": 10,
"StopTimeout": 5,
"EnableSizeRotation": true,
"RotationSize": 10,
"EnableDateRotation": false,
"DateRotationType": 0,
"MaxRotations": 0,
"UseLocalTimeForRotation": false,
"EnableConsoleUI": false,
"EnableDebugLogs": false,
"EnableHealthMonitoring": true,
"HeartbeatInterval": 30,
"MaxFailedChecks": 3,
"RecoveryAction": 1,
"MaxRestartAttempts": 5,
"HeartbeatUrl": "https://hc-ping.com/your-uuid",
"HeartbeatUrlTimeoutSeconds": 5,
"EnableHeartbeatUrlFlags": true,
"RecoveryOnCleanExit": false,
"FailureProgramPath": "C:\\Program Files\\nodejs\\node.exe",
"FailureProgramStartupDirectory": "C:\\Apps\\Notify",
"FailureProgramParameters": "C:\\Apps\\Notify\\index.js",
"EnvironmentVariables": "APP_ENV=production;APP_CONFIG=C:\\Apps\\App\\config.json",
"AllowOverriddenRuntimeVars": false,
"ServiceDependencies": "ServiceA;ServiceB",
"PreLaunchExecutablePath": "C:\\Program Files\\TestService\\PreLaunch.exe",
"PreLaunchStartupDirectory": "C:\\Program Files\\TestService",
"PreLaunchParameters": "-preArg1 -preArg2",
"PreLaunchEnvironmentVariables": "CONFIG=C:\\Config;LOGS=C:\\Logs",
"PreLaunchStdoutPath": "C:\\Logs\\PreLaunch_out.log",
"PreLaunchStderrPath": "C:\\Logs\\PreLaunch_err.log",
"PreLaunchTimeoutSeconds": 60,
"PreLaunchRetryAttempts": 2,
"PreLaunchIgnoreFailure": true,
"PostLaunchExecutablePath": "C:\\Program Files\\TestService\\PostLaunch.exe",
"PostLaunchStartupDirectory": "C:\\Program Files\\TestService",
"PostLaunchParameters": "-postArg1 -postArg2",
"PreStopExecutablePath": "C:\\Program Files\\TestService\\PreStop.exe",
"PreStopStartupDirectory": "C:\\Program Files\\TestService",
"PreStopParameters": "-stopArg1 -stopArg2",
"PreStopTimeoutSeconds": 30,
"PreStopLogAsError": true,
"PostStopExecutablePath": "C:\\Program Files\\TestService\\PostStop.exe",
"PostStopStartupDirectory": "C:\\Program Files\\TestService",
"PostStopParameters": "-stopArg1 -stopArg2"
}
```
### GUI
* Open Servy
* Click on **Import** menu on the top left
* Choose your import format (XML or JSON)
* Choose the file to import
If valid, the configuration is displayed in the UI. If the service is installed, the configuration is persisted in the database.
If the file is rejected, an error dialog names the reason and nothing is written to the database.
### CLI
* Run the following command to import in XML format:
```cmd
servy-cli import --config="xml" --path="C:\MyRegisteredService.xml"
```
* Run the following command to import in JSON format:
```cmd
servy-cli import --config="json" --path="C:\MyRegisteredService.json"
```
* Run the following command to import an XML and install:
```cmd
servy-cli import --config="xml" --path="C:\MyRegisteredService.xml" --install
```
* Run the following command to import a JSON and install:
```cmd
servy-cli import --config="json" --path="C:\MyRegisteredService.json" --install
```
If valid, the configuration is saved to the database and the service is installed if `--install` option is supplied.
If the file is missing, malformed, blocked by the local-path guard, or contains an invalid field, no service is created and the CLI terminates with an exit code of 1.
### PowerShell
* Import Servy PowerShell module:
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
```
* Import service configuration from XML:
```powershell
Import-ServyServiceConfig -ConfigFileType Xml -Path "C:\MyRegisteredService.xml"
```
* Import service configuration from JSON:
```powershell
Import-ServyServiceConfig -ConfigFileType Json -Path "C:\MyRegisteredService.json"
```
* Import service configuration from XML and install the service:
```powershell
Import-ServyServiceConfig -ConfigFileType Xml -Path "C:\MyRegisteredService.xml" -Install
```
* Import service configuration from JSON and install the service:
```powershell
Import-ServyServiceConfig -ConfigFileType Json -Path "C:\MyRegisteredService.json" -Install
```
If the configuration is valid, it is saved to the Servy database and the service is installed if the `-Install` switch is provided.
Under the same conditions the cmdlet throws; wrap the call in `try/catch` as with `Export-ServyServiceConfig`.
---
# Document: Pre-Launch & Post-Launch Actions
> Source: https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#introduction)
1. [Startup Hook Lifecycle](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#startup-hook-lifecycle)
1. [Pre-Launch](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#pre-launch)
1. [Pre-Launch Settings](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#pre-launch-settings)
1. [GUI Example](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#gui-example)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#cli-example)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#powershell-example)
1. [Post-Launch](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#post-launch)
1. [Post-Launch Settings](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#post-launch-settings)
1. [GUI Example](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#gui-example-1)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#cli-example-1)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions#powershell-example-1)
## Introduction
Servy supports running an optional script or executable before the main service starts. This can be used for tasks like:
* Preparing or generating configuration files
* Fetching secrets or credentials dynamically
* Running any setup or initialization required before the main process
Additionally, Servy supports running an optional script or executable after the process starts successfully. This can be used for tasks such as:
* Initializing dependent services or processes
* Running database migrations or setup scripts
* Sending notifications or logging startup events
* Performing environment-specific configuration
> [!NOTE]
> If the pre-launch (or post-launch) script is a PowerShell script (`.ps1`) or any non-executable file, it **must** be invoked through an executable such as `powershell.exe` or `pwsh.exe`.
>
> For example:
>
> ```powershell
> --preLaunchPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
> --preLaunchParams="-File C:\Scripts\GenerateConfig.ps1 -VaultUrl https://vault.example.com"
> ```
>
> Directly specifying `--preLaunchPath="C:\Scripts\GenerateConfig.ps1"` **will not work**.
## Startup Hook Lifecycle
| Hook Type | Mode | Trigger | Failure Impact | Orphan Cleanup |
| :--- | :--- | :--- | :--- | :--- |
| **Pre-Launch** | **Synchronous** | Timeout > 0 | Service won't start unless **Ignore Failure** is enabled | Yes |
| **Pre-Launch** | **Fire-and-Forget** | Timeout = 0 | Exit code not awaited; a launch failure stops the service unless **Ignore Failure** is enabled | Yes |
| **Post-Launch** | **Fire-and-Forget** | Default | Logged, service stays up | Yes |
*For shutdown-related hooks, see [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions).*
*Servy tracks processes launched during startup to ensure that if the main service crashes, all setup scripts are also terminated.*
> [!NOTE]
> While a synchronous hook is running, Servy periodically notifies the Windows Service Control Manager that the service is still active, so the SCM's own start timeout does not kill the service while a long-running hook is still within its configured timeout. See `Timing:ScmAdditionalTimeMs` in [Advanced Configuration](https://github.com/aelassas/servy/wiki/Advanced-Configuration#servy-service).
## Pre-Launch
### Pre-Launch Settings
* **Pre-Launch Executable Path** - full path to the pre-launch script or executable
* **Pre-Launch Startup Directory** - working directory for the pre-launch process (optional; defaults to the main service's working directory)
* **Pre-Launch Parameters** - command-line arguments for the pre-launch executable
* **Pre-Launch Environment Variables** - optional environment variables for the pre-launch process (same format as main process)
* **Pre-Launch Stdout/Stderr File Paths** - log files for capturing output and errors
* **Pre-Launch Timeout (seconds)** - maximum allowed time per attempt, set to **0** to run in fire-and-forget mode (default: 30 seconds)
* **Pre-Launch Retry Attempts** - number of retries on failure (default: 0)
* **Ignore Failure** - if enabled, service startup proceeds even if pre-launch script fails
By default, the pre-launch hook runs synchronously with a timeout. If the pre-launch script exits with a non-zero exit code or times out, the service will fail to start unless the **Ignore Failure** option is enabled.
Setting the timeout to 0 runs the pre-launch hook in fire-and-forget mode. When set to 0, the hook is started and the service is launched immediately without waiting for completion. Use this only for tasks that do not affect the service's ability to start or run correctly. `stdout`/`stderr` redirection and retries are not available in fire-and-forget mode. The hook's exit code is not awaited in this mode, but if the hook cannot be launched at all, the service still fails to start unless **Ignore Failure** is enabled.
Fire-and-forget pre-launch hooks are executed as part of the start sequence and are tracked for orphan cleanup when the service stops.
### GUI Example
### CLI Example
```powershell
servy-cli install `
--name="MyService" `
--description="Runs app with dynamic config" `
--path="C:\Apps\App\App.exe" `
--startupDir="C:\Apps\App" `
--params="--mode=production" `
--preLaunchPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--preLaunchStartupDir="C:\Scripts" `
--preLaunchParams="-File C:\Scripts\GenerateConfig.ps1 -VaultUrl https://vault.example.com" `
--preLaunchEnv="ENV=production;API_KEY=abcdef123" `
--preLaunchStdout="C:\Logs\prelaunch_stdout.log" `
--preLaunchStderr="C:\Logs\prelaunch_stderr.log" `
--preLaunchTimeout="60" `
--preLaunchRetryAttempts="2" `
--preLaunchIgnoreFailure
```
### PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyService"
Description = "Runs app with dynamic config"
Path = "C:\Apps\App\App.exe"
StartupDir = "C:\Apps\App"
Params = "--mode=production"
PreLaunchPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
PreLaunchStartupDir = "C:\Scripts"
PreLaunchParams = "-File C:\Scripts\GenerateConfig.ps1 -VaultUrl https://vault.example.com"
PreLaunchEnv = "ENV=production;API_KEY=abcdef123"
PreLaunchStdout = "C:\Logs\prelaunch_stdout.log"
PreLaunchStderr = "C:\Logs\prelaunch_stderr.log"
PreLaunchTimeout = 60
PreLaunchRetryAttempts = 2
PreLaunchIgnoreFailure = $true
}
Install-ServyService @installParams
```
## Post-Launch
### Post-Launch Settings
* **Post-Launch Executable Path** - full path to the post-launch script or executable
* **Post-Launch Startup Directory** - working directory for the post-launch process (optional; defaults to the main service's working directory)
* **Post-Launch Parameters** - command-line arguments for the post-launch executable
**Environment Variables:** This hook inherits the **main service's** `EnvironmentVariables`. There is no separate `Post-Launch / Pre-Stop / Post-Stop / Failure-Program EnvironmentVariables` setting - only `Pre-Launch` supports a per-hook override.
The post-launch script is executed after the service process has fully started in fire-and-forget mode.
Post-launch hooks run after the service has started and are tracked for orphan cleanup when the service stops.
### GUI Example
### CLI Example
```powershell
servy-cli install `
--name="MyService" `
--description="Runs app with dynamic config" `
--path="C:\Apps\App\App.exe" `
--startupDir="C:\Apps\App" `
--params="--mode=production" `
--postLaunchPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--postLaunchStartupDir="C:\Scripts" `
--postLaunchParams="-File C:\Scripts\Notify.ps1 -VaultUrl https://vault.example.com"
```
### PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyService"
Description = "Runs app with dynamic config"
Path = "C:\Apps\App\App.exe"
StartupDir = "C:\Apps\App"
Params = "--mode=production"
PostLaunchPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
PostLaunchStartupDir = "C:\Scripts"
PostLaunchParams = "-File C:\Scripts\Notify.ps1 -VaultUrl https://vault.example.com"
}
Install-ServyService @installParams
```
---
# Document: Pre-Stop & Post-Stop Actions
> Source: https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#introduction)
1. [Shutdown Hook Lifecycle](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#shutdown-hook-lifecycle)
1. [Pre-Stop](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#pre-stop)
1. [Pre-Stop Settings](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#pre-stop-settings)
1. [GUI Example](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#gui-example)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#cli-example)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#powershell-example)
1. [Post-Stop](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#post-stop)
1. [Post-Stop Settings](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#post-stop-settings)
1. [GUI Example](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#gui-example-1)
1. [CLI Example](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#cli-example-1)
1. [PowerShell Example](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions#powershell-example-1)
## Introduction
Servy supports running an optional script or executable before the main service stops. This can be used for tasks such as:
* Gracefully shutting down external dependencies
* Flushing logs or metrics
* Notifying external systems before shutdown
Servy also supports running an optional script or executable after the service process has stopped. This can be used for tasks such as:
* Cleanup operations
* Notifications or alerts
* Post-shutdown automation
> [!NOTE]
> If the pre-stop (or post-stop) script is a PowerShell script (`.ps1`) or any non-executable file, it **must** be invoked through an executable such as `powershell.exe` or `pwsh.exe`.
>
> For example:
>
> ```powershell
> --preStopPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
> --preStopParams="-File C:\Scripts\PreStop.ps1 -VaultUrl https://vault.example.com"
> ```
>
> Directly specifying `--preStopPath="C:\Scripts\PreStop.ps1"` **will not work**.
## Shutdown Hook Lifecycle
| Hook Type | Mode | Trigger | Failure Impact | Orphan Cleanup |
| :--- | :--- | :--- | :--- | :--- |
| **Pre-Stop** | **Synchronous** | Timeout > 0 | Logged, service stops | No |
| **Pre-Stop** | **Fire-and-Forget** | Timeout = 0 | Logged, service stops | No |
| **Post-Stop** | **Fire-and-Forget** | Default | Logged | No |
*For startup-related hooks, see [Pre-Launch & Post-Launch Actions](https://github.com/aelassas/servy/wiki/Pre-Launch-&-Post-Launch-Actions).*
*Servy tracks processes launched during startup to ensure that if the main service crashes, all setup scripts are also terminated. For Stop hooks, cleanup is disabled because these processes are intended to finish their work as the service environment itself is being torn down.*
> [!NOTE]
> While a synchronous hook is running, Servy periodically notifies the Windows Service Control Manager that the service is still active, so the SCM's own stop timeout does not kill the service while a long-running hook is still within its configured timeout. See `Timing:ScmAdditionalTimeMs` in [Advanced Configuration](https://github.com/aelassas/servy/wiki/Advanced-Configuration#servy-service).
## Pre-Stop
### Pre-Stop Settings
* **Pre-Stop Executable Path**: Full path to the pre-stop script or executable
* **Pre-Stop Startup Directory**: Working directory for the pre-stop process (optional; defaults to the main service's working directory)
* **Pre-Stop Parameters**: Command line arguments for the pre-stop executable
* **Pre-Stop Timeout (seconds)**: Maximum allowed execution time. Default is 5 seconds. Set to 0 to run in fire-and-forget mode
* **Log Pre-Stop Failure as Error**: When enabled, pre-stop failures are logged as errors
**Environment Variables:** This hook inherits the **main service's** `EnvironmentVariables`. There is no separate `Post-Launch / Pre-Stop / Post-Stop / Failure-Program EnvironmentVariables` setting - only `Pre-Launch` supports a per-hook override.
The pre-stop script is executed synchronously by default. Servy waits for the script to complete before continuing the service shutdown process.
If the script fails or times out and **Log Pre-Stop Failure as Error** is enabled, the failure is logged as an error and the service continues stopping.
Setting the timeout to 0 runs the pre-stop hook in fire-and-forget mode. In this mode, the hook is started and the service shutdown continues immediately without waiting for completion. This should only be used for non-critical tasks.
Fire-and-forget pre-stop hooks are executed as part of the stop sequence and are not tracked for orphan cleanup.
### GUI Example
### CLI Example
```powershell
servy-cli install `
--name="MyService" `
--description="Runs app with dynamic config" `
--path="C:\Apps\App\App.exe" `
--startupDir="C:\Apps\App" `
--params="--mode=production" `
--preStopPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--preStopStartupDir="C:\Scripts" `
--preStopParams="-File C:\Scripts\PreStop.ps1 -VaultUrl https://vault.example.com -SecretName AppSecrets" `
--preStopTimeout="60" `
--preStopLogAsError
```
### PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyService"
Description = "Runs app with dynamic config"
Path = "C:\Apps\App\App.exe"
StartupDir = "C:\Apps\App"
Params = "--mode=production"
PreStopPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
PreStopStartupDir = "C:\Scripts"
PreStopParams = "-File C:\Scripts\PreStop.ps1 -VaultUrl https://vault.example.com -SecretName AppSecrets"
PreStopTimeout = 60
PreStopLogAsError = $true
}
Install-ServyService @installParams
```
## Post-Stop
### Post-Stop Settings
* **Post-Stop Executable Path**: Full path to the post-stop script or executable
* **Post-Stop Startup Directory**: Working directory for the post-stop process (optional; defaults to the main service's working directory)
* **Post-Stop Parameters**: Command line arguments for the post-stop executable
**Environment Variables:** This hook inherits the **main service's** `EnvironmentVariables`. There is no separate `Post-Launch / Pre-Stop / Post-Stop / Failure-Program EnvironmentVariables` setting - only `Pre-Launch` supports a per-hook override.
The post-stop script is executed in fire-and-forget mode after the service process has fully stopped.
Post-stop hooks run after the service has stopped and are not tracked for orphan cleanup.
### GUI Example
### CLI Example
```powershell
servy-cli install `
--name="MyService" `
--description="Runs app with dynamic config" `
--path="C:\Apps\App\App.exe" `
--startupDir="C:\Apps\App" `
--params="--mode=production" `
--postStopPath="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--postStopStartupDir="C:\Scripts" `
--postStopParams="-File C:\Scripts\Notify.ps1 -VaultUrl https://vault.example.com -SecretName AppSecrets"
```
### PowerShell Example
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyService"
Description = "Runs app with dynamic config"
Path = "C:\Apps\App\App.exe"
StartupDir = "C:\Apps\App"
Params = "--mode=production"
PostStopPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
PostStopStartupDir = "C:\Scripts"
PostStopParams = "-File C:\Scripts\Notify.ps1 -VaultUrl https://vault.example.com -SecretName AppSecrets"
}
Install-ServyService @installParams
```
---
# Document: Shutdown & Teardown
> Source: https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown
## Table of Contents
1. [OS Shutdown Handling (v6.2+)](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown#os-shutdown-handling-v62)
1. [The Shutdown Sequence](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown#the-shutdown-sequence)
1. [Key Features](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown#key-features)
1. [Mandatory Reinstallation](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown#mandatory-reinstallation)
1. [Extending the Shutdown Window](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown#extending-the-shutdown-window)
1. [Notes](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown#notes)
## OS Shutdown Handling (v6.2+)
Servy v6.2+ introduces high-reliability service teardown during operating system shutdown and reboot, matching the graceful-shutdown behavior expected of a Windows service wrapper.
## The Shutdown Sequence
When a system shutdown or reboot is detected, Servy executes a specialized teardown workflow designed to ensure data integrity and a graceful exit. The sequence is as follows:
1. **Pre-Shutdown Event:** Servy immediately captures the `SERVICE_CONTROL_PRESHUTDOWN` signal from the Windows Service Control Manager (SCM).
2. **Pre-Stop Hook:** If a Pre-Stop hook is configured, it is executed. By default, this runs synchronously to allow for cleanup. However, if the **Pre-Stop Timeout** is set to **0**, the hook runs in **fire-and-forget** mode and Servy proceeds immediately to the next step.
3. **Process Termination:** Servy stops the managed child process. It first attempts a graceful close (sending `WM_CLOSE` or `CTRL_C_EVENT`) before resorting to a forced termination if the process does not exit within the configured timeout.
4. **Post-Stop Hook:** If a Post-Stop hook is configured, it is executed in **fire-and-forget** mode. This hook runs after the child process has exited but does not block the service from finishing its own shutdown.
5. **Logging & Finalization:** Servy logs the successful completion of the pre-shutdown sequence to the Windows Event Log and officially transitions to the `STOPPED` state.
## Key Features
* **Pre-Shutdown Registration:** Registers for `SERVICE_CONTROL_PRESHUTDOWN`. This allows Servy to begin cleanup immediately when an OS shutdown or reboot is initiated, occurring before the standard service stop command is issued.
* **SCM Progress Reporting:** Periodically reports progress using wait hints and checkpoints to the Windows Service Control Manager (SCM), preventing premature termination during long cleanup tasks.
* **Synchronized Teardown:** Protects against race conditions when `STOP` and `PRESHUTDOWN` control codes are received concurrently.
## Mandatory Reinstallation
> [!IMPORTANT]
> **Compatibility:** You must reinstall your service using Servy v6.2+. Services installed with earlier versions lack the `SERVICE_ACCEPT_PRESHUTDOWN` flag in the SCM database and will ignore these high-priority notifications.
## Extending the Shutdown Window
By default, a Servy service (v6.2+, which registers for pre-shutdown notifications) receives the global `PreshutdownTimeout` window (180 seconds on standard Windows configurations) before the SCM proceeds. Servy dynamically sends wait hints and checkpoints to report shutdown progress, preventing the OS from prematurely killing the wrapper during active cleanup.
### Method 1: Per-Service Timeout Override (Recommended)
You do **not** need to edit the registry or reboot the system to give a Servy-managed service a longer shutdown window. Servy automatically configures a per-service pre-shutdown timeout directly on the Windows Service Control Manager (SCM) via `ChangeServiceConfig2` using `SERVICE_CONFIG_PRESHUTDOWN_INFO`.
The per-service pre-shutdown timeout is calculated dynamically whenever a service is installed or updated:
$$\text{Preshutdown Timeout} = \text{Baseline} + \text{PreStopTimeout} + \text{SCM Buffer}$$
Where:
* **Baseline:** The highest of three values: your configured `StopTimeout`, any previously recorded historical stop duration (itself capped at 86400 seconds), and a floor of **60 seconds** (`ScmStopTimeoutFloorSeconds`). A `StopTimeout` below the floor does not shorten the window.
* **PreStopTimeout:** The maximum duration allocated for the Pre-Stop executable hook, or 0 when no Pre-Stop executable is configured.
* **SCM Buffer:** A mandatory 15-second safety margin (`ScmTimeoutBufferSeconds`) added automatically to prevent OS timing race conditions.
For example, a service with `-StopTimeout 90` and `-PreStopTimeout 30` that has never recorded a slower stop is registered with a pre-shutdown timeout of `90 + 30 + 15 = 135` seconds. Leaving both at their defaults gives `60 + 0 + 15 = 75` seconds without a Pre-Stop hook, or `60 + 5 + 15 = 80` seconds with one.
To grant a specific service a longer shutdown window:
1. Increase the service's `-StopTimeout` (and `-PreStopTimeout` if a Pre-Stop hook is used) via the CLI, PowerShell cmdlet, or Servy Manager UI.
2. Update the service configuration and re-install.
The SCM immediately honors the custom pre-shutdown timeout for that specific service - **no registry edits, no system reboots, and no impact on other services on the machine.**
> [!NOTE]
> For parameter specifications and usage examples, refer to:
>
> * [Servy CLI Reference](https://github.com/aelassas/servy/wiki/Servy-CLI) (`--stopTimeout`, `--preStopTimeout`)
> * [Servy PowerShell Module Reference](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module) (`-StopTimeout`, `-PreStopTimeout`)
> * [Pre-Stop & Post-Stop Actions](https://github.com/aelassas/servy/wiki/Pre-Stop-&-Post-Stop-Actions)
### Method 2: Global System Registry Fallback (Machine-Wide)
If you need to increase the hard ceiling for non-Servy services receiving pre-shutdown notifications or extend the subsequent `WaitToKillServiceTimeout` phase (which governs standard services receiving `SERVICE_CONTROL_STOP` and defaults to only **5 seconds** on current Windows versions), you can modify the global registry keys.
Because the Service Control Manager reads these global control registry keys during system startup, a full reboot is required after applying changes.
The following PowerShell script extends the global shutdown timeouts. Run it as **Administrator**, set the desired timeout in milliseconds, and then reboot the system.
```powershell
# Registry path
$registryPath = "HKLM:\SYSTEM\CurrentControlSet\Control"
# Timeout in milliseconds (example: 80 seconds)
$timeoutValue = 80000
# 1. Set PreshutdownTimeout (DWORD, milliseconds)
if (Get-ItemProperty -Path $registryPath -Name "PreshutdownTimeout" -ErrorAction SilentlyContinue) {
Set-ItemProperty -Path $registryPath -Name "PreshutdownTimeout" -Value $timeoutValue
} else {
New-ItemProperty -Path $registryPath -Name "PreshutdownTimeout" -Value $timeoutValue -PropertyType DWord -Force
}
# 2. Set WaitToKillServiceTimeout (REG_SZ, milliseconds)
# Note: This is stored as a string (REG_SZ) in the Windows Registry
Set-ItemProperty -Path $registryPath -Name "WaitToKillServiceTimeout" -Value "$timeoutValue"
Write-Host "Shutdown timeouts updated to $timeoutValue ms."
Write-Host "A system reboot is required for these changes to take effect."
```
## Notes
* **Per-Service Preshutdown Timeout:** Configured via Servy's `StopTimeout` / `PreStopTimeout` settings. Registered directly with the SCM using `SERVICE_CONFIG_PRESHUTDOWN_INFO`. Takes effect immediately without a reboot.
* **PreshutdownTimeout (Registry):** Controls the global fallback timeout (in milliseconds) Windows grants to pre-shutdown handling services (`SERVICE_CONTROL_PRESHUTDOWN`) that have not set an explicit per-service pre-shutdown timeout.
* **WaitToKillServiceTimeout (Registry):** Controls the global fallback timeout (in milliseconds) Windows grants to standard services (`SERVICE_CONTROL_STOP`) before forcibly terminating non-responsive processes.
* **Configuration Tip:** Set your service's `StopTimeout` to exceed the maximum expected duration of your longest-running managed process and its associated shutdown hooks so Windows doesn't force-terminate it prematurely.
---
# Document: Servy Automation & CI/CD
> Source: https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#introduction)
1. [Installation Options](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#installation-options)
1. [Manual Download & Silent Install](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#manual-download--silent-install)
1. [Package Manager Installation](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#package-manager-installation)
1. [CLI Usage](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#cli-usage)
1. [Jenkins Integration](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#jenkins-integration)
1. [TeamCity Integration](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#teamcity-integration)
1. [GitHub Actions Integration](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#github-actions-integration)
1. [Azure DevOps Integration](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#azure-devops-integration)
1. [Automation Best Practices](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#automation-best-practices)
1. [References](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#references)
## Introduction
Servy is designed for programmatic management, making it ideal for automated environments and CI/CD pipelines.
## Installation Options
### Manual Download & Silent Install
**.NET 10.0+ version (x64)**
```text
https://github.com/aelassas/servy/releases/download/v/servy--x64-installer.exe
```
**.NET 10.0+ version (ARM64)**
```text
https://github.com/aelassas/servy/releases/download/v/servy--arm64-installer.exe
```
**.NET Framework 4.8 version (x64)**
```text
https://github.com/aelassas/servy/releases/download/v/servy--net48-x64-installer.exe
```
**Silent installation commands:**
```cmd
.\servy--x64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
.\servy--arm64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
.\servy--net48-x64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL
```
**Silent installation commands (CLI only):**
```cmd
.\servy--x64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL /SetupType=custom /Components=install_cli
.\servy--arm64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL /SetupType=custom /Components=install_cli
.\servy--net48-x64-installer.exe /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /SP- /CLOSEAPPLICATIONS /NOCANCEL /SetupType=custom /Components=install_cli
```
For further details, check out the [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide).
### Package Manager Installation
- **WinGet**:
```powershell
winget install --id aelassas.Servy -e --accept-package-agreements --accept-source-agreements --silent
```
- **Chocolatey**:
```powershell
choco install -y servy
```
- **Scoop**:
```powershell
scoop bucket add extras
scoop install servy
```
- **Patch My PC**:
Servy is available in the official [Patch My PC catalog](https://patchmypc.com/supported-products/) for enterprise automated deployment and updates via Microsoft Intune and ConfigMgr (SCCM).
> [!NOTE]
> For unattended automated environments and CI/CD pipelines, WinGet commands must explicitly include `--accept-package-agreements --accept-source-agreements --silent` to prevent interactive prompt stalls, alongside `--id aelassas.Servy -e` to pin the package by exact identity rather than performing an ambiguous search.
Once installed, the CLI executable is available at:
```text
%ProgramFiles%\Servy\servy-cli.exe
```
And the PowerShell module is available at:
```text
%ProgramFiles%\Servy\Servy.psm1
```
After a default installation `servy-cli` is on the system **PATH** - see [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide#add-servy-to-path) for the option that controls this and for the portable package.
## CLI Usage
Servy CLI can manage services programmatically. Typical commands:
```powershell
# Install or update a service
servy-cli install --name="MyApp" --path="C:\MyApp\MyApp.exe" --startupType="Automatic"
# Start a service
servy-cli start --name="MyApp"
# Get a service status
servy-cli status --name="MyApp"
# Stop a service
servy-cli stop --name="MyApp"
# Uninstall a service
servy-cli uninstall --name="MyApp"
```
**Notes:**
- All commands return exit codes suitable for CI/CD pipeline checks.
- See [Automation Best Practices](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD#automation-best-practices) for idempotency, restart rules, and security guidelines.
- Full CLI documentation is available in the [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI) reference.
- Full PowerShell module documentation is available in the [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module) reference.
## Jenkins Integration
1. Install Servy CLI on your Jenkins Windows agent.
2. Add a build step (Execute Windows batch command or PowerShell):
```powershell
# Install or update service
servy-cli install --name="MyApp" --path="C:\MyApp\MyApp.exe" --startupType="Automatic"
# Start the service
servy-cli start --name="MyApp"
```
3. Use exit codes to fail the build if Servy commands fail.
## TeamCity Integration
1. Install Servy CLI on the TeamCity agent.
2. Add a Command Line build step:
```powershell
servy-cli install --name="MyApp" --path="C:\TeamCity\Builds\MyApp.exe" --startupType="Automatic"
servy-cli start --name="MyApp"
```
3. Monitor exit codes to verify success.
## GitHub Actions Integration
Example step for a Windows runner:
```yaml
name: Install MyApp with Servy
on:
workflow_dispatch:
jobs:
test-servy:
runs-on: windows-latest
steps:
- name: Install Servy via WinGet
run: |
winget install --id aelassas.Servy -e --accept-package-agreements --accept-source-agreements --silent
shell: powershell
- name: Verify Servy CLI installed
run: |
& "C:\Program Files\Servy\servy-cli.exe" --version --quiet
shell: powershell
- name: Install MyApp as a Windows Service
run: |
& "C:\Program Files\Servy\servy-cli.exe" install `
--name="MyApp" `
--path="C:\MyApp\MyApp.exe" `
--startupType="Automatic" `
--startupDir="C:\MyApp"
shell: powershell
- name: Start MyApp service
run: |
& "C:\Program Files\Servy\servy-cli.exe" start --name="MyApp"
shell: powershell
- name: Verify service status
run: |
if ((Get-Service -Name MyApp).Status -ne 'Running') { exit 1 }
shell: powershell
```
## Azure DevOps Integration
1. Add a Windows agent to your pipeline.
2. Add a PowerShell task:
```powershell
servy-cli install --name="MyApp" --path="$(Build.ArtifactStagingDirectory)\MyApp.exe" --startupType="Automatic"
servy-cli start --name="MyApp"
```
3. Use task result codes to fail pipeline on errors.
## Automation Best Practices
- **Idempotency:** Always use the `install` command in your scripts. It safely handles both fresh installs and updates to existing services.
- **Restart Required:** A service restart is necessary for configuration updates to take effect.
- **Security Best Practice:** Avoid using sensitive flags (e.g., `--password`, `--params`, `--envVars`, `--preLaunchEnv`) in production or scripts. Passing these values as command-line arguments makes them visible to any user or process with access to the Windows Process List or shell history files. Instead, set the corresponding environment variables (e.g., `SERVY_PASSWORD`, `SERVY_PROCESS_PARAMETERS`, `SERVY_ENVIRONMENT_VARIABLES`) before running the install command. See [Security](https://github.com/aelassas/servy/wiki/Security#6-sensitive-command-line-arguments--service-account-credentials) page for more information.
- **Health Checks:** `servy-cli status` exits `0` whenever it can read the status, including `Stopped` and `NotInstalled`, so its exit code alone does not prove the service is running. Check the status token it prints last:
```powershell
$status = servy-cli status --name="MyApp" --quiet | Select-Object -Last 1
if ($status -notmatch '\bRunning$') { Write-Error "MyApp is not running: $status"; exit 1 }
```
The token is invariant (`Running`, `Stopped`, `NotInstalled`, `Unknown`); only the surrounding message is localized.
- **Exit Codes:** Servy CLI returns standard exit codes (`0` for success, non-zero for failure). Ensure your CI/CD platform is configured to fail the build on non-zero returns. A non-zero exit from `status` means the query itself failed, not that the service is down.
- **Shutdown Handling:** For CI/CD environments that use ephemeral runners or auto-scaling groups, ensure your services are installed with **Servy v6.2+** to take advantage of `SERVICE_CONTROL_PRESHUTDOWN` for graceful teardowns.
- **Quote Parameters:** In PowerShell, double-quoted strings expand `$variable` and `$(...)` before the CLI receives them. Use single quotes (`'...'`) when you need the CLI to receive the literal `$`/`` ` ``/`"` characters - for example: `--params='Set-Item Env:Foo "$bar"'`. Use double quotes when you actually want PowerShell to substitute, e.g. `--path="$env:ProgramFiles\MyApp\app.exe"`.
## References
- [Servy Releases](https://github.com/aelassas/servy/releases)
- [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI)
- [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
---
# Document: Embedding in Custom Installers
> Source: https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers
## Table of Contents
1. [Overview](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#overview)
1. [Why Servy Requires Automated Initialization](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#why-servy-requires-automated-initialization)
1. [WiX Toolset (MSI) Integration](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#wix-toolset-msi-integration)
1. [Inno Setup Integration](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#inno-setup-integration)
1. [Advanced Installer & InstallShield](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#advanced-installer--installshield)
1. [Exit Codes, Re-Runs & Cleanup](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#exit-codes-re-runs--cleanup)
1. [Verification & Troubleshooting](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#verification--troubleshooting)
## Overview
When packaging software into enterprise installers (such as WiX, Inno Setup, Advanced Installer, or InstallShield), background services are typically installed by writing registry keys or using built-in service tables.
Legacy wrappers like NSSM are not secure for production environments because NSSM stores the service configuration, including application parameters and environment variables, in plaintext in the Windows Registry, where anyone with read access to the registry can inspect it. In contrast, Servy encrypts sensitive fields (the account password, the process and hook parameters, and the environment variables) with AES-256 and HMAC-SHA256 authenticated encryption under a DPAPI-protected, HKDF-derived key, and each service account can reach only its own configuration and logs (see [Security](https://github.com/aelassas/servy/wiki/Security)).
Servy functions as an enterprise service host rather than a standalone runner script. To maintain production stability, security, and real-time monitoring, services must be installed via the command-line tool (`servy-cli.exe`) during the installation phase and removed during the uninstallation phase.
The `servy-cli.exe` utility provides complete service configuration, including process execution settings, environment variables, logging and log rotation, health monitoring and automated recovery, service dependencies, the service account, and pre/post launch and stop hooks. See [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI#install-command) for every `install` option.
To include `servy-cli.exe` in your installer package:
- Extract `servy-cli.exe` directly from the modern (.NET 10.0+) build portable package (`servy-x.x-x64-portable.7z` or `servy-x.x-arm64-portable.7z`), choosing the one that matches the architecture of the target machines. Servy is built for x64 and ARM64 only; there is no 32-bit build.
- **`servy-cli.exe` is completely self-contained** and does not require any external runtime dependencies or pre-installed frameworks (.NET runtime is embedded), making it ideal for clean, standalone installer distributions. It is a single file: `appsettings.cli.json` is optional and nothing else needs to be shipped next to it.
- **`servy-cli.exe` carries the rest of Servy inside it.** The first `install`, `start`, `restart` or `import` command extracts the service wrapper (`Servy.Service.CLI.exe`), `Servy.Restarter.exe`, the host service binary (`Servy.Host.exe`) and Sysinternals `handle64.exe` (`handle64a.exe` on ARM64) into `%ProgramData%\Servy`, then installs and starts the `Servy` host service. Do not ship or copy these files yourself, and do not install them to your application folder.
- **.NET Framework 4.8 build:** if you embed the legacy build (`servy-x.x-net48-x64-portable.7z`) instead, its `servy-cli.exe` is not a single file: ship it together with the `*.dll` files from the same package, and make sure .NET Framework 4.8 is installed on the target machine. The binaries it extracts are named `Servy.Service.CLI.Net48.exe`, `Servy.Restarter.Net48.exe` and `Servy.Host.Net48.exe`.
- **Elevation is required.** `servy-cli install` refuses to run without administrator rights, and creating, starting, stopping or deleting a Windows service needs them anyway. In an MSI, this means a deferred custom action with `Impersonate="no"`, which runs as `LocalSystem`.
- **The service name `Servy` is reserved** for the Servy host service and is rejected by `install`. Pick any other name for your service.
## Why Servy Requires Automated Initialization
Standard legacy wrappers (like `srvany` or NSSM) rely solely on static registry keys. Servy requires explicit initialization through `servy-cli install`, immediate startup via `servy-cli start`, clean shutdown via `servy-cli stop`, and proper cleanup via `servy-cli uninstall` because it automatically configures:
- **Central Host Windows Service (`Servy`):** Installs the `Servy` host service (`Servy.Host.exe`) when it is missing, starts it, and makes every Servy service depend on it. Your service gets its configuration from this host over a local named pipe.
- **Encrypted Vault & SQLite Database:** Creates `%ProgramData%\Servy\db\Servy.db`, the SQLite database that holds the configuration of each service (sensitive fields encrypted) and its runtime state.
- **DPAPI & AES-256 Key Material:** Creates the machine-wide master key `%ProgramData%\Servy\security\aes_key.dat`, protected with DPAPI in machine scope and bound to the machine. The AES-256 encryption and HMAC-SHA256 authentication sub-keys are derived from it with HKDF. The key cannot be copied to another machine.
- **Directory & Pipe ACL Hardening:** Applies strict Windows Access Control Lists (ACLs) to `%ProgramData%\Servy`, the extracted binaries, the log folder of each service (`logs\services\\`) and the host's named pipe, so a service account gets only the access its own service needs. See [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening).
Attempting to register or remove the Servy wrapper (`Servy.Service.CLI.exe`) directly via raw registry writes or the MSI `ServiceInstall` table bypasses these steps, causing service launch or uninstallation failures: a service registered that way has no record in `Servy.db` to load its configuration from, and a Servy service removed that way leaves its record and access grants behind.
## WiX Toolset (MSI) Integration
To bundle Servy into a WiX installer without opening command prompt windows for the user, use deferred, elevated **Quiet Execution Custom Actions** (`CAQuietExec` or `WixQuietExec`).
Place `servy-cli.exe` alongside your application binaries, then configure your `.wxs` source file as follows. The sample uses WiX v3 syntax; `CAQuietExec` and the `WixCA` binary come from `WixUtilExtension`, so pass `-ext WixUtilExtension` to both `candle` and `light`.
```xml
NOT Installed AND NOT PATCH
NOT Installed AND NOT PATCH
REMOVE="ALL"
REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE
```
> [!NOTE]
> Setting `Execute="deferred"` with `Impersonate="no"` ensures the installer executes `servy-cli` as `LocalSystem`, with the administrative privileges needed to create the `%ProgramData%\Servy` vault and to install, start, stop, or unregister Windows services. An immediate or impersonated custom action runs as the installing user, who on a UAC-enabled machine typically has no elevated token, so `servy-cli install` fails its elevation check.
**Exit codes.** `servy-cli` exits with `0` on success and a non-zero code on failure (see [Exit Codes, Re-Runs & Cleanup](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#exit-codes-re-runs--cleanup)). `Return="check"` on `InstallServyService` makes the MSI fail and roll back when the service cannot be installed. The stop and uninstall passes use `Return="ignore"` because they exit with `1` when the service does not exist, which must not block an uninstall. `StartServyService` also uses `Return="ignore"`, so a service that does not reach the `Running` state within its start timeout leaves the installation in place; change it to `Return="check"` if a failed start should fail the setup. The sample defines no rollback action: if a later action fails after `InstallServyService` succeeded, the service stays registered unless you add a matching `Execute="rollback"` custom action that runs `servy-cli uninstall`.
**Major upgrades.** The upgrade handling above relies on the default `` scheduling (`afterInstallValidate`), which removes the previous version before the new files are installed. The `NOT UPGRADINGPRODUCTCODE` condition must already be present in the version being upgraded from, because the removal runs that version's custom actions. Running `servy-cli install` for a service that already exists updates its configuration instead of failing, and the configuration takes effect the next time the service starts.
> [!WARNING]
> **Keep secrets out of the MSI log.** Windows Installer writes property values and `CustomActionData` to verbose logs (`/l*v`). If a command line carries a secret (`--password`, or `--params` / `--envVars` holding credentials), hide it: declare the property hidden (``) and add `HideTarget="yes"` to the custom action. The command line is still visible in the process list while `servy-cli.exe` runs. `servy-cli` can read these values from environment variables instead (`SERVY_PASSWORD`, `SERVY_PROCESS_PARAMETERS`, `SERVY_ENVIRONMENT_VARIABLES`, ...), but a quiet-execution custom action cannot set them; an alternative is to install from a configuration file with `servy-cli import --config=xml --path="..." --install` and delete the file afterwards (see [Export/Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services#import) and [Security](https://github.com/aelassas/servy/wiki/Security#6-sensitive-command-line-arguments--service-account-credentials)).
## Inno Setup Integration
Inno Setup supports executing administrative commands directly using the `[Run]` and `[UninstallRun]` sections. The setup must run elevated, so keep `PrivilegesRequired=admin` (the Inno Setup default) in the `[Setup]` section.
Add the following sections to your `.iss` script:
```iss
[Files]
; Include servy-cli.exe in the installation package (a single file for the .NET 10 build)
Source: "servy\servy-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
; Your application's own files
Source: "bin\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
[Run]
; Silently register and initialize the service during installation
Filename: "{app}\servy-cli.exe"; \
Parameters: "install --name=""MyService"" --path=""{app}\myapp.exe"" --startupDir=""{app}"" --startupType=""Automatic"" --params=""--port 8080"""; \
Flags: runhidden waituntilterminated; \
StatusMsg: "Registering background service..."
; Silently start the service immediately after registration
Filename: "{app}\servy-cli.exe"; \
Parameters: "start --name=""MyService"""; \
Flags: runhidden waituntilterminated; \
StatusMsg: "Starting background service..."
[UninstallRun]
; Stop the active service process first to release file locks
Filename: "{app}\servy-cli.exe"; \
Parameters: "stop --name=""MyService"""; \
Flags: runhidden waituntilterminated; \
RunOnceId: "StopMyService"
; Silently unregister and clean up the service during uninstallation
Filename: "{app}\servy-cli.exe"; \
Parameters: "uninstall --name=""MyService"""; \
Flags: runhidden waituntilterminated; \
RunOnceId: "UninstallMyService"
[Code]
// When the setup runs over an existing installation, stop the service before its files are replaced.
// On a first install servy-cli.exe is not there yet, so nothing runs.
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
begin
if FileExists(ExpandConstant('{app}\servy-cli.exe')) then
Exec(ExpandConstant('{app}\servy-cli.exe'), 'stop --name="MyService"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
Result := '';
end;
```
Using `Flags: runhidden waituntilterminated` ensures that installation and uninstallation wait for complete vault generation and service management tasks without showing console windows to the user.
Keep the following in mind:
- **Exit codes are not checked.** Inno Setup logs the exit code of a `[Run]` or `[UninstallRun]` entry but carries on whatever it is. If a failed `servy-cli install` must fail the setup, run it from `[Code]` with `Exec` and check `ResultCode` (`0` is success).
- **Upgrades.** Running the setup again over an existing installation runs the `[Run]` entries again. `servy-cli install` updates the existing service in place instead of failing, and `start` then starts it with the new configuration. The `PrepareToInstall` function above stops the service first, so its files are not locked when they are replaced.
- **`{app}` has no trailing backslash**, so `--startupDir=""{app}""` is safe. Never pass a quoted path that ends with a backslash: the backslash escapes the closing quote.
## Advanced Installer & InstallShield
### Advanced Installer
1. Go to the **Custom Actions** page.
2. Add a new **Launch File** action under `InstallExecuteSequence` -> `Add Resources`.
3. Set **File Path** to `[#servy-cli.exe]`.
4. Set **Command Line** (Installation). `[APPDIR]` ends with a backslash, so the startup directory is written as `"[APPDIR]."`: a backslash right before a closing quote escapes the quote.
```text
install --name="MyService" --path="[APPDIR]myapp.exe" --startupDir="[APPDIR]." --startupType="Automatic" --params="--port 8080"
```
5. Set **Execution Options** to **Deferred with no impersonation** (to ensure administrative elevation) and check **Hide console window**.
6. Add a second **Launch File** action right after installation to start the service, with this command line:
```text
start --name="MyService"
```
7. Under the `Uninstall` sequence, add two more **Launch File** actions with the same execution options, the first to stop the service and the second to unregister it:
```text
stop --name="MyService"
```
```text
uninstall --name="MyService"
```
8. Condition the install and start actions with `NOT Installed AND NOT PATCH`, the stop action with `REMOVE="ALL"`, and the uninstall action with `REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE`, so an upgrade does not unregister the service (see [WiX Toolset (MSI) Integration](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#wix-toolset-msi-integration)).
### InstallShield
1. Navigate to **Behavior and Logic** -> **Custom Actions and Sequences**.
2. Right-click **Custom Actions** and select **New Executable** -> **Installed with Product**. `servy-cli.exe` must be installed with your product, because the uninstall actions run it from the installation folder.
3. Set **Target** to `[INSTALLDIR]servy-cli.exe`.
4. Set **Command Line Arguments** (Installation). `[INSTALLDIR]` ends with a backslash, so the startup directory is written as `"[INSTALLDIR]."`: a backslash right before a closing quote escapes the quote.
```text
install --name="MyService" --path="[INSTALLDIR]myapp.exe" --startupDir="[INSTALLDIR]." --startupType="Automatic" --params="--port 8080"
```
5. Add a second custom action to start the service immediately after install, with these arguments:
```text
start --name="MyService"
```
6. Add two custom actions to the uninstallation sequence, the first to stop the service and the second to unregister it, with these arguments:
```text
stop --name="MyService"
```
```text
uninstall --name="MyService"
```
7. Set **In-Script Execution** to **Deferred Execution in System Context** on all four actions.
8. Condition the install and start actions with `NOT Installed AND NOT PATCH`, the stop action with `REMOVE="ALL"`, and the uninstall action with `REMOVE="ALL" AND NOT UPGRADINGPRODUCTCODE`, so an upgrade does not unregister the service (see [WiX Toolset (MSI) Integration](https://github.com/aelassas/servy/wiki/Embedding-in-Custom-Installers#wix-toolset-msi-integration)).
## Exit Codes, Re-Runs & Cleanup
`servy-cli` is designed to be driven by scripts and installers. Its behavior in the cases an installer author runs into:
| Exit code | Meaning |
| :--- | :--- |
| `0` | The command succeeded. |
| `1` | The command failed (invalid option, missing elevation, service not found, timeout, cancellation, unexpected error). The reason is printed to the console, which a quiet-execution custom action copies into the MSI log. |
| `2` | Incompatible environment: the SQLite library is older than the minimum version Servy requires. |
- **`install` on an existing service** updates its configuration in place instead of failing, so it is safe to run again on a repair or an upgrade. A running service picks up the new configuration the next time it starts.
- **`start`** succeeds at once when the service is already running, and fails when the service does not reach the `Running` state within its start timeout.
- **`stop`** succeeds at once when the service is already stopped. It fails with exit code `1` when Servy does not know the service.
- **`uninstall`** stops the service itself and waits for it to stop before deleting it; if the service does not stop within its stop timeout, the uninstall is aborted with exit code `1` rather than leaving the service marked for deletion. It fails with exit code `1` when the service does not exist. Running `stop` first, as in the samples above, keeps the stop explicit in the setup log.
- **No console interaction is needed.** The spinner is turned off automatically when there is no interactive console, as in a hidden custom action; `--quiet` (`-q`) turns it off explicitly.
- **What uninstalling your service leaves behind.** `servy-cli uninstall` removes your service from the Service Control Manager and from `Servy.db`, and takes back the access its service account was granted (see [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening)). It does not remove the `Servy` host service, the binaries extracted into `%ProgramData%\Servy`, the database, the encryption key or the logs (the log folder of your service, `logs\services\\`, is kept for the administrators), because other Servy services on the machine may still use them. `servy-cli` has no command to remove the host service. Servy's own uninstaller removes the `Servy` host service and the extracted `*.exe` and `*.dll` files only when no Servy-managed service remains, and always keeps `db\`, `security\` and `logs\`; if you remove them from your own uninstaller, apply the same check. Never delete `security\aes_key.dat` while `Servy.db` is kept: the configuration in the database cannot be decrypted without it.
## Verification & Troubleshooting
After running your custom installer, verify that setup, startup, and removal succeed cleanly:
1. **Verify SCM Registration & Running State:** Run `sc query MyService` or check `services.msc` to confirm the service is registered and currently in the `RUNNING` state after installation. `servy-cli status --name="MyService"` reports the same from the command line.
2. **Verify Uninstallation:** Uninstall the application via Add/Remove Programs and run `sc query MyService` to ensure the service is completely removed without leaving orphaned entries.
3. **Verify Vault Initialization:** Confirm that the `Servy` Windows service is running (`sc query Servy`) and that the vault files are created in `%ProgramData%\Servy` (`db\Servy.db`, `security\aes_key.dat`) with restricted ACLs (see [Security](https://github.com/aelassas/servy/wiki/Security#the-double-lock-system)).
4. **Check for Locked Files (`DELETE_PENDING`):** Ensure the service is stopped before your application files are removed or replaced (by `servy-cli stop`, or by `servy-cli uninstall`, which stops it first) so the binaries in the application folder can be removed without requiring a system reboot.
5. **Review Installer Logs:** If the service fails to register, start, or unregister during MSI installation, run the installer with logging enabled:
```cmd
msiexec /i MySetup.msi /l*v install.log
```
Search `install.log` for `CAQuietExec` or `servy-cli` output to inspect the underlying exit code.
6. **Review Servy Logs:** `servy-cli` writes its own log to `%ProgramData%\Servy\logs\Servy.CLI.log`, the host service to `%ProgramData%\Servy\logs\Servy.Host.log`, and the wrapper of your service to `%ProgramData%\Servy\logs\services\\Servy.Service.log`. Read them from an elevated prompt: the `logs` folder is restricted to administrators and `SYSTEM`.
---
# Document: Integration with Monitoring Tools
> Source: https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#introduction)
1. [Supported Integrations](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#supported-integrations)
1. [CLI Example: Integrating with CI/CD Pipeline](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#cli-example-integrating-with-cicd-pipeline)
1. [Heartbeat URL & Out-of-Band Pings](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#heartbeat-url--out-of-band-pings)
1. [Configuration Parameters](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#configuration-parameters)
1. [Operational Execution Rules](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#operational-execution-rules)
1. [Integrating with Healthchecks.io](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools#integrating-with-healthchecksio)
## Introduction
Servy can be integrated with external monitoring and automation tools to streamline service management, alerting, and CI/CD workflows. This allows you to track service health, logs, and lifecycle events in enterprise environments.
## Supported Integrations
- **Healthchecks.io & Uptime Monitoring Services (Uptime Kuma, Pingdom, Better Stack)**
- Transmit asynchronous HTTP GET pings over out-of-band channels directly to your monitoring endpoint.
- Automatically report service startup, healthy periodic checks, and failure/recovery signals using extended lifecycle flags (`/start`, `/fail`).
- **Jenkins, TeamCity, Azure DevOps, GitHub Actions**
- Use the **Servy CLI** to install, start, stop, or uninstall services as part of your CI/CD pipelines.
- Automate deployment of services with pre-launch scripts, environment variables, and dependencies.
- See the full guide: [Servy Automation & CI/CD](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD)
- **Prometheus, Grafana & Log Shippers**
- Health check events, process lifecycle transitions, and diagnostic entries are written to both the **Windows Event Log** (the **Application** log, source `Servy`) and the service's own Servy log file:
```text
%ProgramData%\Servy\logs\services\\Servy.Service.log
```
- Example log entries:
```text
[MyApp] Health monitoring started.
[MyApp] [CheckHealth] Health check failed (1/3).
[MyApp] [CheckHealth] Health check failed (2/3).
[MyApp] [CheckHealth] Health check failed (3/3). Initiating recovery.
[MyApp] Started child process with PID: 17852
```
- **Monitoring & Automated Triggers:**
- **Log Shipping:** Tools like **Promtail**, **Vector**, or **Logstash** can tail `Servy.Service.log` to stream metrics, trigger alerts, and build Grafana dashboards (tracking uptime, restart counts, and failure rates).
- **Event Viewer Scraping:** Native Windows log forwarders or the Prometheus `windows_exporter` can collect entries from the **Application** log filtered on the `Servy` source.
- **Failure Actions:** On repeated health check failures, Servy can automatically trigger a recovery action - **Restart Service** (default), **Restart Process**, **Restart Computer**, or **None** - execute an external script (`FailureProgramPath`), or ping a webhook (`HeartbeatUrl`), which appends `/fail` when `EnableHeartbeatUrlFlags` is enabled.
- **Alerting Tools**
- Servy supports [Service Event Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications) for Windows toast notifications and email alerts when a service fails.
- This ensures users or administrators are immediately notified without constantly monitoring the service.
- For custom alerting, you can trigger scripts that integrate with Slack, Teams, or other messaging platforms.
- **Windows Event Viewer / SIEM**
- All important service events are logged in Windows Event Viewer.
- Integrate with SIEM tools (Splunk, ELK, Graylog) for centralized log aggregation and alerting.
## CLI Example: Integrating with CI/CD Pipeline
```powershell
.\servy-cli install `
--name="MyNodeApp" `
--description="Node.js API Service" `
--path="C:\Program Files\nodejs\node.exe" `
--startupDir="C:\Apps\MyNodeApp" `
--params="C:\Apps\MyNodeApp\server.js" `
--startupType="Automatic" `
--enableHealth `
--heartbeatInterval="10" `
--maxFailedChecks="3" `
--recoveryAction="RestartService" `
--stdout="C:\Logs\MyNodeApp_stdout.log" `
--stderr="C:\Logs\MyNodeApp_stderr.log" `
--enableSizeRotation `
--rotationSize="10"
```
> [!TIP]
> Use Servy CLI in your CI/CD scripts to deploy services reliably, monitor their health, and integrate with enterprise monitoring and alerting tools.
For alerts, see [Service Event Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications).
## Heartbeat URL & Out-of-Band Pings
Servy allows services to send out-of-band diagnostic heartbeat pings to external uptime and health monitoring platforms (such as [healthchecks.io](https://healthchecks.io/), Uptime Kuma, or Pingdom) via HTTP/HTTPS GET requests.
### Configuration Parameters
- **Heartbeat URL**: Optional string. An absolute HTTP/HTTPS URL (e.g., `https://hc-ping.com/your-uuid`) used for sending out-of-band diagnostic heartbeat pings to external monitoring services.
- **Heartbeat URL Timeout**: Optional integer, default is `10` seconds (range: `2`-`30` seconds). Timeout in seconds for the heartbeat HTTP GET request.
- **Heartbeat URL Flags**: Optional switch/boolean. Appends `/start` when the service starts and `/fail` when recovery fails, to the Heartbeat URL.
### Operational Execution Rules
When `HeartbeatUrl` is configured and health monitoring is enabled (`--enableHealth`), Servy transmits asynchronous HTTP GET pings to the external endpoint during service startup (`/start`), on each steady-state successful health check (base URL), on the first successful check after failures (`/start`, signalling recovery), and upon process failures or recovery triggers (`/fail`).
For full details on ping conditions, timeouts, and state management, see the canonical [Heartbeat Ping URL Logic](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery#heartbeat-ping-url-logic) documentation.
### Integrating with Healthchecks.io
To integrate a Servy-managed Windows service with [healthchecks.io](https://healthchecks.io/):
1. **Create a Check in Healthchecks.io:**
- Set the check interval to match or slightly exceed your Servy `--heartbeatInterval` (e.g., every 60 seconds with a 20-second grace period).
- Copy your check's unique ping URL (e.g., `https://hc-ping.com/5f8c8d82-3d2d-4b82-9f0a-123456789abc`).
2. **Configure Servy with CLI Flags:**
```powershell
.\servy-cli install `
--name="MyWorkerService" `
--path="C:\Services\MyWorker.exe" `
--enableHealth `
--heartbeatInterval="60" `
--maxFailedChecks="3" `
--recoveryAction="RestartService" `
--heartbeatUrl="https://hc-ping.com/5f8c8d82-3d2d-4b82-9f0a-123456789abc" `
--heartbeatUrlTimeoutSeconds="10" `
--enableHeartbeatUrlFlags
```
---
# Document: Service Event Notifications
> Source: https://github.com/aelassas/servy/wiki/Service-Event-Notifications
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#introduction)
1. [Requirements](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#requirements)
1. [Common](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#common)
1. [For Toast notifications (`ServyFailureNotification.ps1`)](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#for-toast-notifications-servyfailurenotificationps1)
1. [For Email notifications (`ServyFailureEmail.ps1`)](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#for-email-notifications-servyfailureemailps1)
1. [For Heartbeat Ping URL Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#for-heartbeat-ping-url-notifications)
1. [Setup Toast Notifications via Task Scheduler](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#setup-toast-notifications-via-task-scheduler)
1. [Manual Notification (Optional)](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#manual-notification-optional)
1. [Email Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#email-notifications)
1. [Generating credentials as SYSTEM](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#generating-credentials-as-system)
1. [Heartbeat Ping URL Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#heartbeat-ping-url-notifications)
1. [External Alerts via Heartbeat URLs (Slack, Teams, Phone Call, etc.)](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#external-alerts-via-heartbeat-urls-slack-teams-phone-call-etc)
1. [How can I get failure alerts sent to Slack, Microsoft Teams, Phone Call, or WhatsApp?](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#how-can-i-get-failure-alerts-sent-to-slack-microsoft-teams-phone-call-or-whatsapp)
1. [Limitations](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#limitations)
1. [Tips](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#tips)
1. [Troubleshooting](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#troubleshooting)
## Introduction
Servy can notify users when a managed service fails by showing interactive Windows toast notifications, sending an email, or firing out-of-band HTTP heartbeat pings to external monitoring services. This allows administrators to be immediately aware of service crashes or health check failures without constantly monitoring the application interface.
## Requirements
### Common
* Servy must be installed and running.
* Access to the Windows Application Event Log.
### For Toast notifications (`ServyFailureNotification.ps1`)
* Windows 10 or 11, Windows Server 2016+
* PowerShell 5.1+ (Windows Runtime toast APIs)
### For Email notifications (`ServyFailureEmail.ps1`)
* Windows 7 SP1+ / Windows Server 2008 R2+
* PowerShell 5.1+ (`Get-WinEvent`)
* SMTP relay reachable from the host
### For Heartbeat Ping URL Notifications
* Direct or proxy-routed HTTP/HTTPS outbound connectivity to your designated monitoring endpoint (e.g., [healthchecks.io](https://healthchecks.io/), Uptime Kuma, Pingdom).
## Setup Toast Notifications via Task Scheduler
1. Install Servy as usual.
2. Ensure the notification script exists at:
```text
%ProgramFiles%\Servy\taskschd\ServyFailureNotification.ps1
```
3. Import the Scheduled Task:
* Open **Task Scheduler**.
* Click **Import Task…** from the right-hand Actions pane.
* Navigate to and select:
```text
%ProgramFiles%\Servy\taskschd\ServyFailureNotification.xml
```
* In the **General Tab**:
* Ensure **"Run only when user is logged on"** is selected (Toasts cannot render if the user is not logged into an active desktop session).
* Check **"Run with highest privileges"** to ensure the script has permission to read the Event Log.
Once configured, Windows will automatically trigger this task whenever Servy writes a new Error event to the Application log.
> [!IMPORTANT]
> If you are using the portable version of Servy, you must edit `ServyFailureNotification.xml` and replace `{SERVY_INSTALL_PATH}` with the absolute path to the directory containing your Servy executable.
## Manual Notification (Optional)
You can test or run the notification script manually in a PowerShell window:
```powershell
& "C:\Program Files\Servy\taskschd\ServyFailureNotification.ps1"
```
The script will automatically parse the Event Log for the latest Servy errors and display a toast containing the service name and the specific error message.
> [!NOTE]
> The scheduled tasks are unaffected by the machine's PowerShell execution policy - they run through `ServyFailureNotification.vbs`, which invokes PowerShell with `-ExecutionPolicy Bypass`. Only this manual invocation is subject to it. If it is blocked, run the script the same way the task does rather than changing the machine policy:
> ```powershell
> powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Program Files\Servy\taskschd\ServyFailureNotification.ps1"
> ```
## Email Notifications
For server environments where users may not have an active desktop session, Servy can be configured to send email notifications.
1. Install Servy as usual.
2. Configure your SMTP settings by editing `%ProgramFiles%\Servy\taskschd\smtp-config.xml`:
```xml
smtp.example.com
587
true
30000
servy.notifications@example.com
admin1@example.com;admin2@example.com
```
3. To avoid hardcoding passwords in plaintext, the email script requires an encrypted XML credential file. Open PowerShell as Administrator and run the following block:
```powershell
$targetDir = "C:\Program Files\Servy\taskschd"
$cred = Get-Credential
$cred | Export-Clixml -Path (Join-Path $targetDir "smtp-cred.xml")
```
> [!IMPORTANT]
> **CRITICAL SECURITY NOTE:** Windows encrypts this XML file using the Data Protection API (DPAPI). This means the file can **only be decrypted by the exact user account that created it**. You must run the `Get-Credential` command while logged in as the same user account that will run the Scheduled Task. If the task runs as `SYSTEM`, you must generate the credentials as `SYSTEM`.
### Generating credentials as SYSTEM
If you have explicitly changed the Scheduled Task to run as `NT AUTHORITY\SYSTEM` (via "Change User or Group..." - the imported task defaults to the account that imported it), you must generate `smtp-cred.xml` from a SYSTEM-context PowerShell session. The recommended approach is via [PsExec](https://learn.microsoft.com/sysinternals/downloads/psexec):
```powershell
# From an elevated admin prompt, after downloading PsExec:
psexec.exe -i -s powershell.exe
# In the new SYSTEM-context window (verify with: whoami → "nt authority\system"):
$targetDir = "C:\Program Files\Servy\taskschd"
$cred = Get-Credential
$cred | Export-Clixml -Path (Join-Path $targetDir "smtp-cred.xml")
```
If you cannot use PsExec, change the Scheduled Task to run under a dedicated service account instead - that account can then generate `smtp-cred.xml` from its own logon session.
4. Import the Scheduled Task:
* Open **Task Scheduler**.
* Click **Import Task…**
* Select:
```text
%ProgramFiles%\Servy\taskschd\ServyFailureEmail.xml
```
* In the **General Tab**:
* Select **"Run whether user is logged on or not"**.
* Check **"Run with highest privileges"**.
* Ensure the user specified in the security options matches the user who generated `smtp-cred.xml` in Step 3.
> [!IMPORTANT]
> If you are using the portable version of Servy, you must edit `ServyFailureEmail.xml` and replace `{SERVY_INSTALL_PATH}` with the absolute path to the directory containing your Servy executable.
## Heartbeat Ping URL Notifications
In addition to local toasts and emails, Servy supports real-time out-of-band notification pings directly to external uptime and health monitoring services (such as [healthchecks.io](https://healthchecks.io/), Uptime Kuma, or Pingdom).
Servy executes asynchronous HTTP GET calls based on operational lifecycle states:
* **Startup Notification (`/start`):** When `--enableHeartbeatUrlFlags` is enabled, Servy issues a ping to `https://hc-ping.com/your-uuid/start` upon service startup to notify your monitoring platform that the process has initialized.
* **Periodic Health Check Ping:** A health check that finds the process healthy in steady state pings the exact base URL (`https://hc-ping.com/your-uuid`) to refresh the uptime timer. A healthy check that follows one or more failed checks instead re-sends `/start` (when `--enableHeartbeatUrlFlags` is enabled), signalling that the service recovered - see [Health Monitoring & Recovery](https://github.com/aelassas/servy/wiki/Health-Monitoring-&-Recovery) for the full ping rules.
* **Failure Alert Ping (`/fail`):** When a child process crashes, health check retries fail, or restart quotas are exceeded, Servy immediately fires a ping to `https://hc-ping.com/your-uuid/fail` (when `--enableHeartbeatUrlFlags` is enabled) to trigger instant alerts across your connected platforms (Slack, PagerDuty, Teams, SMS).
```powershell
.\servy-cli install `
--name="MyWorkerService" `
--path="C:\Services\Worker.exe" `
--enableHealth `
--heartbeatInterval="60" `
--maxFailedChecks="3" `
--recoveryAction="RestartService" `
--heartbeatUrl="https://hc-ping.com/your-uuid" `
--heartbeatUrlTimeoutSeconds="10" `
--enableHeartbeatUrlFlags
```
For full details on configuring out-of-band ping intervals and timeout limits, see [Integration with Monitoring Tools](https://github.com/aelassas/servy/wiki/Integration-with-Monitoring-Tools).
## External Alerts via Heartbeat URLs (Slack, Teams, Phone Call, etc.)
### How can I get failure alerts sent to Slack, Microsoft Teams, Phone Call, or WhatsApp?
Servy relies on out-of-band monitoring services to deliver real-time incident alerts. Point Servy's **Heartbeat URL** to a ping provider like [healthchecks.io](https://healthchecks.io/), which natively connects to downstream notification platforms.
1. Create a check in your monitoring service (e.g., `healthchecks.io`) and copy its unique ping URL.
2. Enter the URL into Servy's **Heartbeat URL** setting and enable **Heartbeat URL Flags**.
3. In your monitoring service dashboard, attach your preferred integration channels.
When Servy sends a `/fail` signal or stops pinging because the host machine went down, the monitoring platform triggers alerts to your configured channels, including:
* **Chat & Collaboration:** Slack, Microsoft Teams, Discord, Telegram, WhatsApp, Signal, Google Chat, Matrix, Mattermost, Rocket.Chat, Zulip.
* **Incident Management & Webhooks:** PagerDuty, Opsgenie, Splunk On-Call, Spike.sh, PagerTree, custom Webhooks.
* **Direct Notifications:** Email, SMS, Phone Call, Pushbullet, Pushover, ntfy, Gotify.
* **Issue & Event Tracking:** GitHub Issues, Trello, Prometheus.
> [!TIP]
> For local or isolated alerting without an external ping service, you can also use Servy's **Failure Program Path** to execute a local script (such as a PowerShell script calling a Slack Webhook) after all recovery attempts fail.
## Limitations
* **Interactive Session Required:** Toast notifications operate strictly in the user context (Session 1+). They will not appear if the task is run as `SYSTEM` (Session 0) or if the user is completely logged out.
* **Event Log Dependency:** Task Scheduler triggers for toast and email scripts are bound to the Windows Event Log. Notifications will only fire for events logged specifically by the "Servy" source with an Error level.
* **Credential Portability:** The `smtp-cred.xml` file cannot be copied to another machine or used by another user account without being regenerated.
* **Network Connectivity for Heartbeat Pings:** Heartbeat URL pings require outbound HTTP/HTTPS communication. High-security offline environments without internet or proxy routing will drop out-of-band ping attempts.
## Tips
* **State Tracking:** The PowerShell scripts maintain a `.dat` timestamp file (e.g., `last-processed-toast.dat`) in the `taskschd` directory to ensure they do not send duplicate alerts for the same error. If you need to test the notification pipeline, delete this file. **Note:** Only the single most recent error will be processed on the next run; older unread errors will be dropped to prevent alert floods. To replay history, set the file's contents to a timestamp older than the events you want to see (in ISO 8601 `o` format, e.g. `2026-01-01T00:00:00.0000000Z`).
* **Customization:** You can safely edit the `.ps1` scripts to customize the toast notification titles, icons, or email HTML bodies to fit your organization's monitoring standards.
* **Defense in Depth:** Combine Toasts, Emails, and Heartbeat Ping URLs. Toasts notify developers locally, emails notify sysadmins via inbox, and Heartbeat Ping URLs integrate into automated incident response tools (PagerDuty, Slack).
## Troubleshooting
* **Symptom:** Emails are never sent, and the fallback log (`ServyFailureEmail.log`) shows `"Key not valid for use in specified state"` or a similar `CryptographicException`.
* **Cause:** This indicates the `smtp-cred.xml` credential file was encrypted by a different Windows account than the Scheduled Task's Run-As identity.
* **Fix:** Refer to the [Generating credentials as SYSTEM](https://github.com/aelassas/servy/wiki/Service-Event-Notifications#generating-credentials-as-system) section to regenerate the credential file using the exact user context configured in the task.
---
# Document: Backup / Restore & VM Cloning
> Source: https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#introduction)
1. [Backup & Restore Utility Scripts](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#backup--restore-utility-scripts)
* [Servy-Dump.ps1](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#servy-dumpps1)
* [Servy-Restore.ps1](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#servy-restoreps1)
1. [Security Warnings & Credential Handling](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#security-warnings--credential-handling)
* [What does not survive a dump/restore cycle?](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#what-does-not-survive-a-dumprestore-cycle)
1. [Virtual Machine Cloning & Migration (VMware / Hyper-V)](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#virtual-machine-cloning--migration-vmware--hyper-v)
* [1. Will cloning VMs or deploying from templates break entropy and encryption?](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#1-will-cloning-vms-or-deploying-from-templates-break-entropy-and-encryption)
* [2. Will changing Virtual CPU or RAM configurations break encryption?](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#2-will-changing-virtual-cpu-or-ram-configurations-break-encryption)
* [3. Will changing Virtual NIC MAC addresses break encryption?](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#3-will-changing-virtual-nic-mac-addresses-break-encryption)
* [4. How does key storage differ from dynamic entropy?](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#4-how-does-key-storage-differ-from-dynamic-entropy)
1. [Recommended VMware / Golden Image Deployment Workflow](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#recommended-vmware--golden-image-deployment-workflow)
* [Automation Steps](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#automation-steps)
1. [See Also](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning#see-also)
## Introduction
This document covers backup and recovery procedures for Servy service configurations using the official PowerShell utility scripts (`Servy-Dump.ps1` and `Servy-Restore.ps1`), along with architectural guidelines for machine migrations, Windows reinstalls, virtual machine cloning, imaging, and template migrations (VMware, Hyper-V, Azure, AWS).
Servy automatically encrypts sensitive fields stored in its configuration database; including process parameters, environment variables, passwords, API keys, and pre/post lifecycle hooks; using a master AES key bound to the local OS installation via Windows Data Protection API (DPAPI) and the system's `MachineGuid` (`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid`). Because this cryptographic vault is strictly tied to the specific Windows installation, performing operations that alter machine identity; such as copying database files to another host, reinstalling Windows, restoring OS disk images, or cloning virtual machines (VMware/Hyper-V); permanently breaks decryption capabilities for those protected fields.
The `Servy-Dump.ps1` and `Servy-Restore.ps1` scripts exist to bridge this gap: they export service definitions (without credentials) into XML archives on the source machine and re-import them on the target, where a fresh, machine-bound cryptographic vault is initialized during the restore.
## Backup & Restore Utility Scripts
Starting with **v10.0+**, Servy includes two administrative PowerShell scripts located directly in `%ProgramFiles%\Servy\` (or the root of portable distributions) to streamline environment migrations, backup routines, and template-based provisioning. For versions prior to v10.0, the scripts can be downloaded directly from the official repository:
* [`Servy-Dump.ps1`](https://github.com/aelassas/servy/blob/main/src/Servy.CLI/Servy-Dump.ps1) (`main` branch)
* [`Servy-Restore.ps1`](https://github.com/aelassas/servy/blob/main/src/Servy.CLI/Servy-Restore.ps1) (`main` branch)
For the .NET Framework 4.8 build, the scripts can be downloaded directly from the `net48` branch:
* [`Servy-Dump.ps1`](https://github.com/aelassas/servy/blob/net48/src/Servy.CLI/Servy-Dump.ps1) (`net48` branch)
* [`Servy-Restore.ps1`](https://github.com/aelassas/servy/blob/net48/src/Servy.CLI/Servy-Restore.ps1) (`net48` branch)
> [!IMPORTANT]
> **SYSTEM REQUIREMENTS & OS FLOORS**
>
> Both scripts require Administrator privileges (returns `exit code 1` if not run as Administrator).
> * **`main` Branch (.NET 10.0+ / Modern Builds):**
> * **`Servy-Dump.ps1`**: Requires **Windows 10 / Windows Server 2016 or later** and **PowerShell 5.1+**. It queries `Servy.db` using the OS-native `%SystemRoot%\System32\winsqlite3.dll` with native UTF-16 marshaling.
> * **`Servy-Restore.ps1`**: Requires **Windows 10 / Windows Server 2016 or later** and **PowerShell 5.1+**.
>
> * **`net48` Branch (.NET Framework 4.8 / Legacy Builds):**
> * **`Servy-Dump.ps1`**: Supports **Windows 7 SP1 / Windows Server 2008 R2 or later** and **PowerShell 2.0+**. Uses a multi-tier database inspection layer (prefers `System.Data.SQLite.dll` or `e_sqlite3.dll` in the Servy installation directory, with dynamic fallback to `winsqlite3.dll` / `sqlite3.dll`).
> * **`Servy-Restore.ps1`**: Supports **Windows 7 SP1 / Windows Server 2008 R2 or later** and **PowerShell 2.0+**. Uses native COM `Shell.Application` extraction as a fallback when `Expand-Archive` or `.NET ZipFile` is unavailable.
>
> * **Cross-Version Restore Compatibility:**
>
> `Servy-Restore.ps1` has no direct SQLite interop dependency; it operates via `Import-ServyServiceConfig`. Dump archives generated on modern machines can be restored directly onto legacy Windows 7 / Server 2008 R2 targets using the `net48` branch version of `Servy-Restore.ps1`.
>
### Servy-Dump.ps1
`Servy-Dump.ps1` inspects the local Servy SQLite database (`%ProgramData%\Servy\db\Servy.db`), enumerates all registered service definitions, and prompts once for confirmation via `ShouldProcess` before exporting the configurations into individual XML files using `Export-ServyServiceConfig`. See [Export & Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services) for the configuration file format and the complete field reference. All XML definitions are then compressed into a single consolidated `.zip` archive along with a `.sha256` sidecar file for integrity verification. If `-Uninstall` is specified, that same prompt also covers the uninstallation from the Windows SCM and database.
#### Script Features
* **Native & Multi-Tier Interop:** On the `main` branch, queries `Servy.db` using the Windows native `%SystemRoot%\System32\winsqlite3.dll` without requiring external DLL installations. On the `net48` branch, uses managed `System.Data.SQLite.dll` / `e_sqlite3.dll` with dynamic `kernel32` fallback.
* **Per-Service Error Isolation:** Individual export failures do not delete successfully exported files or abort the entire process. If at least one service exports, the zip archive is produced and exit code `7` is returned. A failure to write the `.sha256` sidecar is reported the same way: the archive is kept and exit code `7` is returned.
* **Sanitized Filenames & Encoding Safety:** Automatically sanitizes service names containing illegal filesystem characters and uses native UTF-16 marshaling to safely handle Unicode service names (e.g., `Café-Svc` or `服务`).
* **Safety Checks:** Requires elevated Administrator privileges and blocks accidental overwrites unless explicit permission is granted.
#### Syntax & Usage
```powershell
# Basic usage (fails with exit code 3 if destination archive already exists)
.\Servy-Dump.ps1 -DestinationArchivePath "C:\Backups\Servy_Dump.zip"
# Force overwrite of existing dump archive
.\Servy-Dump.ps1 -DestinationArchivePath "C:\Backups\Servy_Dump.zip" -Overwrite
# Force overwrite of existing dump archive, uninstall each successfully exported
# service from the Windows SCM, and remove it from the Servy database (prompts for uninstallation confirmation)
.\Servy-Dump.ps1 -DestinationArchivePath "C:\Backups\Servy_Dump.zip" -Overwrite -Uninstall
# Suppress confirmation prompts for uninstallation in an automated workflow
.\Servy-Dump.ps1 -DestinationArchivePath "C:\Backups\Servy_Dump.zip" -Overwrite -Uninstall -Confirm:$false
```
#### Parameters
| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `-DestinationArchivePath` | `String` | **Yes** | Target zip archive destination file (e.g., `C:\Backups\Servy_Dump.zip`). If a directory path or trailing separator is provided, it writes to `$DestinationArchivePath\Servy_Dump.zip`; if no file extension is specified, `.zip` is appended. Use `-Overwrite` to replace an existing archive. |
| `-Overwrite` | `Switch` | No | Overwrite the destination dump archive if it already exists. |
| `-Uninstall` | `Switch` | No | When present, uninstalls each successfully exported service from the Windows SCM and removes it from the Servy database after successful export. The script's single `ShouldProcess` prompt covers this as well, unless `-Confirm:$false` is specified. |
#### Exit Codes
| Code | Meaning |
| --- | --- |
| `0` | Success. All registered service configurations were successfully exported and archived. (Note: Also returned when no database exists or no services are registered, in which case **no archive is written**). |
| `1` | Not running with Administrator privileges. |
| `2` | The Servy PowerShell module (`Servy.psm1`) could not be located or imported. |
| `3` | The destination archive already exists and `-Overwrite` was not specified. |
| `4` | I/O & Inspection Failure. The database could not be read, the destination path is invalid or unwritable, an existing SHA-256 sidecar could not be replaced under `-Overwrite`, archive compression or ACL hardening failed, or an unexpected runtime error occurred. |
| `5` | Setup Compilation Failure. Failed to compile native SQLite dynamic P/Invoke assembly bindings. |
| `6` | Complete Export Failure. No service configurations could be exported; no output archive was generated. |
| `7` | Partial Export Warning. The dump archive was created, but one or more services failed to export or uninstall, or the `.sha256` sidecar could not be written. Without the sidecar, `Servy-Restore.ps1` refuses the archive (exit `5`) unless `-SkipIntegrityCheck` is used; regenerate it with `Get-FileHash` before restoring. |
| `8` | Archive Staging Mismatch. Staged configuration count does not match exported count; dump aborted. |
> [!WARNING]
> In an automated backup job, check that the archive file exists and is
> non-empty in addition to checking the exit code. Exit code `0` alone does not
> guarantee an archive was produced.
> [!WARNING]
> **SECURITY & FILE PERMISSIONS NOTICE**
>
> Dump archives created by `Servy-Dump.ps1` (`.zip` dumps and `.sha256` sidecars) contain unencrypted, plaintext service configurations. No credentials of any kind are exported (`UserAccount`, `Password`, and `RunAsLocalSystem` are omitted from every export), but sensitive data such as execution parameters, API keys, command-line arguments, environment variables, and pre/post hooks are written in plaintext.
>
> To prevent credential and configuration exposure, `Servy-Dump.ps1` automatically enforces strict Windows Access Control Lists (ACLs) on all generated output files:
> * Permission inheritance from parent directories is broken.
> * All broad group permissions (`Users`, `Authenticated Users`, `Everyone`) and custom user ACEs are stripped.
> * Access is restricted exclusively to **Built-in Administrators** (`S-1-5-32-544`) and **Local SYSTEM** (`S-1-5-18`).
>
> If secondary service runner accounts require read access to dump archives, administrators must explicitly grant those permissions after the dump completes.
> [!TIP]
> `Servy-Dump.ps1` prompts for confirmation before exporting configurations and uninstalling services. To suppress prompts in an automated workflow, use `-Confirm:$false`:
> ```powershell
> .\Servy-Dump.ps1 -DestinationArchivePath "C:\Backups\Servy_Dump.zip" -Overwrite -Uninstall -Confirm:$false
> ```
### Servy-Restore.ps1
`Servy-Restore.ps1` ingests a consolidated `.zip` dump archive generated by `Servy-Dump.ps1`, verifies its integrity against the accompanying `.sha256` sidecar file (which must be located in the same directory as the dump file), extracts the individual service XML files into a secure staging location, and prompts once for confirmation via `ShouldProcess` before importing the configurations into the local Servy database via `Import-ServyServiceConfig` (and optionally installing services into the SCM when `-Install` is supplied), unless `-Confirm:$false` is specified.
#### Syntax & Usage
```powershell
# Restore service configurations (imports definitions into the Servy database)
.\Servy-Restore.ps1 -DumpArchivePath "C:\Backups\Servy_Dump.zip"
# Restore service configurations AND install them into Windows SCM with confirmation prompt
.\Servy-Restore.ps1 -DumpArchivePath "C:\Backups\Servy_Dump.zip" -Install
# Restore service configurations AND install them into Windows SCM without confirmation prompt
.\Servy-Restore.ps1 -DumpArchivePath "C:\Backups\Servy_Dump.zip" -Install -Confirm:$false
# Restore service configurations AND install them into Windows SCM without SHA-256 sidecar integrity verification
.\Servy-Restore.ps1 -DumpArchivePath "C:\Backups\Servy_Dump.zip" -Install -SkipIntegrityCheck
```
> [!WARNING]
> **RESTORING OVERWRITES EXISTING SERVICES**
>
> `Servy-Restore.ps1` asks for one confirmation for the whole archive (`ConfirmImpact = 'High'`),
> then imports every configuration in it. If a service of the same name already exists on the
> target machine, its stored configuration is **replaced**; and with the `-Install` option, its
> Windows SCM registration is rewritten.
> There is no undo capability: any configuration changes made on the target since the dump was taken will be overwritten.
> `-Confirm:$false` suppresses the prompt, so an unattended restore overwrites without asking.
>
> This behavior is intentional in the golden image cloning workflow below, where stale inherited configuration is purged prior to importing. However, be deliberate when restoring onto a machine that is actively in service.
>
> **Important Cryptographic Prerequisite:**
>
> You can only create a pre-restore safety dump if the target system's local machine identity and DPAPI keys are intact. If the target OS has already undergone Sysprep, a Windows reinstall, or a machine SID regeneration, its existing database cannot be decrypted by `Servy-Dump.ps1`; the stale vault (`%ProgramData%\Servy\db` and `%ProgramData%\Servy\security`) must be purged instead. Do not delete the entire `%ProgramData%\Servy` directory, as `%ProgramData%\Servy\logs` contains valuable runtime logs that should be preserved.
>
> If the target machine's DPAPI scope is intact and active services are running, take a fresh safety dump **before** restoring over them:
>
> ```powershell
> # Take a safety backup on an active, healthy host prior to running a restore
> .\Servy-Dump.ps1 -DestinationArchivePath "C:\Backups\Servy_PreRestore.zip" -Overwrite
> ```
#### Parameters
| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `-DumpArchivePath` | `String` | **Yes** | Path specifying the target `.zip` dump archive to restore. |
| `-Install` | `Switch` | No | Automatically registers each imported service with the Windows Service Control Manager (SCM). The single confirmation prompt the script shows then covers both the import and the installation, unless `-Confirm:$false` is specified.|
| `-SkipIntegrityCheck` | `Switch` | No | Skips SHA-256 sidecar verification entirely: the archive is restored without an integrity check, whether the `.sha256` sidecar is absent, stale, or mismatching. |
| `-MaxAllowedEntries` | `Int32` | No | Maximum number of entries permitted in the archive to prevent zip bomb attacks during extraction (defaults to `1000`, range: `1`-`100,000`). |
| `-MaxUncompressedBytes` | `Int64` | No | Maximum total uncompressed size in bytes permitted during extraction (defaults to `104857600` bytes / 100 MB, range: `1`-`10737418240` bytes / 10 GB). |
> [!TIP]
> `Servy-Restore.ps1` prompts for confirmation before importing configurations into the database and installing services into the SCM. To suppress prompts in an automated workflow, use `-Confirm:$false`:
> ```powershell
> .\Servy-Restore.ps1 -DumpArchivePath "C:\Backups\Servy_Dump.zip" -Install -Confirm:$false
> ```
#### Exit Codes
| Code | Meaning |
| --- | --- |
| `0` | Success. Also returned when the archive contains no XML configuration files, in which case **nothing is imported**. |
| `1` | Not running with Administrator privileges. |
| `2` | The Servy PowerShell module (`Servy.psm1`) could not be located or imported. |
| `3` | The specified dump archive does not exist. |
| `4` | I/O & Extraction Failure. The archive path is invalid, the archive could not be extracted, ACL hardening failed, malformed entries were detected, the `-MaxAllowedEntries` or `-MaxUncompressedBytes` safety limit was exceeded, or an unexpected runtime error occurred. |
| `5` | Checksum Verification Failure. The `.sha256` sidecar is missing (without `-SkipIntegrityCheck`) or a hash mismatch was detected. |
| `6` | Complete Import Failure. No service configurations could be imported from the archive. |
| `7` | Partial Import Warning. The restore completed, but one or more services failed to import. |
## Security Warnings & Credential Handling
> [!CAUTION]
> **CRITICAL SECURITY WARNING: UNENCRYPTED CONFIGURATION**
>
> The dump archive generated by `Servy-Dump.ps1` contains **unencrypted
> plain-text XML files**. No credentials of any kind are exported (`UserAccount`,
> `Password`, and `RunAsLocalSystem` are omitted from every export), but sensitive
> data such as execution parameters, API keys, command-line arguments, environment
> variables, and pre/post hooks are written in plaintext. Restrict access to
> generated dump archives to authorized administrative personnel only.
> See [Export & Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services) for the full field reference.
>
> The archive also contains every **executable path** the service configuration
> references: the wrapped program and its startup directory, the failure
> program, and the pre-launch, post-launch, pre-stop, and post-stop hook executables with their
> arguments and environment.
>
> Because restored services default to `LocalSystem` (see the callout below), anyone able to **modify** a dump archive controls what runs as `LocalSystem` on every machine restored from it. Restrict **write** access as strictly as read access, and store dump archives in a directory whose ACL grants access only to Administrators and SYSTEM, which is the same protection the installer applies to `%ProgramData%\Servy`:
>
> ```powershell
> icacls "C:\Staging" /inheritance:r /grant:r "*S-1-5-32-544:(OI)(CI)F" "*S-1-5-18:(OI)(CI)F"
> ```
> [!IMPORTANT]
> **CREDENTIAL RESET TO LOCALSYSTEM ON RESTORE**
>
> For security reasons, Servy does not export Windows Service Account credentials (usernames and passwords). Restoring configurations via `Servy-Restore.ps1`, `servy-cli`, or Servy Manager will automatically reset all service logon identities to `LocalSystem`.
>
> **Post-Restore Action Required:**
>
> If any restored service runs under a custom account
> (`.\svc_account`, `DOMAIN\svc_account`, or gMSA), you must manually re-enter the logon
> **username and password** via [Servy Manager](https://github.com/aelassas/servy/wiki/Servy-Manager), [`servy-cli`](https://github.com/aelassas/servy/wiki/Servy-CLI)
> or the [PowerShell module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module). Reinstalling the service this way re-applies the hardening
> automatically (v10.2+): the account gets write access to the log folder of the service (`logs\services\\`) and access to the `Servy` service's named pipe,
> and the binaries and configuration files are hardened (see [Security](https://github.com/aelassas/servy/wiki/Security)). On Servy 10.1 and earlier, also re-run
> `Set-ServyExePermissions.ps1` for the account (see [Servy 10.1 and Earlier](https://github.com/aelassas/servy/wiki/Security#servy-101-and-earlier)).
### What does not survive a dump/restore cycle?
Not every field in a service definition is carried through an export. Plan for these
before relying on a restored clone being identical to its template:
| Field | Behavior on import | Operational impact |
| --- | --- | --- |
| `UserAccount` | Not exported; reset to `LocalSystem` | Re-enter for every service using a custom identity |
| `Password` | Not exported; reset to empty | Re-enter alongside `UserAccount` |
| `RunAsLocalSystem` | Not exported; forced to the `LocalSystem` baseline | This is the field that causes the reset above |
| `Pid` | Silently ignored | Runtime state; expected |
| `PreviousStopTimeout` | Silently ignored | Recovery tuning is not carried over |
| `ActiveStdoutPath` | Silently ignored | The resolved log destination is re-derived on the clone |
| `ActiveStderrPath` | Silently ignored | As above |
| `RestartAttempts` | Silently ignored | The restart attempts counter starts empty on the clone; recovery counts from zero |
| `RestartAttemptsUpdatedAtTicks` | Silently ignored | As above (the time the counter was last written) |
A warning is written to the log when a custom identity is discarded on import. The six
silently-ignored fields produce no warning at all, so check them explicitly if your
template depends on them.
See [Export & Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services) for the complete field reference.
## Virtual Machine Cloning & Migration (VMware / Hyper-V)
When managing virtualized infrastructure (VMware vSphere, Hyper-V, Azure VMs, AWS EC2), understanding how Servy handles cryptographic machine identity is essential for template-based provisioning and image cloning.
### 1. Will cloning VMs or deploying from templates break entropy and encryption?
**It depends on whether the clone is generalized.**
Servy binds its master AES key to two machine-specific inputs: the Windows DPAPI
LocalMachine master key, and registry entropy read from
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid`.
* **Sysprep / guest customization (`sysprep /generalize`, VMware Guest OS
Customization, Azure and AWS image deployment): YES, decryption breaks.**
Generalization regenerates both the machine SID and `MachineGuid`, and resets
the DPAPI machine keys. The cloned instance cannot decrypt `Password`,
`Parameters`, `EnvironmentVariables` or any other protected field in
`%ProgramData%\Servy\db\Servy.db`. Follow the workflow below.
* **A raw clone with no generalization (VMware "Clone" without customization, a
Hyper-V export/import, a restored disk image): NO, decryption continues to
work.** Both the registry hive and the DPAPI machine keys are files on the
copied disk, so they are identical to the source and the existing vault stays
readable. No purge or re-import is required.
* **The source machine / template master** is unaffected in either case.
> [!WARNING]
> A raw clone keeps the source machine's keys, which means every clone shares
> them. If that is not acceptable in your environment, generalize the image and
> follow the workflow below so each instance derives its own key.
### 2. Will changing Virtual CPU or RAM configurations break encryption?
**NO.**
Servy does **not** bind its keying material to hardware metrics such as CPU ID, RAM capacity, BIOS UUIDs, or motherboard serial numbers. It binds exclusively to the Windows DPAPI master key and the OS-stored registry value (`MachineGuid`). Modifying vCPU or RAM allocations in VMware has zero impact on cryptographic decryption.
### 3. Will changing Virtual NIC MAC addresses break encryption?
**NO.**
Servy does not inspect or bind keying material to network adapters, IP addresses, or MAC addresses. Swapping virtual NICs, reconfiguring networks, or upgrading VMware Tools will not invalidate existing decryption keys.
### 4. How does key storage differ from dynamic entropy?
* **Key Storage:** Encrypted key material (`aes_key.dat`) is stored on disk in `%ProgramData%\Servy\security\`.
* **Dynamic Entropy:** Additional runtime entropy is derived directly from the operating system registry (`MachineGuid`).
If `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid` is missing, unreadable, or restricted by registry permissions during initial key generation, Servy falls back to using the host's `Environment.MachineName` as its dynamic entropy source and logs a critical security degradation event. A hostname is predictable and not unique per OS installation; two clones sharing a hostname would derive identical entropy. Furthermore, if a system running under this fallback state is subsequently renamed, the entropy calculation changes and breaks decryption for all protected fields.
> [!WARNING]
> **DYNAMIC ENTROPY DEPENDENCY & REGISTRY SENSITIVITY**
>
> Servy binds its DPAPI master key (`%ProgramData%\Servy\security\aes_key.dat`) to the system's `MachineGuid`. If `MachineGuid` is modified, deleted, corrupted, or blocked by registry permissions on an active installation, Servy will fail to unprotect its keying material; causing Servy Manager, `servy-cli`, and the background service to immediately fail decryption and halt operations.
> If you encounter a decryption failure on an existing installation:
> * Restore the original `MachineGuid` string in `HKLM\SOFTWARE\Microsoft\Cryptography`.
> * Fix registry Access Control Lists (ACLs) if permission restrictions prevent Servy from reading `MachineGuid`.
> * Revert the hostname if running under the fallback state.
> * Otherwise, if a valid pre-existing dump archive is available, purge the stale vault (`%ProgramData%\Servy\db` and `%ProgramData%\Servy\security`) and restore configurations using `Servy-Restore.ps1`.
> [!NOTE]
> Key material written by Servy 7.8 or earlier carries no entropy. Servy reads it
> through a compatibility path, logs a `SECURITY DEGRADATION WARNING`, and
> transparently re-saves it in the entropy-protected format on first successful
> read. Seeing that warning once per file during an upgrade is expected.
When machine identity is altered (such as on generalized clones, Windows reinstalls, or OS migrations):
* **Windows DPAPI host keys change:** the OS resets its local cryptographic master keys.
* **Registry `MachineGuid` changes:** the OS customization process generates a new unique identifier for the installation.
Both alterations invalidate the decryption capability of the DPAPI master key (`%ProgramData%\Servy\security\aes_key.dat`) copied over from the original machine.
## Recommended VMware / Golden Image Deployment Workflow
To avoid DPAPI decryption failures across cloned virtual machines, do **not** attempt to copy `%ProgramData%\Servy\security\aes_key.dat` between OS instances. Instead, leverage `Servy-Dump.ps1` and `Servy-Restore.ps1` within your automated post-clone customization pipeline:
```text
[ Golden Image / Template ]
│
├── 1. Install Servy (%ProgramFiles%\Servy)
├── 2. Run Servy-Dump.ps1 (if pre-configured services exist)
│ └─> .\Servy-Dump.ps1 -DestinationArchivePath "C:\Sysprep\Servy_Base_Dump.zip" -Overwrite
│
▼ (VMware Clone / Sysprep Deployment)
[ New Cloned VM Instance ]
│
├── 3. Execute Sysprep / Guest Customization (New IP, Hostname, MachineGuid)
├── 4. Purge the stale vault: delete %ProgramData%\Servy\db and %ProgramData%\Servy\security
├── 5. Run Servy-Restore.ps1 -DumpArchivePath "C:\Sysprep\Servy_Base_Dump.zip" -Install
├── 6. Re-enter Service Account Passwords
└─> Servy re-applies the hardening automatically (v10.2+)
└── 7. Remove Staged Dump Archive (C:\Sysprep\Servy_Base_Dump.zip)
```
### Automation Steps
1. **Prepare Golden Template:** Install Servy on the master image.
2. **Export Base Configurations:** If your template includes standard base service definitions, run:
```powershell
.\Servy-Dump.ps1 -DestinationArchivePath "C:\Sysprep\Servy_Base_Dump.zip" -Overwrite
```
Use the `-Uninstall` switch to remove every successfully exported service from both the Windows Service Control Manager (SCM) and the Servy database:
```powershell
.\Servy-Dump.ps1 -DestinationArchivePath "C:\Sysprep\Servy_Base_Dump.zip" -Overwrite -Uninstall
```
3. **Deploy Clone:** Clone the VM in VMware and execute standard Guest OS Customization / Sysprep.
4. **Purge the Stale Vault:** On the cloned VM, delete the database and key material inherited from the template. Servy recreates both folders, with their hardened ACLs, on the next CLI operation.
```powershell
Remove-Item -LiteralPath "$env:ProgramData\Servy\db" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath "$env:ProgramData\Servy\security" -Recurse -Force -ErrorAction SilentlyContinue
```
Only the `db\` and `security\` folders need to be removed from the vault `%ProgramData%\Servy`. Deleting all of `%ProgramData%\Servy` also discards `logs\`, which is unnecessary and costs you the service logs from the template.
5. **Restore Configurations:** On first boot of the newly cloned VM, run:
```powershell
.\Servy-Restore.ps1 -DumpArchivePath "C:\Sysprep\Servy_Base_Dump.zip" -Install
```
Servy will automatically initialize a brand-new `aes_key.dat` tied to the new VM's unique `MachineGuid` and DPAPI scope.
6. **Re-apply Logon Credentials & Binary Hardening:** Re-assign custom service runner credentials (e.g., Servy Manager, `servy-cli`, or Servy PowerShell module). Since v10.2, reinstalling the service re-applies the hardening automatically: the account gets write access to the log folder of the service (`logs\services\\`) and access to the `Servy` service's named pipe, and the binary and configuration file permissions are enforced. On Servy 10.1 and earlier, run the hardening script afterwards (see [Servy 10.1 and Earlier](https://github.com/aelassas/servy/wiki/Security#servy-101-and-earlier)):
```powershell
.\Set-ServyExePermissions.ps1 -TargetAccount "DOMAIN\svc-runner"
```
7. **Remove Staged Dump Archive:** The archive is plaintext (see the security warning above) and, staged inside the golden image, it is replicated to every VM deployed from that template. Delete it as the last step of guest customization:
```powershell
Remove-Item -LiteralPath "C:\Sysprep\Servy_Base_Dump.zip" -Force -ErrorAction SilentlyContinue
```
> [!CAUTION]
> Deleting the staged dump archive on the template is not enough. Because each clone receives its own copy from the image, the removal must run on every deployed VM after the restore completes.
## See Also
* [Export & Import Services](https://github.com/aelassas/servy/wiki/Export-Import-Services) - configuration file format and field reference
* [Security](https://github.com/aelassas/servy/wiki/Security) - the vault, key material, and executable hardening
* [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module) - `Export-ServyServiceConfig` and `Import-ServyServiceConfig`
* [Troubleshooting](https://github.com/aelassas/servy/wiki/Troubleshooting) - DPAPI decryption failures after a restore
---
# Document: Examples & Recipes
> Source: https://github.com/aelassas/servy/wiki/Examples-&-Recipes
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#introduction)
1. [Quick Start (Any App)](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#quick-start-any-app)
1. [Example: Run a simple HTTP server](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#example-run-a-simple-http-server)
1. [How Servy Runs Your App](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#how-servy-runs-your-app)
1. [Service Account & Permissions](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#service-account--permissions)
1. [Example: Install using a dedicated service account](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#example-install-using-a-dedicated-service-account)
1. [Verifying the Service](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#verifying-the-service)
1. [Common Problems & Fixes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#common-problems--fixes)
1. [Service starts then stops immediately](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#service-starts-then-stops-immediately)
1. [Works in terminal but not as a service](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#works-in-terminal-but-not-as-a-service)
1. [No logs are produced](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#no-logs-are-produced)
1. [Tips & Notes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#tips--notes)
1. [See Also](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#see-also)
### [JavaScript & TypeScript Runtimes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#javascript--typescript-runtimes-1)
1. [Node.js / Next.js / Express](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-nodejs--nextjs--express-app-as-a-service)
1. [Deno](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-deno-app-as-a-service)
1. [Bun](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-bun-app-as-a-service)
### [Containers & Infrastructure](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#containers--infrastructure-1)
1. [Docker](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-docker-container-as-a-service)
1. [Docker Compose](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-docker-compose-stack-as-a-service)
1. [Nginx](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-nginx-web-server-as-a-service)
1. [Redis](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-redis-server-as-a-service)
1. [PocketBase](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-pocketbase-instance-as-a-service)
### [AI & Modern Tools](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#ai--modern-tools-1)
1. [Ollama AI Instance](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-ollama-ai-instance-as-a-service)
1. [Ghost CMS](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-ghost-cms-instance-as-a-service)
### [Compiled Languages](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#compiled-languages-1)
1. [Go](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-go-app-as-a-service)
1. [Rust](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-rust-app-as-a-service)
1. [C / C++](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-c--c-compiled-app-as-a-service)
1. [Zig](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-zig-app-as-a-service)
1. [Pascal / Delphi](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-pascal--delphi-app-as-a-service)
1. [Fortran](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-fortran-app-as-a-service)
### [Managed Runtimes](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#managed-runtimes-1)
1. [.NET](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-net-app-as-a-service)
1. [Java](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-java-jar-as-a-service)
1. [Elixir](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-elixir-script-as-a-service)
1. [Erlang](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-erlang-script-as-a-service)
### [Scripting & Automation](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#scripting--automation-1)
1. [PowerShell](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-powershell-script-as-a-service)
1. [Batch](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-batch-file-as-a-service)
1. [Python](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-python-script-as-a-service)
1. [PHP](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-php-app-as-a-service)
1. [Laravel Queue Worker](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-laravel-queue-worker-as-a-service)
1. [Ruby](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-ruby-app-as-a-service)
1. [VBScript](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-vbscript-as-a-service)
1. [AutoHotkey](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-autohotkey-script-as-a-service)
1. [WSL Bash](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-wsl-bash-script-as-a-service)
### [Data Science & Analytics](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#data-science--analytics-1)
1. [Julia](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-julia-script-as-a-service)
1. [R](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-r-script-as-a-service)
### [Other Languages & Tools](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#other-languages--tools-1)
1. [Haskell](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-haskell-app-as-a-service)
1. [Dart](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-dart-server-or-script-as-a-service)
1. [Lua](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-lua-script-as-a-service)
1. [Perl](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-a-perl-script-as-a-service)
1. [OCaml](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-an-ocaml-script-or-app-as-a-service)
1. [Kopia](https://github.com/aelassas/servy/wiki/Examples-&-Recipes#run-kopia-as-a-service)
## Introduction
This documentation shows how to run almost any application as a native Windows service using the Servy CLI. It includes practical examples across languages, runtimes, and infrastructure tools. Use the table of contents to jump directly to the runtime or setup you need.
Servy can run any app as a native Windows service. This page provides examples for the most popular languages and frameworks, ready to run as background services.
Services can be installed and configured through the Servy Desktop App or the PowerShell module, but this page focuses on real-world examples using the Servy CLI for automation, scripting, and CI/CD pipelines.
After a default installation `servy-cli` is on the system **PATH** - see [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide#add-servy-to-path) for the option that controls this and for the portable package.
## Quick Start (Any App)
The basic pattern for running any application as a Windows service is:
```powershell
servy-cli install `
--name="MyService" `
--path="C:\path\to\app.exe" `
--params="optional arguments" `
--startupDir="C:\path\to" `
--startupType="Automatic"
```
If your app runs correctly from a terminal, it will run correctly as a service using this pattern.
### Example: Run a simple HTTP server
```powershell
servy-cli install `
--name="HelloServer" `
--path="C:\Program Files\nodejs\node.exe" `
--params="server.js" `
--startupDir="C:\apps\hello" `
--startupType="Automatic"
```
If `node server.js` works in your terminal, this service will work.
## How Servy Runs Your App
Servy runs your application as a **native Windows service** by registering it with the Windows Service Control Manager (SCM).
Your application does **not** need to implement Windows Service APIs or be service-aware. Servy launches your executable or command when the service starts, monitors it if recovery is enabled, runs pre-launch and post-launch hooks, and stops it when the service is stopped, including pre-stop and post-stop hooks.
Key characteristics:
* Your application runs in **Session 0**, like all Windows services.
* Standard input/output is **not interactive**.
* Environment variables, working directory, and arguments are explicitly defined at install time.
* The service lifecycle (start, stop, restart) is fully managed by the SCM.
If your application cannot run unattended, requires a desktop session, or expects user interaction, it is not suitable to run as a Windows service.
## Service Account & Permissions
By default, services are installed to run under the **LocalSystem** account.
Keep in mind:
* Network access may differ from your user account
* Mapped drives are not available
* Environment variables must be system-wide
If your application needs access to network shares, databases, or restricted folders, configure a **dedicated service account** instead of running under the default **LocalSystem** account.
Use the `--user` option when installing the service and supply the password through the `SERVY_PASSWORD` environment variable (see the example below). Starting from v10.2, Servy gives the account the access it needs under `%ProgramData%\Servy` (write access to the log folder of the service, `logs\services\\`, read access to its binaries and configuration files, and access to the `Servy` service's named pipe) automatically when the service is installed; see [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening). Then ensure the account has **Modify** permissions for:
* The application's startup directory
* Any additional files, folders, or network resources the app depends on
### Example: Install using a dedicated service account
`servy-cli` reads the account password from `SERVY_PASSWORD` when `--password` is not supplied, which keeps it out of process listings and shell history.
```powershell
# Set the password in the current process environment first
$env:SERVY_PASSWORD = "your_secret_password"
servy-cli install `
--name="MySecureService" `
--path="C:\apps\secure\app.exe" `
--startupDir="C:\apps\secure" `
--user="DOMAIN\svc-myapp" `
--startupType="Automatic"
# Clear sensitive variables from memory immediately after use
Remove-Item Env:SERVY_PASSWORD
```
> [!IMPORTANT]
> **Security:** Avoid `--password` on the command line - it is visible in OS process listings and shell history. See the [Security](https://github.com/aelassas/servy/wiki/Security#6-sensitive-command-line-arguments--service-account-credentials) page for details.
For more details about service accounts, trust boundaries, and security considerations, see the [Security Model](https://github.com/aelassas/servy/wiki/Security) documentation.
## Verifying the Service
After installation:
```powershell
sc.exe query MyService
sc.exe start MyService
sc.exe stop MyService
```
Or using Servy CLI:
```powershell
servy-cli status --name="MyService"
servy-cli start --name="MyService"
servy-cli stop --name="MyService"
```
Or open **Servy Manager** to:
* Start / stop the service
* View logs
* Adjust restart and failure policies
* View CPU / RAM live performance graphs
* Preview service `stdout`/`stderr`
* Preview service dependencies
## JavaScript & TypeScript Runtimes
### Run a Node.js / Next.js / Express App as a Service
```powershell
servy-cli install `
--name="MyNodeApp" `
--description="Node.js Express API" `
--path="C:\Program Files\nodejs\node.exe" `
--params="server.js" `
--startupDir="C:\apps\myapp" `
--startupType="Automatic"
```
Run an npm script (`npm start`) as a Service:
```powershell
servy-cli install `
--name="MyNodeApp" `
--description="Node.js App via npm" `
--path="C:\Program Files\nodejs\npm.cmd" `
--params="start" `
--startupDir="C:\apps\myapp" `
--startupType="Automatic"
```
Run a Next.js Production App as a Service:
```powershell
servy-cli install `
--name="MyNextApp" `
--description="Next.js App" `
--path="C:\Program Files\nodejs\npm.cmd" `
--params="start" `
--startupDir="C:\apps\myapp" `
--startupType="Automatic"
```
This runs:
```text
npm start -> next start
```
which is the correct way to run Next.js in production.
### Run a Deno App as a Service
```powershell
servy-cli install `
--name="MyDenoService" `
--description="Deno background script" `
--path="C:\tools\deno\deno.exe" `
--params="run --allow-net worker.ts" `
--startupDir="C:\apps\deno" `
--startupType="Automatic"
```
**Notes:**
* The `--allow-net` flag is an example; add other permissions (`--allow-read`, `--allow-write`, etc.) as needed.
* Works for both scripts and Deno HTTP servers.
### Run a Bun App as a Service
```powershell
servy-cli install `
--name="MyBunService" `
--description="Bun backend service" `
--path="C:\tools\bun\bun.exe" `
--params="C:\apps\bun\server.ts" `
--startupDir="C:\apps\bun" `
--startupType="Automatic"
```
**Notes:**
* Bun automatically detects whether the file is a script or server.
* You can pass arguments like `--port 3000` in `--params` if needed.
## Containers & Infrastructure
### Run a Docker Container as a Service
```powershell
servy-cli install `
--name="MyDockerService" `
--description="Docker container service" `
--path="C:\Program Files\Docker\Docker\resources\bin\docker.exe" `
--params="run --rm --name myapp -p 8080:80 myimage:latest" `
--startupDir="C:\Program Files\Docker\Docker\resources\bin" `
--startupType="Automatic"
```
**Notes:**
* `--rm` ensures the container is cleaned up when it stops.
* Adjust `-p` and `myimage:latest` as needed.
### Run a Docker Compose Stack as a Service
This is useful when you want your entire container stack to start automatically at boot without relying on Docker Desktop auto-start behavior.
```powershell
servy-cli install `
--name="MyDockerComposeService" `
--description="Docker Compose stack service" `
--path="C:\Program Files\Docker\Docker\resources\bin\docker-compose.exe" `
--params="-f C:\apps\mycompose\docker-compose.yml up" `
--startupDir="C:\apps\mycompose" `
--startupType="Automatic"
```
**Notes:**
* This runs the entire `docker-compose.yml` stack as a background service.
* Do not add `--detach`: it makes `docker-compose` exit immediately after starting the containers, so the service would stop right away (see *Service starts then stops immediately* below) and stopping the service would no longer stop the stack. In foreground mode the service status tracks the stack and a service stop shuts the containers down cleanly.
### Run an Nginx Web Server as a Service
Running the Windows port of Nginx as a service ensures the web server persists across reboots.
```powershell
servy-cli install `
--name="Nginx" `
--description="Nginx Web Server" `
--path="C:\nginx\nginx.exe" `
--params='-g "daemon off;"' `
--startupDir="C:\nginx" `
--startupType="Automatic"
```
To ensure consistent UTF-8 output for logs (especially when shipping logs or using non-ASCII characters), wrap Nginx with cmd.exe and force code page 65001:
```powershell
servy-cli install `
--name="Nginx" `
--description="Nginx Web Server" `
--path="C:\Windows\System32\cmd.exe" `
--params='/c "C:\nginx\start-nginx.cmd"' `
--startupDir="C:\nginx" `
--startupType="Automatic"
```
Where `start-nginx.cmd` is as follows:
```cmd
@echo off
chcp 65001 >nul
cd /d C:\nginx
nginx.exe -g "daemon off;"
```
Nginx logs go to: `C:\nginx\logs\`
### Run a Redis Server as a Service
```powershell
servy-cli install `
--name="Redis" `
--description="Redis In-Memory Data Store" `
--path="C:\redis\redis-server.exe" `
--params="redis.windows.conf" `
--startupDir="C:\redis" `
--startupType="Automatic"
```
> [!NOTE]
> Redis for Windows is not officially supported by Redis Labs. For production use, consider running Redis in Docker or WSL.
### Run a PocketBase Instance as a Service
PocketBase is a single-file backend that is highly effective when run as a service.
```powershell
servy-cli install `
--name="PocketBase" `
--description="PocketBase backend" `
--path="C:\apps\pocketbase\pocketbase.exe" `
--params="serve --http=0.0.0.0:8090" `
--startupDir="C:\apps\pocketbase" `
--startupType="Automatic"
```
## AI & Modern Tools
### Run an Ollama AI Instance as a Service
Keep your local LLM API available in the background.
```powershell
servy-cli install `
--name="Ollama" `
--description="Ollama Local AI API" `
--path="C:\Users\Admin\AppData\Local\Programs\Ollama\ollama.exe" `
--params="serve" `
--startupType="Automatic"
```
### Run a Ghost CMS Instance as a Service
```powershell
servy-cli install `
--name="GhostCMS" `
--path="C:\Program Files\nodejs\node.exe" `
--params="current\index.js" `
--startupDir="C:\var\www\ghost" `
--startupType="Automatic"
```
## Compiled Languages
### Run a Go App as a Service
```powershell
servy-cli install `
--name="MyGoService" `
--description="Go background service" `
--path="C:\apps\my-go-app\my-go-app.exe" `
--params="--port=8080 --mode=worker" `
--startupDir="C:\apps\my-go-app" `
--startupType="Automatic"
```
### Run a Rust App as a Service
```powershell
servy-cli install `
--name="MyRustService" `
--description="Rust background service" `
--path="C:\apps\rustsvc\rust_svc.exe" `
--startupDir="C:\apps\rustsvc" `
--startupType="Automatic"
```
### Run a C / C++ Compiled App as a Service
```powershell
servy-cli install `
--name="MyCppService" `
--description="C++ Application" `
--path="C:\apps\cpp-service\service.exe" `
--startupDir="C:\apps\cpp-service" `
--startupType="Automatic"
```
### Run a Zig App as a Service
```powershell
servy-cli install `
--name="MyZigService" `
--description="Zig background worker" `
--path="C:\apps\zig\myapp.exe" `
--startupDir="C:\apps\zig" `
--startupType="Automatic"
```
### Run a Pascal / Delphi App as a Service
```powershell
servy-cli install `
--name="MyPascalService" `
--description="Delphi background app" `
--path="C:\apps\pascal\worker.exe" `
--startupDir="C:\apps\pascal" `
--startupType="Automatic"
```
### Run a Fortran App as a Service
```powershell
servy-cli install `
--name="MyFortranService" `
--description="Fortran computational service" `
--path="C:\apps\fortran\worker.exe" `
--startupDir="C:\apps\fortran" `
--startupType="Automatic"
```
## Managed Runtimes
### Run a .NET App as a Service
```powershell
servy-cli install `
--name="MyDotNetApp" `
--description=".NET Worker Service" `
--path="C:\apps\dotnetapp\MyApp.exe" `
--startupDir="C:\apps\dotnetapp" `
--startupType="Automatic"
```
Or, if using dotnet runtime with a DLL:
```powershell
servy-cli install `
--name="MyDotNetApp" `
--description=".NET Worker Service" `
--path="C:\Program Files\dotnet\dotnet.exe" `
--params="C:\apps\dotnetapp\app.dll" `
--startupDir="C:\apps\dotnetapp" `
--startupType="Automatic"
```
### Run a Java JAR as a Service
```powershell
servy-cli install `
--name="MyJavaService" `
--description="Java Spring Boot App" `
--path="%JAVA_HOME%\bin\java.exe" `
--params="-jar C:\apps\springboot\app.jar" `
--startupDir="C:\apps\springboot" `
--startupType="Automatic"
```
By using `%JAVA_HOME%` system environment variable, Servy resolves the correct Java installation path at runtime instead of relying on a hardcoded version. This ensures the service continues to work after Java updates and keeps the configuration portable across different environments.
### Run an Elixir Script as a Service
```powershell
servy-cli install `
--name="MyElixirService" `
--description="Elixir background worker" `
--path="C:\Windows\System32\cmd.exe" `
--params='/c "C:\Program Files\Elixir\bin\elixir.bat C:\apps\elixir\worker.exs"' `
--startupDir="C:\apps\elixir" `
--startupType="Automatic"
```
For Elixir, you can point `--params` to any `.exs` script or mix task.
### Run an Erlang Script as a Service
```powershell
servy-cli install `
--name="MyErlangService" `
--description="Erlang background worker" `
--path="C:\Program Files\erl-25.3\bin\erl.exe" `
--params="-noshell -s my_app start -s init stop" `
--startupDir="C:\apps\erlang" `
--startupType="Automatic"
```
For Erlang, the `-s` parameters start the desired module and function. Adjust according to your OTP app.
## Scripting & Automation
### Run a PowerShell script as a Service
```powershell
servy-cli install `
--name="MyPowerShellScript" `
--description="PowerShell automation job" `
--path="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--params='-File "C:\scripts\script\my script.ps1"' `
--startupDir="C:\scripts\script" `
--startupType="Automatic"
```
### Run a Batch File as a Service
```powershell
servy-cli install `
--name="MyBatchScript" `
--description="Batch automation job" `
--path="C:\Windows\System32\cmd.exe" `
--params="/c C:\scripts\backup-job.bat" `
--startupDir="C:\scripts" `
--startupType="Automatic"
```
### Run a Python Script as a Service
```powershell
servy-cli install `
--name="MyPythonJob" `
--description="Python background job" `
--path="C:\Python311\python.exe" `
--params="C:\apps\scripts\job.py" `
--startupDir="C:\apps\scripts" `
--startupType="Automatic"
```
#### Why `--enableConsoleUI` Is Required for Some Python Applications
Many Python libraries and orchestration frameworks (e.g., **Prefect**, **Rich**, **Colorama**) attempt to initialize interactive terminal features like colors, progress bars, or advanced logging. When running as a standard Windows service in **Session 0**, these applications typically fail to start because they cannot find a valid terminal handle (stdin/stdout/stderr).
Setting the `--enableConsoleUI` option instructs Servy to:
* **Allocate a real console buffer** for the wrapped process within Session 0.
* **Provide a valid terminal handle**, satisfying the application's requirement for a console environment.
* **Ensure stability** for scripts that would otherwise exit immediately with an `Illegal operation on a terminal` or `No such file or directory` error when trying to access the console.
> [!NOTE]
> `--enableConsoleUI` disables stdout/stderr redirection: the process writes to its own console instead of to Servy's pipes, so `--stdout`/`--stderr` log files stay empty. Use it only for applications that fail without a console, and have them log to a file themselves. See [Logging & Log Rotation](https://github.com/aelassas/servy/wiki/Logging-&-Log-Rotation).
### Run a PHP App as a Service
```powershell
servy-cli install `
--name="MyPHPWorker" `
--description="PHP queue worker" `
--path="C:\php\php.exe" `
--params="C:\apps\worker\queue-worker.php" `
--startupDir="C:\apps\worker" `
--startupType="Automatic"
```
### Run a Laravel Queue Worker as a Service
Ideal for handling background jobs in PHP/Laravel applications.
```powershell
servy-cli install `
--name="LaravelWorker" `
--description="Laravel Queue Worker" `
--path="C:\php\php.exe" `
--params="artisan queue:work --tries=3" `
--startupDir="C:\inetpub\wwwroot\myapp" `
--startupType="Automatic"
```
### Run a Ruby App as a Service
```powershell
servy-cli install `
--name="MyRubyApp" `
--description="Ruby background app" `
--path="C:\Ruby32\bin\ruby.exe" `
--params="C:\apps\rubyapp\app.rb" `
--startupDir="C:\apps\rubyapp" `
--startupType="Automatic"
```
### Run a VBScript as a Service
```powershell
servy-cli install `
--name="MyVBScript" `
--description="VBScript automation job" `
--path="C:\Windows\System32\cscript.exe" `
--params="C:\scripts\tasks\job.vbs" `
--startupDir="C:\scripts\tasks" `
--startupType="Automatic"
```
If you prefer wscript.exe (windowed, but still works as a service):
```powershell
servy-cli install `
--name="MyVBScript" `
--description="VBScript automation job" `
--path="C:\Windows\System32\wscript.exe" `
--params="C:\scripts\tasks\job.vbs" `
--startupDir="C:\scripts\tasks" `
--startupType="Automatic"
```
### Run an AutoHotkey script as a Service
Running AutoHotkey (AHK) as a service allows scripts to execute before user login.
> [!WARNING]
> **Desktop Interaction:** Windows services run in Session 0. This means scripts requiring GUI interaction, mouse movements, or keystrokes sent to active windows will not function correctly. Use service mode for background file monitoring or logic-only scripts.
```powershell
servy-cli install `
--name="MyAutoHotkeyService" `
--description="AutoHotkey background script" `
--path="C:\Program Files\AutoHotkey\v2\AutoHotkey.exe" `
--params="C:\scripts\service.ahk" `
--startupDir="C:\scripts" `
--startupType="Automatic"
```
### Run a WSL Bash Script as a Service
```powershell
servy-cli install `
--name="MyWSLScript" `
--description="WSL Bash script service" `
--path="C:\Windows\System32\wsl.exe" `
--params="bash /home/user/scripts/run.sh" `
--startupDir="C:\Windows\System32" `
--startupType="Automatic"
```
If you need a specific distribution:
```powershell
servy-cli install `
--name="MyUbuntuWSLService" `
--description="WSL Ubuntu service job" `
--path="C:\Windows\System32\wsl.exe" `
--params="-d Ubuntu bash /home/user/app/start.sh" `
--startupDir="C:\Windows\System32" `
--startupType="Automatic"
```
> [!WARNING]
> **Per-user distributions:** WSL distros are registered per user account. Under the default **LocalSystem** account, `wsl.exe` finds no distributions and the service exits immediately (`WSL_E_DISTRO_NOT_FOUND`). Install the service with `--user` set to the account that owns the distro (see *Service Account & Permissions*), and note that the WSL VM lifecycle then follows that user's session policies.
## Data Science & Analytics
### Run a Julia Script as a Service
*(Used for ML jobs, analytics, long-running computation workers)*
```powershell
servy-cli install `
--name="MyJuliaService" `
--description="Julia analytics worker" `
--path="C:\Julia-1.10\bin\julia.exe" `
--params="C:\apps\julia\worker.jl" `
--startupDir="C:\apps\julia" `
--startupType="Automatic"
```
### Run an R Script as a Service
```powershell
servy-cli install `
--name="MyRService" `
--description="R background job" `
--path="C:\Program Files\R\R-4.4.1\bin\Rscript.exe" `
--params="C:\apps\r\job.R" `
--startupDir="C:\apps\r" `
--startupType="Automatic"
```
## Other Languages & Tools
### Run a Haskell App as a Service
```powershell
servy-cli install `
--name="MyHaskellService" `
--description="Haskell background worker" `
--path="C:\apps\haskell\myapp.exe" `
--startupDir="C:\apps\haskell" `
--startupType="Automatic"
```
### Run a Dart Server or Script as a Service
*(Shelf web services, background workers, API servers, etc.)*
```powershell
servy-cli install `
--name="MyDartService" `
--description="Dart backend service" `
--path="C:\tools\dart-sdk\bin\dart.exe" `
--params="C:\apps\dart\server.dart" `
--startupDir="C:\apps\dart" `
--startupType="Automatic"
```
### Run a Lua Script as a Service
```powershell
servy-cli install `
--name="MyLuaService" `
--description="Lua automation script" `
--path="C:\Lua\5.4\lua.exe" `
--params="C:\apps\lua\script.lua" `
--startupDir="C:\apps\lua" `
--startupType="Automatic"
```
### Run a Perl Script as a Service
```powershell
servy-cli install `
--name="MyPerlService" `
--description="Perl Script" `
--path="C:\Perl64\bin\perl.exe" `
--params="C:\apps\perl-task\task.pl" `
--startupDir="C:\apps\perl-task" `
--startupType="Automatic"
```
### Run an OCaml Script or App as a Service
```powershell
servy-cli install `
--name="MyOCamlService" `
--description="OCaml background worker" `
--path="C:\OCaml\bin\ocaml.exe" `
--params="C:\apps\ocaml\worker.ml" `
--startupDir="C:\apps\ocaml" `
--startupType="Automatic"
```
If you compiled your OCaml code to a native executable (e.g., `worker.exe`), you can skip `ocaml.exe` and set `--path` directly to the executable:
```powershell
servy-cli install `
--name="MyOCamlService" `
--description="OCaml compiled worker" `
--path="C:\apps\ocaml\worker.exe" `
--startupDir="C:\apps\ocaml" `
--startupType="Automatic"
```
This way, you can run either scripts or compiled OCaml apps as Windows services using Servy.
### Run Kopia as a Service
Setting up Kopia as a Windows service is a smart move. It ensures your backups run in the background without needing a user to be logged in, and using Servy makes the process straightforward.
To keep your backup server credentials safe from being exposed in command-line histories, logs, or process enumeration tools, pass the startup parameters securely using the `SERVY_PROCESS_PARAMETERS` environment variable (starting from Servy v8.5):
```powershell
# Set the sensitive arguments securely inside your current session memory
$env:SERVY_PROCESS_PARAMETERS = 'server start --insecure --address=127.0.0.1:51515 --server-username=admin --server-password=somepwd'
# Install the service without passing secrets over the command line
servy-cli install `
--name="KopiaService" `
--description="Kopia Service" `
--path="C:\Program Files\KopiaUI\resources\server\kopia.exe" `
--startupDir="C:\Program Files\KopiaUI\resources\server" `
--startupType="Automatic" `
--stdout="C:\Program Files\KopiaUI\resources\server\stdout.log" `
--stderr="C:\Program Files\KopiaUI\resources\server\stderr.log" `
--enableSizeRotation `
--rotationSize="10"
# Clear the session variable after deployment
Remove-Item Env:SERVY_PROCESS_PARAMETERS
```
> [!WARNING]
> **Production Security Warning:** Passing sensitive fields directly over raw CLI arguments (`--params`) can expose passwords to any system monitoring tool or user with process-listing clearance. Always use the `SERVY_PROCESS_PARAMETERS` marshal pattern shown above when deploying real production passwords. For more details, consult the [Security Guidance](https://github.com/aelassas/servy/wiki/Security#6-sensitive-command-line-arguments--service-account-credentials).
* `server start`: This keeps Kopia running in the background as a local server.
* `--insecure`: Since it's only listening on 127.0.0.1 (localhost), this is generally fine for a local setup, but you can configure TLS if preferred.
* `--address`: Defines the port where the Kopia UI/API will live.
Adjust Kopia command args as needed. See the [official docs](https://kopia.io/docs/reference/command-line/common/server-start/).
Once Kopia is running as a service (server mode), use Kopia's internal Policy system via the Web UI (easiest) or via the Command Line to schedule your snapshots.
## Common Problems & Fixes
### Service starts then stops immediately
* The executable exits immediately
* Missing arguments in `--params`
* Incorrect working directory
**Fix:** Run the same command manually from a terminal.
### Works in terminal but not as a service
* App relies on user-specific environment variables
* Uses mapped network drives
* Insufficient NTFS permissions
**Fix:** Use a dedicated service account and system-wide environment variables.
### No logs are produced
* App logs to relative paths
* Startup directory is incorrect
* `--enableConsoleUI` is set (it disables stdout/stderr capture)
**Fix:** Set `--startupDir` explicitly and verify log paths.
## Tips & Notes
If your application can be started from the command line, Servy can run it as a Windows service.
If it cannot, Servy will not hide the problem. It will surface it clearly through logs and exit codes.
* **Environment Variables:** If your application relies on specific environment variables (like `JAVA_HOME`), ensure they are set as System variables, as services run under the SYSTEM account by default.
* **Logging:** Use Servy Manager to configure log rotation and capture `stdout`/`stderr` for debugging background services.
* **Permissions:** Ensure the service account has NTFS **Modify** permissions for the application's startup directory. Servy gives it the access it needs under `%ProgramData%\Servy` automatically when the service is installed (v10.2+). See [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening).
## See Also
* [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide)
* [Overview](https://github.com/aelassas/servy/wiki/Overview)
* [Servy CLI](https://github.com/aelassas/servy/wiki/Servy-CLI)
* [Servy PowerShell Module](https://github.com/aelassas/servy/wiki/Servy-PowerShell-Module)
* [Servy Automation & CI/CD](https://github.com/aelassas/servy/wiki/Servy-Automation-&-CI-CD)
---
# Document: Security
> Source: https://github.com/aelassas/servy/wiki/Security
## Table of Contents
1. [Security Overview](https://github.com/aelassas/servy/wiki/Security#security-overview)
1. [The Double-Lock System](https://github.com/aelassas/servy/wiki/Security#the-double-lock-system)
1. [How Servy Protects Your Data](https://github.com/aelassas/servy/wiki/Security#how-servy-protects-your-data)
1. [Automatic Directory Hardening (ACLs)](https://github.com/aelassas/servy/wiki/Security#1-automatic-directory-hardening-acls)
1. [Machine-Unique Encryption (Dynamic Entropy)](https://github.com/aelassas/servy/wiki/Security#2-machine-unique-encryption-dynamic-entropy)
1. [Cryptographic Key Derivation (HKDF)](https://github.com/aelassas/servy/wiki/Security#3-cryptographic-key-derivation-hkdf)
1. [Authenticated Encryption (v6.5+)](https://github.com/aelassas/servy/wiki/Security#4-authenticated-encryption-v65)
1. [In-Memory Defense (Memory Zeroing)](https://github.com/aelassas/servy/wiki/Security#5-in-memory-defense-memory-zeroing)
1. [Sensitive Command-Line Arguments & Service Account Credentials](https://github.com/aelassas/servy/wiki/Security#6-sensitive-command-line-arguments--service-account-credentials)
1. [Infiltration Guard: Local Import Enforcement](https://github.com/aelassas/servy/wiki/Security#infiltration-guard-local-import-enforcement)
1. [Mitigation: Defense-in-Depth Pipeline](https://github.com/aelassas/servy/wiki/Security#mitigation-defense-in-depth-pipeline)
1. [Supply Chain and Trust](https://github.com/aelassas/servy/wiki/Security#supply-chain-and-trust)
1. [The Servy Trust Boundary](https://github.com/aelassas/servy/wiki/Security#the-servy-trust-boundary)
1. [Architectural Design & Runtime Permissions](https://github.com/aelassas/servy/wiki/Security#architectural-design--runtime-permissions)
1. [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening)
1. [What Is Hardened](https://github.com/aelassas/servy/wiki/Security#what-is-hardened)
1. [Servy 10.1 and Earlier](https://github.com/aelassas/servy/wiki/Security#servy-101-and-earlier)
1. [What This Means for Your Deployment](https://github.com/aelassas/servy/wiki/Security#what-this-means-for-your-deployment)
1. [Automatic Permissions Table (v7.9+)](https://github.com/aelassas/servy/wiki/Security#automatic-permissions-table-v79)
1. [File Locations and Recovery](https://github.com/aelassas/servy/wiki/Security#file-locations-and-recovery)
1. [Critical Warning: Machine Migration](https://github.com/aelassas/servy/wiki/Security#critical-warning-machine-migration)
1. [Best Practices](https://github.com/aelassas/servy/wiki/Security#best-practices)
1. [Troubleshooting](https://github.com/aelassas/servy/wiki/Security#troubleshooting)
## Security Overview
Starting from v10.2, Servy is built for enterprise use. Every protection below is applied automatically, with no manual step and no sysadmin intervention:
* **100% per-service isolation between service accounts:** each service account can reach only its own configuration and runtime state (through the `Servy` service, which checks the caller of every request) and only its own log folder (`logs\services\\`). It cannot read or change the configuration database, the encryption key, Servy's own logs or the logs of any service running under another account. See [The Servy Trust Boundary](https://github.com/aelassas/servy/wiki/Security#the-servy-trust-boundary).
* **DPAPI and AES encryption:** sensitive fields are encrypted with AES-256 and authenticated with HMAC-SHA256, under a key protected by DPAPI and bound to the machine. See [How Servy Protects Your Data](https://github.com/aelassas/servy/wiki/Security#how-servy-protects-your-data).
* **Automatic ACLs:** the vault, its binaries, its configuration files and each service's log folder get least-privilege access lists when a service is installed, and the grants are revoked when it is removed. See [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening).
* **Automatic DACL on the named pipe:** the `Servy` service's named pipe is open only to the accounts of the services Servy manages, and the list is rebuilt whenever a service is installed, moved to another account or removed. See [Architectural Design & Runtime Permissions](https://github.com/aelassas/servy/wiki/Security#architectural-design--runtime-permissions).
* **Signed binaries, SBOM and scanned releases.** See [Supply Chain and Trust](https://github.com/aelassas/servy/wiki/Security#supply-chain-and-trust).
Servy acts as a secure vault for your Windows service configurations. Servy encrypts sensitive data, including passwords, environment variables, and all execution arguments (`Parameters`, `Password`, `EnvironmentVariables`, `FailureProgramParameters`, `PreLaunchParameters`, `PreLaunchEnvironmentVariables`, `PostLaunchParameters`, `PreStopParameters`, and `PostStopParameters`), using industry-standard AES-256 encryption. If the database is compromised, your actual secrets remain unreadable text.
### The Double-Lock System
Starting in version 7.9, Servy uses a two-layer defense strategy to keep your secrets safe:
* **The Locked Room (ACLs):** Servy automatically restricts who can even view the Servy folders on your hard drive.
* **The Machine-Unique Key (Dynamic Entropy):** Servy ties your encryption key to the unique machine identity of your specific computer. If the files are moved to another PC, they cannot be decrypted without the machine-specific entropy stored in the originating system's registry.
You can verify that the "Locked Room" is active by checking the Access Control List (ACL) of the data directory. Run the following command in an elevated PowerShell window:
```powershell
(Get-Acl "$env:ProgramData\Servy").Access | Select-Object IdentityReference, IsInherited, AccessControlType, FileSystemRights
```
**What to look for in the output:**
* **IdentityReference:** You should see two principals: `NT AUTHORITY\SYSTEM` and `BUILTIN\Administrators`.
* *Note:* The installer grants no personal user ACE, and removes any grant it finds for the installing account, because it always runs elevated (`PrivilegesRequired=admin`). A third, per-user Full Control ACE appears only when the vault was created or re-hardened by a Servy process running non-elevated and not as `SYSTEM` (`SecurityHelper.ApplySecurityRules`). Since v10.2, custom service accounts do **not** appear here: they get access only to the log folder of each of their own services (`logs\services\\`) and to the binaries and configuration files they read (see [What Is Hardened](https://github.com/aelassas/servy/wiki/Security#what-is-hardened)). On 10.1 and earlier they appear here with `Modify` rights once `Set-ServyExePermissions.ps1` has been run for them.
* **IsInherited:** This must be **False** for all entries. Servy explicitly breaks inheritance from the `%ProgramData%` root to prevent "sideways" access from other applications or standard users.
* **AccessControlType:** This should be **Allow**. There should be **no entries** for broad identity groups such as `Everyone`, `Users`, or `Authenticated Users`, as these are surgically purged during the hardening phase.
* **Executable Hardening Status:** When inspecting individual core binaries under `%ProgramData%\Servy` (such as `Servy.Service.exe`), the executable permission hardening ensures that custom service runner accounts display `ReadAndExecute` rights rather than directory-inherited `Modify` or `FullControl` rights. See this [section](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening) for further details.
#### Implementation Context for Auditors
To reconcile this with the source code, auditors can refer to the following logic gates:
* **Inno Setup (`servy.iss`):** The installer executes with `PrivilegesRequired=admin` and explicitly grants permissions only to `BUILTIN\Administrators` and `NT AUTHORITY\SYSTEM`. It intentionally grants no personal user ACE because the elevated installer's user account is already fully covered by the `Administrators` group grant.
* **Runtime (`SecurityHelper.cs`):** The `ApplySecurityRules` method adds an explicit Full Control ACE for the current user **only when the process is running non-elevated and is not the LocalSystem account** (elevated admins and SYSTEM are already covered by the mandatory Administrators/SYSTEM ACEs). Any non-elevated runtime process invoking vault creation triggers this path.
#### Subdirectory Inheritance Note
While the root vault (`%ProgramData%\Servy`) has inheritance explicitly broken, all internal child folders such as `db`, `security` and `logs` are created with inheritance enabled relative to the vault root. This ensures the same "Locked Room" model (`SYSTEM` and `Administrators`, plus the installing user only in the non-elevated case described above) is maintained consistently throughout the entire data structure without redundant ACL writes.
## How Servy Protects Your Data
Servy has overhauled its security model to be proactive rather than reactive.
### 1. Automatic Directory Hardening (ACLs)
In previous versions, Servy relied on Windows defaults for the `%ProgramData%\Servy` folder. In v7.9+, Servy takes control. Upon installation or startup, the application automatically performs the following actions:
* **Breaks Inheritance:** Servy disconnects the folder from the open permissions of the parent drive.
* **Explicit Purge:** Servy removes access for the `Users`, `Authenticated Users`, and `Everyone` groups.
* **Restricted Entry:** Only broad, unprivileged groups are removed - `Users`, `Authenticated Users` and `Everyone`. `SYSTEM` and `Administrators` are granted Full Control, the installing user is preserved (see the table below), and explicit `Allow` rules you have granted to named accounts are kept. This prevents Local Privilege Escalation: the risk of a standard user tampering with a service to gain Admin rights.
* **Downward Inheritance:** All subfolders and files within `%ProgramData%\Servy` automatically inherit these strict parent ACLs, ensuring new service directories, configurations, and logs remain locked down by default.
* **Custom Permissions Preserved (with caveat):** Explicit `Allow` ACEs you have added for *named identities* are retained. Any **`Allow`** rule targeting the broad groups `Users`, `Authenticated Users`, or `Everyone` is removed on every run. **`Deny`** rules for those broad groups are left in place; only `Deny` rules targeting `Administrators`, `LocalSystem`, or the installing user are removed as an anti-squatting measure.
### 2. Machine-Unique Encryption (Dynamic Entropy)
Servy uses the Windows Data Protection API (DPAPI) with an added security layer. To prevent binary analysis (where someone reads the source code to find a secret), Servy derives its encryption entropy from your computer's unique `MachineGuid`.
* **Why it is safe:** Encryption entropy is derived at runtime from your Windows Registry rather than hardcoded in the application binary.
* **Non-Portable:** Because every computer has a different ID, your `aes_key.dat` file is useless if copied to another machine.
If `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid` is missing, unreadable, or restricted by registry permissions, Servy logs a critical security degradation error and falls back to deriving entropy from the host's `Environment.MachineName`. Under this fallback mode, renaming the host computer alters the entropy calculation and permanently breaks decryption for all protected configuration fields.
> [!CAUTION]
> **DYNAMIC ENTROPY DEPENDENCY & REGISTRY SENSITIVITY**
>
> Servy binds its DPAPI master key (`%ProgramData%\Servy\security\aes_key.dat`) to the system's `MachineGuid`. If `MachineGuid` is modified, deleted, corrupted, or blocked by registry permissions on an active installation, Servy will fail to unprotect its keying material; causing Servy Manager, `servy-cli`, and the background service to immediately fail decryption and halt operations.
>
> If `MachineGuid` is missing or unreadable during initial key generation, Servy falls back to using the machine's hostname (`Environment.MachineName`) as its dynamic entropy source and logs a critical security degradation event. If the host is subsequently renamed while running under this fallback state, DPAPI decryption will also fail.
>
> To recover from a decryption failure without re-importing service configurations:
> * Restore the original `MachineGuid` string in `HKLM\SOFTWARE\Microsoft\Cryptography`.
> * Fix registry Access Control Lists (ACLs) if permission restrictions prevent Servy from reading `MachineGuid`.
> * Revert the hostname if running under the fallback state.
> * Otherwise, if a valid pre-existing dump archive is available, purge the stale vault (`%ProgramData%\Servy\db` and `%ProgramData%\Servy\security`) and restore configurations using `Servy-Restore.ps1` (see [Backup/Restore & VM Cloning](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning)).
### 3. Cryptographic Key Derivation (HKDF)
To adhere to strict cryptographic best practices, Servy uses **HKDF (RFC 5869)** to derive independent sub-keys from your master key. Distinct `info` context strings (`V2_AES_ENCRYPTION` and `V2_HMAC_AUTHENTICATION`) give the encryption and authentication sub-keys domain separation, ensuring neither can be derived from or substitute for the other.
### 4. Authenticated Encryption (v6.5+)
Servy protects sensitive data using DPAPI-derived keying material combined with machine-specific registry entropy. Authenticated encryption (HMAC-SHA256 + AES-256-CBC) ensures both confidentiality and tamper detection, preventing bit-flipping attacks by refusing to decrypt any modified payload.
### 5. In-Memory Defense (Memory Zeroing)
Security doesn't stop at the hard drive. To protect against advanced memory scraping attacks, Servy securely handles secrets in RAM. The `SecureData` class implements `IDisposable` and utilizes `CryptographicOperations.ZeroMemory()` to wipe every sensitive buffer as soon as it is no longer needed:
* **Transient Data:** Plaintext and ciphertext buffers are zeroed immediately after each encryption/decryption call.
* **Initialization Material:** The master key clone passed during construction is wiped as soon as sub-keys are derived.
* **Key Material:** All active sensitive buffers are securely zeroed upon the object's disposal. This includes the two HKDF-derived V2 sub-keys required for modern AES encryption and HMAC authentication. (Note: The two legacy V1 buffers, used for master key cloning and static IV retention, remain unallocated in shipped production builds as `AllowLegacyV1Decryption` is permanently disabled at compile-time).
Unlike standard array clearing methods, this approach ensures that the memory wipe is never elided by the JIT compiler's release optimizations, significantly reducing the window of opportunity for an attacker to extract keying material from a memory dump.
### 6. Sensitive Command-Line Arguments & Service Account Credentials
While Servy supports CLI flags for configuration convenience (e.g., `--password`, `--envVars`, `--params`), passing sensitive data via command-line arguments is **insecure**. Command-line arguments are visible to any user or process able to enumerate the process list (e.g., `Get-Process`, `pslist`, or Event Tracing for Windows) and are often recorded in shell history files and system audit logs.
#### The Preferred Methods
Starting in **v8.5**, Servy provides two secure alternatives to handle sensitive data:
1. **Environment Variables (Recommended for per-service secrets):** For each sensitive field, Servy supports an associated environment variable. Servy reads these variables transparently at install time, ensuring the secret never touches the process argument string.
2. **Import Configuration (Recommended for complex deployments):** Use the `import` command with an XML or JSON configuration file. This keeps sensitive values entirely out of the command line and allows for structured, version-controlled configuration management.
#### Sensitive Fields Reference
The following parameters are considered sensitive and should be provided via environment variables (v8.5+) or configuration files:
| Parameter | Environment Variable | Description |
| --- | --- | --- |
| `--password` | `SERVY_PASSWORD` | Windows service account password. |
| `--params` | `SERVY_PROCESS_PARAMETERS` | Command-line arguments for the service process. |
| `--envVars` | `SERVY_ENVIRONMENT_VARIABLES` | Environment variables for the service process. |
| `--failureProgramParams` | `SERVY_FAILURE_PROGRAM_PARAMETERS` | Arguments for the failure recovery program. |
| `--preLaunchParams` | `SERVY_PRE_LAUNCH_PARAMETERS` | Arguments for the pre-launch executable. |
| `--preLaunchEnv` | `SERVY_PRE_LAUNCH_ENVIRONMENT_VARIABLES` | Env vars for the pre-launch executable. |
| `--postLaunchParams` | `SERVY_POST_LAUNCH_PARAMETERS` | Arguments for the post-launch executable. |
| `--preStopParams` | `SERVY_PRE_STOP_PARAMETERS` | Arguments for the pre-stop executable. |
| `--postStopParams` | `SERVY_POST_STOP_PARAMETERS` | Arguments for the post-stop executable. |
| `--heartbeatUrl` | `SERVY_HEARTBEAT_URL` | Heartbeat ping URL. Monitoring endpoints such as `https://hc-ping.com/` are capability URLs: anyone who learns the path can fake a "service is alive" ping. Servy masks the URL in its own logs. Available from v10.4; earlier versions ignore the variable, so pass `--heartbeatUrl` there. |
##### PowerShell Example:
```powershell
# Set the secrets in the process-level environment
$env:SERVY_PASSWORD = 'p@ssw0rd_123!'
$env:SERVY_ENVIRONMENT_VARIABLES = 'API_KEY=secret_key_123;DB_URL=...'
# Install the service (omit sensitive flags)
servy-cli install --name="MySecureService" --path="C:\App\app.exe" --user="DOMAIN\svc_account"
# Clear variables immediately after use
Remove-Item Env:SERVY_PASSWORD
Remove-Item Env:SERVY_ENVIRONMENT_VARIABLES
```
## Infiltration Guard: Local Import Enforcement
Importing service configurations from Universal Naming Convention (UNC) paths or through redirected paths (such as symbolic links or junctions) poses severe security risks. These techniques are designed to bypass system trust boundaries and expose the application execution layer to malicious configuration injection.
To preserve system integrity, the import pipeline explicitly blocks non-local paths. The primary attack vectors mitigated by this enforcement include:
* **Attacker-Controlled Configuration Injection:** UNC targets (e.g., `\\attacker\share\evil.xml`) allow a remote adversary to host a malicious configuration file on an infrastructure node under their direct control. If ingested, an attacker can inject arbitrary executable paths, unauthorized parameters, or unverified environment variables, effectively hijacking the service's runtime behavior.
* **Path Redirection Attacks:** By leveraging filesystem symbolic links, directory junctions, or specialized Win32 reparse points, an attacker can manipulate the path resolution mechanics. This can trick the engine into reading from an entirely different backend target than intended, exposing sensitive files or pulling parameters from unexpected network locations.
* **Privilege Escalation and System Integrity:** Because the engine performs administrative elevation validation checks to operate securely, it possesses high-privilege access to the local machine. If an import task is manipulated into processing files from protected operating system directories (such as the `Windows` or `System32` namespaces), it can facilitate unintended system-level file access or manipulation.
* **Network-Based Mapping Bypasses:** Virtual local paths - including mapped network drives (e.g., `Z:\config.json`) or local DOS device substitutions (`subst`) - frequently mask underlying remote storage volumes. These targets inherently lack the rigid security boundaries of localized storage hardware, introducing network-level interception vectors.
### Mitigation: Defense-in-Depth Pipeline
To counter these vectors, the engine pipes all configuration paths through a strict, sequential validation chain before any file access occurs across the CLI or GUI interfaces:
1. **Explicit UNC Inspection:** Rejects any raw path strings starting with standard network prefixes (`\\`) or parsing as a remote URI.
2. **Drive Interface Queries:** Evaluates the target volume via `DriveInfo` to proactively block network-backed logical drive letters.
3. **Reparse Point Ancestor Walks:** Recursively traces the full directory tree to verify that no parent or sibling paths utilize symbolic links or directory junctions.
4. **File-Level Symlink Evaluation:** Directly inspects filesystem attributes to confirm the target file is a physical, non-symbolic entity.
5. **Reserved Device Blocks:** Prevents spoofing attempts using legacy system device designations (e.g., `CON`, `PRN`, `AUX`).
6. **Protected System Directory Fencing:** Prevents configuration loading out of primary administrative operating system paths.
7. **Win32 Kernel Handle Finalization:** Opens a temporary restricted read handle and resolves the target's final canonical path via `GetFinalPathNameByHandle`, ensuring junctions, subst mappings, and virtual devices cannot conceal a UNC target.
## Supply Chain and Trust
Security requires transparency. You should not have to guess if Servy is safe.
* **Digitally Signed:** All executables and installers are signed by SignPath. This proves the code has not been altered since it left the build server.
* **SBOM (Software Bill of Materials):** Servy releases include a full inventory of every component and dependency in the CycloneDX format.
* **Vulnerability Scanning:** GitHub Dependabot raises an alert whenever a published advisory matches one of the dependencies.
* **Scanned Releases:** Release binaries are scanned on VirusTotal, and false-positive reports are submitted to Microsoft Security Intelligence and affected AV vendors.
## The Servy Trust Boundary
Starting from v10.2, Servy uses **per-service security isolation**. Before v10.2, every service account shared a single trust boundary: each one could read and change the shared configuration database, read the encryption key, and read or delete the logs and recovery state of every other service. v10.2 removes that shared tier. A service account can now reach only its own configuration, its own runtime state and the log folders of its own services (`logs\services\\`).
### Architectural Design & Runtime Permissions
v10.2 adds a background Windows service, **`Servy`** (`Servy.Host.exe`). It runs as `LocalSystem` and is the only process that opens the configuration database and the encryption key on behalf of a running service. Each `Servy.Service.exe` still runs under the identity of your configured service account, but it no longer opens `db\Servy.db` or `security\aes_key.dat` itself. It asks the `Servy` service for what it needs over a local named pipe (`\\.\pipe\SERVY_HOST_IPC_PIPE`):
* **Reading its configuration:** the service receives its own decrypted configuration, and nothing else. The password of the service account is never sent.
* **Writing its runtime state:** the process ID of the wrapped application, the active `stdout`/`stderr` log paths and the stop timeout in effect are written for it.
* **Restart attempts:** the restart-attempt counter of the recovery feature is stored in `Servy.db` and read and written through the pipe. Before v10.2 it was kept in text files in `%ProgramData%\Servy\recovery\`; that folder is imported into the database and deleted on the first start of v10.2.
The `Servy` service checks every request:
* **Who may connect:** the pipe is created with a protected access list that grants `NT AUTHORITY\SYSTEM` and `BUILTIN\Administrators` Full Control, grants the accounts of the services Servy manages **Read/Write** only. Network logons are supported: a local process whose token comes from a network logon (for example `servy-cli` in a PowerShell remoting or WinRM session) is checked like any other. The pipe is local only: it is created with `PIPE_REJECT_REMOTE_CLIENTS`, so a client on another computer cannot open it, and the `Servy` service also refuses any request from one. The list is rebuilt whenever a service is installed, reinstalled under another account or removed, and is applied to the pipe immediately, without restarting the `Servy` service, so an account loses access to the pipe when its last service goes away.
* **What a caller may ask for:** a service account may only read or update the service it is running. The `Servy` service compares the process ID of the caller (`GetNamedPipeClientProcessId`) with the process ID the Service Control Manager reports for the service named in the request, and refuses the request when they differ. Administrators and `SYSTEM` are not restricted.
* **Who the client talks to:** before sending anything, `Servy.Service.exe` checks that the process serving the pipe is the one the Service Control Manager reports for the `Servy` service, so another process cannot squat the pipe name and serve a forged configuration.
Every service Servy installs depends on the `Servy` service, so Windows starts it first. The Desktop App, Servy Manager and `servy-cli` extract `Servy.Host.exe` into `%ProgramData%\Servy`, install the `Servy` service when it is missing (startup type **Automatic**), start it when it is not running, and add the dependency to services installed by an older version. When the `Servy` service is registered with another executable than the one they extracted (after switching between the .NET 10 and the .NET Framework 4.8 build, which names it `Servy.Host.Net48.exe`), they stop the running Servy services and the `Servy` service, point the service at their own executable, and start them again. When `Servy.Service.exe` or `Servy.Host.exe` has to be updated, they stop the running Servy services and the `Servy` service, replace the binaries, then start them again. The name `Servy` is reserved and cannot be used for one of your own services.
`Servy.Service.exe` writes its own log to the folder of its service, `%ProgramData%\Servy\logs\services\\Servy.Service.log`, and `Servy.Restarter.exe`, which it launches, writes `Servy.Restarter.log` to the same folder. A character a folder name cannot hold (`< > : " / \ | ? *` and control characters), a trailing dot or space, a reserved device name such as `CON`, and the characters `%` and `~` are percent-encoded in the folder name (`My:Service` logs to `logs\services\My%3AService\`), so two services never share a folder; a name whose encoded form is longer than 100 characters is shortened to its first 64 encoded characters, followed by `~` and 16 hexadecimal characters of a hash of the name. Before v10.2 the log of every service was `logs\Servy.Service.log`. On an upgrade that file is left where it is, in `logs\`, for the administrators: it is not moved or deleted, no service writes to it any more, and no service account can read it, since it may hold the entries of every service. Delete it once you no longer need it.
### Executable Permission Hardening
A service account gets the least privilege its services need: nothing on the vault root, nothing in `db\`, `security\` and `logs\`, write access only in the log folder of each of its own services (`logs\services\\`), **Read & Execute** on the binaries and **Read** on the configuration files. Starting from v10.2, Servy applies this hardening itself. It is built into the Desktop App, Servy Manager, `servy-cli` and the PowerShell module, so it works wherever they run, including a portable `servy-cli.exe` copied on its own. No script and no manual step are needed.
> [!NOTE]
> **AUTOMATIC SINCE v10.2**
>
> Servy hardens the vault for an account:
>
> * when it installs a service under an account other than Local System (including `LocalService`, `NetworkService`, virtual accounts and gMSAs), and when it reconfigures an existing service under such an account;
> * again for every account a service runs under, whenever the Desktop App, Servy Manager or `servy-cli` extracts one of its binaries into `%ProgramData%\Servy` (a first run or an update), because a newly written file carries no grant for the service accounts and would be unreadable to them.
>
> Local System, the `BUILTIN\Administrators` group and its members are left untouched: they keep **Full Control**, so there is nothing to restrict. The outcome is written to the Servy log. If the hardening cannot be applied, the service is still installed and the log says why; installing the service again re-applies it.
#### What Is Hardened
| Path under `%ProgramData%\Servy` | Service account's rights |
| :--- | :--- |
| The vault folder itself | **None**. A grant a previous version wrote there is removed. |
| `db\`, `security\` | **None**. The account's explicit entries on the folders and on the files directly inside them (`Servy.db`, `aes_key.dat`, ...) are removed. Only the `Servy` service, running as `LocalSystem`, opens them. |
| `logs\` and everything under it, other than the account's own service log folders | **None**. The account cannot list, read, write or delete the logs of Servy, the shared `Servy.Service.log` that versions before v10.2 wrote to `logs\`, or the log folder of any service it does not run. The account's explicit entries on every file and folder under `logs\` are removed. |
| `logs\services\\`, for each service the account runs | On the folder: **List** and **Create Files** (creating the log, and the new file of a log rotation), never **Delete** (it cannot rename or delete the folder). On the files in it: **Read, Write, Delete** (log rotation and pruning). The folder is created if missing. |
| `Servy.Host.exe` / `Servy.Host.Net48.exe` | **None**. The service account cannot read, write or execute the host binaries. |
| `*.exe` and `*.dll`: `Servy.Service.exe`, `Servy.Service.CLI.exe`, `Servy.Restarter.exe`, `handle64.exe` / `handle64a.exe` (.NET Framework 4.8 build: the `*.Net48.exe` binaries, `handle64.exe` and every `*.dll`) | **Read & Execute** |
| `appsettings.host.json` / `Servy.Host.Net48.exe.config` | **None**. The service account cannot read the host configuration file. |
| `*.json` and `*.config`: `appsettings.service.json`, `appsettings.restarter.json` (.NET Framework 4.8 build: the `*.exe.config` files) | **Read** only |
The service account needs no right on `logs\` or `logs\services\` to reach `logs\services\\`: Windows grants every account the **Bypass traverse checking** privilege by default, so a full path can be opened without listing its parents.
Accounts are compared by SID, so a service account that runs several services (written `.\user` for one and `MACHINE\user` for another, for example) is granted the folders of all of them, and installing one more service under it keeps the folders of the others. Two services that run under the same account share that account's identity, so they can read each other's logs; give each service its own account when their logs must be isolated from each other as well.
Each hardened file stops inheriting from the vault, is owned by `BUILTIN\Administrators`, and keeps **Full Control** for `NT AUTHORITY\SYSTEM` and `BUILTIN\Administrators` (well-known SIDs, so it works on any Windows language). Explicit grants to `Everyone`, `Users` and `Authenticated Users` are removed. Explicit entries for other accounts are kept, so hardening one service account never removes another's access. The access is taken back when a service goes away: after a service is uninstalled, or reinstalled under another account, Servy always removes the former account's entries from that service's log folder, `logs\services\\` (the folder and its logs are kept, for the administrators). When no other service in the database still runs under that account (the same account written as `.\user` or `MACHINE\user` counts as one), Servy also removes the account's explicit entries from the vault root, `db\`, `security\`, every file and folder under `logs\` and every hardened file, and revokes its access to the named pipe. A failure is logged and does not fail the uninstall. The service account can delete nothing outside the log folders of its own services, and no folder. A file that is a symbolic link or a junction, or that has more than one hard link, is left untouched and reported as a failure. A binary that is not present yet (for example `Servy.Service.CLI.exe` when only the Desktop App has been used) is hardened once it is extracted.
When an existing installation is upgraded to v10.2, the grants that earlier versions gave the service accounts on `db\`, `logs\`, `recovery\`, `Servy.db` and `aes_key.dat` are removed, and the log folder of each service is created and granted to its account, the next time the vault is hardened for each account, which happens when the Desktop App, Servy Manager or `servy-cli` extracts the new binaries. The `recovery\` folder itself is deleted once its counters have been imported into `Servy.db`.
Since v10.2, `Servy.Restarter.exe` is extracted by the Desktop App, Servy Manager and `servy-cli` rather than by the service, so the service account needs no **Delete** right on it.
**Behavior Once Hardened:**
The hardening breaks ACL inheritance on Servy's core binaries and restricts explicit execution rights, so a custom service account added later does **not** inherit rights on the hardened binaries from the `%ProgramData%\Servy` parent directory. Servy hardens each account when a service is installed under it; an account that is only granted permissions on the vault root by hand would be denied execution rights on `Servy.Service.exe` by the Windows Service Control Manager (SCM) (`ERROR_ACCESS_DENIED`).
The executable permission hardening protects your deployment against:
* **Unprivileged Binary Replacement & Tampering:** Prevents a compromised service process or runner account from overwriting core binaries (`Servy.Service.exe`, `Servy.Restarter.exe`, `Servy.Host.exe`, etc.) to execute arbitrary code under elevated administrative contexts.
* **DLL Hijacking:** Blocks rogue or compromised non-admin identities from planting or replacing shared `.dll` dependencies within the application vault directory.
* **Local Privilege Escalation (LPE):** Ensures that service runner privileges cannot be leveraged to gain unauthorized write access over executable components executed by `SYSTEM` or `Administrators`.
* **Configuration Database Access:** A service account cannot open `db\Servy.db` at all. It can neither read the configuration of other services nor change, replace or delete the database.
* **Encryption Key Access:** A service account cannot open `security\aes_key.dat`, so it can neither read nor replace the key every Servy process trusts.
* **Cross-Service Log Access:** A service account cannot read, write or delete Servy's own logs or the log folder of any service that runs under another account.
> [!IMPORTANT]
> **Administrative Service Accounts:** Use a dedicated, unprivileged service account. Because Windows evaluates all group SIDs attached to an access token and `Allow` ACEs do not subtract rights, any member of `BUILTIN\Administrators` inherently keeps `FullControl` over the hardened files through group membership, which is why Servy skips the hardening for such an account. Membership through a nested group (for example a domain group added to the local `Administrators` group) cannot always be detected; Servy then applies the hardening and logs that it could not rule it out.
> [!NOTE]
> Starting from v9.7, Servy automatically captures and preserves existing explicit Access Control Lists (ACLs) across atomic file updates for `*.exe` and `*.dll` files located in `%ProgramData%\Servy`. Once configured, your **Read & Execute** permission boundaries will persist automatically across application updates and embedded resource re-extractions.
#### Servy 10.1 and Earlier
Servy 10.1 and earlier do not harden the vault automatically. Run `Set-ServyExePermissions.ps1` from an **Elevated (Administrator)** PowerShell session for every custom account (or a group containing it) that runs a Servy service. Without it, the account has no access to the vault, or, if the vault was granted **Modify** by hand, it keeps inherited `Modify` rights on the core binaries and can replace or tamper with code executed by `SYSTEM` and `Administrators`.
* **Servy v9.7 to 10.1:** the script is located in `%ProgramFiles%\Servy` after installation, and in the root of the portable package. That copy does not grant the vault access: with it, first grant the account **Modify** on `%ProgramData%\Servy` (inherited by subfolders and files), or use the version linked below.
* **Latest script for these versions** (it keeps the **Delete** right on `Servy.Restarter.exe` that these versions need, because their service extracts the restarter itself):
* [Modern Build (.NET 10.0+)](https://raw.githubusercontent.com/aelassas/servy/e11bd64c772da66c7c83b18c9c32576fd259594c/setup/Set-ServyExePermissions.ps1)
* [.NET Framework 4.8 Build](https://raw.githubusercontent.com/aelassas/servy/3e91477330644e3c8edacf29ea0bd75927d3c3a7/setup/Set-ServyExePermissions.ps1)
```powershell
# LocalService account
.\Set-ServyExePermissions.ps1 -TargetAccount "LocalService"
# Local account / relative notation
.\Set-ServyExePermissions.ps1 -TargetAccount ".\user_svc"
# Active Directory domain user or group
.\Set-ServyExePermissions.ps1 -TargetAccount "MYDOMAIN\svc-servy"
# Group Managed Service Account (gMSA)
.\Set-ServyExePermissions.ps1 -TargetAccount "CORP\app-gmsa$"
```
The script applies the hardening of those versions: write access to `db\`, `logs\` and `recovery\`, **Read & Execute** on the binaries and **Read** on the configuration files. It does not cover `aes_key.dat`. Re-running it for another account preserves the permissions of the accounts it hardened before. If `Servy.db` is recreated (for example after purging `db\` on a cloned VM), re-run it.
> [!TIP]
> **Managing Multiple Service Accounts:** On these versions, if you manage dozens of custom runner accounts on a single server, consider creating a dedicated local or domain security group (e.g., `Servy-ServiceRunners`), running `Set-ServyExePermissions.ps1 -TargetAccount "Servy-ServiceRunners"` once, and adding each new service account to that group.
> [!IMPORTANT]
> Prior to v9.7, ACLs for `*.exe` and `*.dll` files inside `%ProgramData%\Servy` are **not preserved** across atomic updates. Upgrading or re-extracting binaries on older versions will cause new files to inherit default directory permissions (`Modify`), requiring `Set-ServyExePermissions.ps1` to be re-run after each update to restore **Read & Execute** hardening.
### What This Means for Your Deployment
* **Per-Service Isolation:** Each service account reaches only its own configuration and runtime state, through the `Servy` service, and only the log folders of its own services. It cannot read the configuration database, the encryption key, the logs of services running under other accounts or Servy's own logs, and it cannot change the records of another service.
* **Per-Service Log Folders:** Each service writes its wrapper and restarter logs to its own folder, `logs\services\\`, which only its own account (plus `SYSTEM` and `Administrators`) can reach. The `stdout`/`stderr` logs of your application are written wherever you configure them and are not affected.
* **The `Servy` Service Must Run:** A service cannot start without its configuration, so every Servy service depends on the `Servy` service. If it is stopped or disabled, the Desktop App, Servy Manager and `servy-cli` start it again on their next run; you can also start it from `services.msc`.
* **Zero Trust Isolation:** Per-service isolation protects co-located services from each other's service accounts. It does not protect against an administrator or `SYSTEM`, and it does not isolate the applications themselves (files, network, registry). If your security architecture requires strict multi-tenant isolation, services should be deployed across distinct Virtual Machines, isolated OS installations, or Windows Containers.
### Automatic Permissions Table (v7.9+)
| Identity | Access Level | Managed By | Description |
| :--- | :--- | :--- | :--- |
| **SYSTEM** | Full Control | Servy (Automatic) | Required for local system service host management. |
| **Administrators** | Full Control | Servy (Automatic) | Required for administrative configuration. |
| **Installing User** | Full Control | Servy (Automatic) | Preserved for operational continuity when installed non-elevated. |
| **Custom Service Accounts** | None on the vault root, `db\`, `security\` and `logs\`; write access to the log folder of each of their own services (`logs\services\\`) only, Read & Execute on the binaries, Read on the configuration files (see [What Is Hardened](https://github.com/aelassas/servy/wiki/Security#what-is-hardened)) | Servy (Automatic, v10.2+) | Granted by Servy when a service is installed under a non-SYSTEM identity. On 10.1 and earlier, **Modify** on the vault, granted by `Set-ServyExePermissions.ps1` run by hand. |
| **Standard Users** | None | Servy (Automatic) | Explicitly purged on startup to prevent standard user tampering. |
> [!NOTE]
> In Servy's ACL hardening context, **Standard Users** refers to broad identity groups including `Everyone`, `Users`, or `Authenticated Users`.
> [!NOTE]
> The installing user ACE is added by `SecurityHelper.ApplySecurityRules` only when the current process identity is **neither SYSTEM nor an Administrator**. An elevated (admin) install adds no ACE of its own: that account is already covered by the `BUILTIN\Administrators` grant.
> [!IMPORTANT]
> If you configure a service to run under a custom local or domain Service Account (or gMSA), that account needs write access to the log folder of its service (`logs\services\\`), read access to the binaries and configuration files, and access to the `Servy` service's named pipe. Starting from v10.2 Servy grants exactly that automatically when it installs the service; on 10.1 and earlier, run `Set-ServyExePermissions.ps1` for the account, which grants `Modify` on `%ProgramData%\Servy`. Without that access, the service runner will fail to load its configuration or write its logs. See [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening) section for details.
## File Locations and Recovery
Your master encryption keys are stored here:
* **Database:** `%ProgramData%\Servy\db\Servy.db` (opened only by the `Servy` service, Servy Manager, the Desktop App and `servy-cli`)
* **Key:** `%ProgramData%\Servy\security\aes_key.dat`
* **IV:** `%ProgramData%\Servy\security\aes_iv.dat`
The `aes_iv.dat` file holds the static IV used by the legacy v1 cipher format (Servy < 6.5). Servy 6.5+ uses a per-message random IV embedded in the ciphertext, so the static IV is no longer used to encrypt or decrypt anything in current builds - v1 decryption is permanently disabled (`AllowLegacyV1Decryption = false`) to mitigate downgrade attacks. While disabled, the file is **not** read on service start and is **not** loaded into memory; the entire load path is compiled out via the `AllowLegacyV1Decryption` constant, so the runtime no longer creates `aes_iv.dat` on fresh installs; the file is only present on machines that were first set up by an older (pre-gating) build, where it should be retained - **do not delete it.** To migrate records written by a pre-6.5 build, export them with a v1-compatible Servy version and import the resulting file into the current version; they will be re-encrypted as v2.
## Critical Warning: Machine Migration
Because the encryption is tied to your specific Windows installation, you cannot copy the `.dat` files to a new server.
To move Servy to a new PC, use [Servy-Dump.ps1 and Servy-Restore.ps1](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning) to export and import your service configurations. If you attempt to copy the `.dat` files to a new machine, Servy will fail to decrypt any of the sensitive fields.
## Best Practices
* **Backup the Whole Servy Data Folder:** Before doing a Windows Reset or Refresh, back up the entire `%ProgramData%\Servy\` tree. The keys (`security\*.dat`) and the encrypted configuration database (`db\Servy.db`) must be restored together - keys alone cannot decrypt anything, and the database alone cannot be decrypted without the matching keys.
* **Use Managed Accounts:** When possible, run services under Group Managed Service Accounts (gMSA) for the best balance of security and ease of use.
* **Audit Access:** Periodically check the Security tab of the `%ProgramData%\Servy` folder to ensure no unauthorized users have been added manually.
## Troubleshooting
* **Access Denied on Startup:** This usually means the account running the service does not have permissions to the `%ProgramData%\Servy` folder. Refer to the Permissions Table above.
* **Service Cannot Load Its Configuration (v10.2+):** Check that the `Servy` service is installed and running (`sc.exe query Servy`), then read `%ProgramData%\Servy\logs\Servy.Host.log` and `%ProgramData%\Servy\logs\services\\Servy.Service.log`. Running the Desktop App, Servy Manager or any `servy-cli` command reinstalls and starts the `Servy` service if needed; reinstalling the service re-grants its account access to the named pipe.
* **Decryption Error:** This happens if the `.dat` files were moved from another computer, restored from an image, corrupted, or if the Windows `MachineGuid` was altered. Servy then refuses to save any service whose encrypted fields (`Password`, `Parameters`, `EnvironmentVariables`, `PreLaunchEnvironmentVariables` and the hook parameters `PreLaunchParameters`, `PostLaunchParameters`, `PreStopParameters`, `PostStopParameters`, `FailureProgramParameters`) cannot be decrypted, so they cannot be re-entered in place, and it never replaces an existing `aes_key.dat`. To recover:
1. Do **not** delete or replace `aes_key.dat` or `aes_iv.dat`: a new key can never decrypt the existing database.
2. Restore `%ProgramData%\Servy\security\aes_key.dat` and `aes_iv.dat` from a backup of **this** machine, then restart Servy.
3. Otherwise, copy `%ProgramData%\Servy\db\Servy.db` back to the original machine and export the service configurations to XML or JSON there. You can use [`Servy-Dump.ps1`](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning.md) to perform the batch export.
4. Only then, on this machine, back up and delete `%ProgramData%\Servy\security` and `%ProgramData%\Servy\db`, and import the exported configurations (per *Critical Warning: Machine Migration*). Usernames and passwords are not exported and must be re-entered. You can use [`Servy-Restore.ps1`](https://github.com/aelassas/servy/wiki/Backup-Restore-&-VM-Cloning.md) to perform the batch import.
Servy v7.9+ automates the routine parts of Windows security - Access Control Lists (ACLs) and machine-bound key derivation - so a locked-down setup is the default rather than a manual checklist.
Questions? Check the full [Troubleshooting Guide](https://github.com/aelassas/servy/wiki/Troubleshooting) or open a [GitHub Issue](https://github.com/aelassas/servy/issues) to get help from the community and developers.
---
# Document: Architecture
> Source: https://github.com/aelassas/servy/wiki/Architecture
## Table of Contents
1. [Overview](https://github.com/aelassas/servy/wiki/Architecture#overview)
1. [Technology Stack](https://github.com/aelassas/servy/wiki/Architecture#technology-stack)
1. [Project Structure](https://github.com/aelassas/servy/wiki/Architecture#project-structure)
1. [Architecture Layers](https://github.com/aelassas/servy/wiki/Architecture#architecture-layers)
1. [Project Details](https://github.com/aelassas/servy/wiki/Architecture#project-details)
1. [Integration Flow](https://github.com/aelassas/servy/wiki/Architecture#integration-flow)
1. [Design Patterns](https://github.com/aelassas/servy/wiki/Architecture#design-patterns)
1. [Tests](https://github.com/aelassas/servy/wiki/Architecture#tests)
1. [Contributing](https://github.com/aelassas/servy/wiki/Architecture#contributing)
## Overview
Servy is a Windows application that lets you run any executable as a Windows service through a simple CLI, PowerShell module, or GUI interface. It provides a reliable solution for the latest environments (**Windows 10 1809+**, **Windows 11**, and **Windows Server 2016+**) while maintaining deep compatibility for legacy infrastructure (**Windows 7 SP1**, **Windows 8**, **Windows 8.1**, and **Server 2008 R2+**) via a dedicated **.NET Framework 4.8** build.
Designed as a next-generation alternative to NSSM and WinSW, Servy is built in C# to ensure transparency and modularity. The codebase is strictly organized to separate core logic, service management, and UI layers, making it highly maintainable for future .NET evolutions.
Starting from v10.2, Servy runs a background Windows service of its own, **`Servy`** (`Servy.Host`), under `LocalSystem`. It is the only process that opens the configuration database (`Servy.db`) and the encryption key on behalf of a running service. Each service wrapper (`Servy.Service.exe`) still runs under the service's own account, but it no longer opens the database or the key itself: it asks the `Servy` service over a local named pipe (`SERVY_HOST_IPC_PIPE`) for its own configuration, and to store its runtime state (the PID of the wrapped process, the active `stdout`/`stderr` log paths, the stop timeout in effect) and its restart attempts counter. The `Servy` service answers a request about a service only to the process the Service Control Manager runs that service in, or to an administrator, so a service account can reach only its own configuration and runtime state. Each wrapper writes its own log to its own folder, `logs\services\\`. Every service Servy installs depends on the `Servy` service, which the Desktop App, Servy Manager and `servy-cli` install and start when needed. See [Security](https://github.com/aelassas/servy/wiki/Security#the-servy-trust-boundary) for the full trust model.
Servy is available in two versions, each maintained in a separate branch:
* **.NET 10.0+ version:** located on the [`main`](https://github.com/aelassas/servy/tree/main) branch
* **.NET Framework 4.8 version:** located on the [`net48`](https://github.com/aelassas/servy/tree/net48) branch
## Technology Stack
### Core Technologies
* **Modern Build:** .NET 10.0+ (Primary framework for current Windows 10 (1809+) / 11 / Server 2016+).
* **Legacy Build:** .NET Framework 4.8 (Maintained for compatibility with Windows 7 SP1 / 8 / 8.1 / Server 2008 R2+).
* **WPF (Windows Presentation Foundation):** Powers the desktop app and Servy Manager interfaces.
* **Windows API:** Direct integration for service lifecycle and ACL management.
### Development & Automation
* **C#:** Primary language for core and UI development.
* **PowerShell:** High-level automation via the `Servy` module.
* **Inno Setup:** Used to generate signed, production-ready installers.
### System Requirements
| Requirement | Modern Build (Default) | Legacy Build (net48) |
| :--- | :--- | :--- |
| **Operating System** | Windows 10 (1809+) / 11 / Server 2016+ | Windows 7 SP1 / 8 / 8.1 / Server 2008 R2+ |
| **Runtime** | Included - self-contained (no separate install) | .NET Framework 4.8 |
| **Architecture** | x64 / ARM64 | x64 |
| **Privileges** | Administrator | Administrator |
## Project Structure
The Servy solution consists of nine main projects:
```mermaid
flowchart LR
SLN["Servy.sln"]
subgraph Apps ["Applications"]
P1["Servy
WPF Application"]
P2["Servy.Manager
WPF Application"]
P3["Servy.CLI
Console Application"]
end
subgraph Libraries ["Libraries"]
P4["Servy.UI
WPF Class Library"]
P5["Servy.Core
Class Library"]
P6["Servy.Infrastructure
Class Library"]
end
subgraph Services ["Windows Services and Utilities"]
P7["Servy.Host
Windows Service"]
P8["Servy.Service
Windows Service"]
P9["Servy.Restarter
Console Application"]
end
SLN --> Apps
SLN --> Libraries
SLN --> Services
```
| Project | Type | Description |
|---------|------|-------------|
| **Servy** | WPF Application | Main user interface for service configuration and management |
| **Servy.Manager** | WPF Application | Main user interface for managing and monitoring installed services |
| **Servy.UI** | WPF Class Library | Shared components, services and WPF utilities |
| **Servy.CLI** | Console Application | Main CLI for service configuration and management |
| **Servy.Core** | Class Library | Shared functionality, utilities, and data models |
| **Servy.Infrastructure** | Class Library | Data access, persistence, and integration with external systems (e.g., SQLite database) |
| **Servy.Host** | Windows Service | The `Servy` service (`LocalSystem`): serves each service wrapper its own configuration and runtime state over a local named pipe, so service accounts never open the database or the encryption key |
| **Servy.Service** | Windows Service | Windows Service executable that wraps target processes |
| **Servy.Restarter** | Console Application | Service restart utility |
## Architecture Layers
Servy follows Clean Architecture principles, separating responsibilities into distinct tiers for clarity, maintainability, and testability:
```text
┌───────────────────────────────────┐
│ │
│ Presentation Layer │
│ │
│ (Servy Apps - GUI & CLI) │
│ Handles user interaction, input, │
│ and output. Communicates with the │
│ business logic layer. │
│ │
├───────────────────────────────────┤
│ │
│ Business Logic Layer │
│ │
│ (Servy.Core) │
│ Implements the core functionality,│
│ workflows, and service management │
│ rules, independent of UI. │
│ │
├───────────────────────────────────┤
│ │
│ Infrastructure Layer │
│ │
│ (Servy.Infrastructure) │
│ Provides data persistence, access │
│ to the SQLite database, and │
│ external system integration. │
│ │
├───────────────────────────────────┤
│ │
│ Host Layer │
│ │
│ (Servy.Host) │
│ The Servy service (LocalSystem). │
│ The only process that opens the │
│ database and the encryption key │
│ for running services. Serves each │
│ wrapper its own data over a local │
│ named pipe. │
│ │
├─────────────── IPC ───────────────┤
│ │
│ Service Layer │
│ │
│ (Servy.Service) │
│ A Windows Service host that runs │
│ configured apps in the background,│
│ monitors them, and applies health │
│ checks and restart policies. Runs │
│ under the service's own account │
│ and reads its configuration from │
│ Servy.Host, never from the │
│ database. │
│ │
└───────────────────────────────────┘
```
The diagram above represents Servy's layered architecture following Clean Architecture principles. At the center is the Core layer (`Servy.Core`), which contains the domain entities (`Service`), abstractions/interfaces (`IServiceRepository`, `IServiceManager`), XML/JSON serialization, and data encryption. External services and APIs are referenced here, following the dependency inversion principle. This layer is independent of external dependencies, ensuring business logic remains decoupled and testable.
```mermaid
flowchart TB
subgraph Infrastructure ["Servy.Infrastructure"]
A[ServiceRepository Implementation]
end
subgraph Core ["Servy.Core"]
B[IServiceRepository]
C[Xml/Json Serialization Services]
D[SecureData Service]
E[Service Domain]
F[IServiceManager]
end
subgraph Application ["Application / Orchestrator"]
G[ServiceCommands]
end
subgraph Host ["Servy.Host (Servy service, LocalSystem)"]
H[Named pipe listener]
I[Caller authorization]
end
subgraph Wrapper ["Servy.Service (service account)"]
J[Service wrapper]
end
subgraph IPC ["Servy.Core.NamedPipes"]
K[INamedPipesService / NamedPipesService]
end
%% Dependencies
A --> B
A --> C
A --> D
G --> E
G --> F
G --> B
H --> I
I --> B
J --> K
K -. "SERVY_HOST_IPC_PIPE" .-> H
```
The Infrastructure layer (`Servy.Infrastructure`) implements the Core interfaces, providing concrete functionality like database persistence (`ServiceRepository`).
Starting from v10.2, the Host layer (`Servy.Host`) is the only consumer of that persistence among the processes that run as services. It references `Servy.Core` and `Servy.Infrastructure`, opens `Servy.db` and the encryption key at startup, and answers the service wrappers over the named pipe. The Service layer (`Servy.Service`) references `Servy.Core` only: it reaches its configuration and runtime state through `INamedPipesService` (`Servy.Core.NamedPipes`), which sends each request to the `Servy` service instead of querying the database. The Desktop App, Servy Manager and the CLI still open the database directly, as administrators.
The Application or Orchestrator layer coordinates domain operations, calling repositories and services, without containing business rules itself. In Clean Architecture terms, dependencies point inwards toward the Core, ensuring the inner domain remains stable even as infrastructure changes.
This separation allows for flexible testing, easier maintenance, and adaptability, as the domain logic does not rely on concrete implementations or frameworks.
## Project Details
### Servy (Desktop App)
The main WPF application provides a user interface for creating and managing Windows services. The application is built using the MVVM (Model-View-ViewModel) design pattern to ensure clean separation of concerns and maintainable code architecture.
**Key Responsibilities:**
* Provide user-friendly WPF interface for service configuration
* Handle user input validation
* Communicate with Windows Service Control Manager
* Manage service installation, uninstallation, and configuration
* Handle UAC elevation requests
**Key Features:**
* Service name & description configuration
* Startup type selection (Automatic, AutomaticDelayedStart, Manual, Disabled)
* Process priority settings (Idle to Real Time)
* CPU affinity
* Custom working directory and parameters
* Output redirection with log rotation
* Health checks and automatic service recovery
* Environment variables
* Service dependencies
* Pre-launch and post-launch hooks
* Pre-stop and post-stop hooks
* Admin privilege management
### Servy Manager
Servy Manager is the graphical interface for managing Windows services created with Servy. It provides an intuitive and centralized way to install, configure, and control services while persisting all configurations in a local database. Unlike directly working with the Windows Service Control Manager (SCM), Servy Manager adds a layer of convenience, advanced features, and structured service management.
**Key Responsibilities:**
* Persist service information in a local database to maintain a consistent view of installed and imported services
* Provide a user-friendly interface for managing the lifecycle of services (install, edit, start, stop, restart, copy PID, uninstall, remove)
* Allow service configurations to be imported into the database without requiring immediate installation
* Offer tools for viewing and editing service configurations directly within the application
* Integrate with logging to make monitoring and troubleshooting services faster and more efficient
**Key Features:**
* Service listing: View all services installed or imported in Servy
* Service control: Start, stop, and restart services from the UI
* Service installation: Install services from configuration files, uninstall or remove them when no longer needed
* Export: Save service configurations in XML or JSON format
* Import: Add service configurations to the database without installing them immediately
* Configuration editor: Open and edit service configurations directly from the interface
* Search: Quickly locate services by name or properties
* Performance: Monitor CPU and RAM service usage with live performance graphs
* Console: Preview service stdout and stderr output in real-time
* Dependencies: Preview Service Dependencies
* Logs: Browse service logs with advanced filtering by log level, date, and keyword
### Servy CLI (CLI)
The Servy CLI provides a text-based interface for advanced users and automation scenarios to create, configure, and manage Windows services. It complements the main WPF application by enabling scripting, CI/CD integration, and headless usage.
**Key Responsibilities:**
* Configure Windows services via command-line parameters and scripts
* Install, uninstall, start, stop, and query services without UI
* Support all service settings (name, description, startup type, priority, working directory, parameters)
* Manage output redirection and log rotation programmatically
* Request UAC elevation when required
* Return meaningful exit codes for scripting automation and error handling
**Key Features:**
* Full service lifecycle management (install, uninstall, start, stop)
* Service configuration (name, description, startup type: Automatic/AutomaticDelayedStart/Manual/Disabled)
* Process priority adjustment (Idle to Real Time)
* CPU affinity
* Custom working directory and command-line parameters
* `stdout`/`stderr` redirection with log rotation options
* Health monitoring and automatic service recovery triggers
* Environment variables
* Service dependencies
* Pre-launch and post-launch hooks
* Pre-stop and post-stop hooks
* Export/Import service configurations
* Designed for scripting, CI/CD pipelines, and remote management
* Admin privilege detection and elevation support
**Note:**
The CLI is designed as a lightweight, script-friendly alternative to the WPF interface, focusing on automation and headless scenarios while sharing core service management logic with the GUI application.
### Servy.UI
Acts as the shared UI infrastructure and component library. It provides the "glue" that allows `Servy` (Desktop App) and `Servy.Manager` (Manager App) to share a consistent architecture, behavior, and visual style.
**Key Responsibilities:**
* **AppBootstrapper:** Orchestrates the application lifecycle, manages the `CancellationTokenSource` for application shutdown, and handles dependency injection (DI) registration for UI-specific services.
* **MVVM Infrastructure:** Provides the foundational implementations for the Model-View-ViewModel pattern, including `ViewModelBase`, `RelayCommand`, and the `AsyncCommand` with `IsBusy` signaling.
* **WPF Services:** Contains shared abstractions for UI-specific interactions that cannot live in the Core library, such as `IFileDialogService` (File/Save dialogs), `IMessageBoxService` (modal prompts), `IHelpService`, `ICursorService`, and `IUiDispatcher` (UI-thread marshalling).
* **Value Converters:** Hosts `InverseBooleanConverter`, the one converter both apps share. The Manager's display converters (CPU and RAM usage, localized status, startup type, PID and log level) live in `Servy.Manager/Converters`.
* **Design-Time Mocking:** Provides design-time data providers that allow XAML designers to visualize complex layouts without requiring a live connection to the Service Control Manager (SCM) or the SQLite database.
### Servy.Core (Core Library)
Shared library containing common functionality used across all projects.
**Key Responsibilities:**
* Implement common utilities and helper classes
* Define interfaces and contracts
**Core Components:**
* `ServiceManager` - Provides methods to install, uninstall, start, stop, restart, and update Windows services.
* `ServiceControllerWrapper` - Defines an abstraction for controlling and monitoring the status of a Windows service.
* `NativeMethods` - Provides a comprehensive collection of Win32 API definitions, structures, and constants for Windows Service management, process lifecycle control, and security rights.
* `WindowsServiceApi` - Provides an abstraction for invoking native Windows Service API functions.
* `Win32ErrorProvider` - Provides access to the last Win32 error code.
* `RotatingStreamWriter` - Writes text to a file with automatic log rotation based on file size or date.
* `SecureData` - Provides thread-safe authenticated encryption (Encrypt-then-MAC).
* `SecurityHelper` - Provides utility methods for managing filesystem security and Access Control Lists (ACLs).
* `ServiceValidationRules` - Provides centralized validation logic for service configurations across all Servy components.
* `PathSecurityGuard` - Provides a centralized static security gate used to evaluate, resolve, and sanitize filesystem paths.
* `ImportGuard` - Provides the shared import-side gate for configuration files: path security, size threshold, and content read from the validated stream.
### Servy.Infrastructure (Infrastructure layer)
The **Infrastructure Layer** is implemented in the `Servy.Infrastructure` project. It is responsible for all **data persistence and retrieval operations**.
#### Responsibilities
* **Database Access** - Interacts with Servy's SQLite database (`Servy.db` by default in `%ProgramData%\Servy\db\`).
* **Data Persistence** - Reads and writes service configurations (including each service's log paths and rotation settings) and the schema version. Service logs themselves are written to files and to the Windows Event Log, not to the database.
#### Dapper ORM
Servy.Infrastructure uses [Dapper](https://github.com/DapperLib/Dapper), a lightweight Object-Relational Mapper (ORM) for .NET, to map database rows to strongly-typed C# objects.
**Key benefits of using Dapper in Servy:**
* **Performance** - Minimal overhead compared to raw ADO.NET, making it suitable for high-frequency queries.
* **Simplicity** - Allows writing SQL directly for clarity and control.
* **Type Safety** - Automatically maps query results to Servy's DTOs and entity classes.
#### Usage Pattern
1. **Define SQL Queries** - Queries are written explicitly in the repository classes.
2. **Execute with Dapper** - `IDbConnection` is used with Dapper's extension methods such as `Query()` and `Execute()`.
3. **Return Mapped Objects** - Results are returned as DTOs to the Business Logic Layer (`Servy.Core`).
```cs
public virtual async Task GetByNameAsync(
string? name,
bool decrypt = true,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(name)) return null;
string sql = $"SELECT * FROM {SqlConstants.ServicesTableName} WHERE Name = @Name COLLATE UNICODE_NOCASE;";
var dto = await ResolveByNameAsync(sql, name, cancellationToken: cancellationToken);
if (decrypt) SafeDecrypt(dto);
return dto;
}
private Task ResolveByNameAsync(
string sql,
string name,
CancellationToken cancellationToken)
{
return ResolveWithLegacyFallbackAsync(
sql: sql,
queryExecutor: (executedSql, parameters) => _dapper.QuerySingleOrDefaultAsync(executedSql, parameters, cancellationToken: cancellationToken),
name: name,
fallbackEvaluationPredicate: result => EqualityComparer.Default.Equals(result, default),
cancellationToken: cancellationToken
);
}
private static async Task ResolveWithLegacyFallbackAsync(
string sql,
Func> queryExecutor,
string name,
Func fallbackEvaluationPredicate,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var result = await queryExecutor(sql, new { Name = name.Trim() });
// Legacy rows (Servy <= 8.3) stored Name with whitespace verbatim.
if (fallbackEvaluationPredicate(result) && name != name.Trim())
{
cancellationToken.ThrowIfCancellationRequested();
result = await queryExecutor(sql, new { Name = name });
}
return result;
}
```
**Note:** `Name` lookups query with the trimmed name first, then fall back to the verbatim name so legacy rows (Servy <= 8.3, stored untrimmed) are still found.
By keeping all persistence logic in `Servy.Infrastructure`, Servy maintains **separation of concerns**, ensuring that higher layers (Core, CLI, GUI) remain independent of the underlying storage mechanism.
### Servy.Service (Windows Service)
Windows Service executable that wraps and manages target processes.
**Key Responsibilities:**
* Act as Windows Service host
* Launch and manage target executables
* Handle process monitoring and restart logic
* Redirect `stdout`/`stderr` with rotation
* Apply process priority, CPU affinity and working directory settings
* Health checks and automatic service recovery
* Run pre-launch and post-launch hooks
* Run pre-stop and post-stop hooks
* Handle service lifecycle operations
* Prevent orphaned/zombie processes and ensure resource cleanup
**Service Lifecycle:**
1. **OnStart** - Load configuration (since v10.2, from the `Servy` service over the named pipe), start target process, register `SERVICE_ACCEPT_PRESHUTDOWN` with SCM.
2. **OnCustomCommand(SERVICE_CONTROL_PRESHUTDOWN)** - High-priority teardown for OS reboot/shutdown (extended SCM wait hint).
3. **OnShutdown** - Final shutdown notification (short OS-controlled window).
4. **OnStop** - Standard SCM stop.
For more information, check out the [Shutdown & Teardown](https://github.com/aelassas/servy/wiki/Shutdown-&-Teardown) documentation.
### Servy.Host (Servy Service)
Background Windows service, added in v10.2, registered as **`Servy`** (display name `Servy`) and running as `LocalSystem`. It is the only Servy process that opens `Servy.db` and the encryption key on behalf of the service wrappers, which talk to it over a local named pipe, `\\.\pipe\SERVY_HOST_IPC_PIPE`. The .NET Framework 4.8 build names its executable `Servy.Host.Net48.exe`.
**Key Responsibilities:**
* **Data Access on Behalf of the Wrappers:** At construction it loads `appsettings.host.json`, applies the SQLite CVE-2025-6965 mitigation check, and opens the data stack (`AppDbContext`, `ProtectedKeyProvider`, `SecureData`, `ServiceRepository`). Opening it runs the database migrations, including the import of the legacy `recovery\` restart counters, before any wrapper can ask for them.
* **Named Pipe Listener:** `OnStart` builds the pipe's DACL, then starts several listener instances in the background, so services starting together at boot never queue behind one connection. Each connection carries one request, has a bounded time to complete, and is closed after the answer.
* **Request Handling:** Serves four per-service actions: `GetByName` (the service's decrypted configuration, with the account password removed), `UpdateRuntimeState` (PID, active `stdout`/`stderr` paths, previous stop timeout), `GetRestartAttempts` and `UpdateRestartAttempts`. The configuration itself is never written through the pipe.
* **Caller Authorization:** A request about a service is answered only when the caller is an administrator (or `LocalSystem`), or when the caller's process ID (`GetNamedPipeClientProcessId`) is the process ID the Service Control Manager reports for that service. Requests from another computer are refused.
* **Pipe Access Control:** The pipe instances are created with `PIPE_REJECT_REMOTE_CLIENTS` (`LocalPipeServer`) and a protected DACL (`ServyHostPipeSecurity`): Full Control for `SYSTEM` and `Administrators`, and Read/Write only for the accounts the installed services run under. An administrator rebuilds the DACL with the `RefreshPipeAccess` action, which `ServiceManager` sends whenever a service is installed, moved to another account, uninstalled or removed, so the change applies without restarting the `Servy` service.
**Server Verification (client side):** Before writing a request, `NamedPipesService` checks through `ServyHostServerVerifier` that the server end of the pipe is owned by the process the Service Control Manager reports for the `Servy` service, so another process cannot squat the pipe name and serve a forged configuration.
**Installation:** The Inno Setup installer does not register the service. The Desktop App, Servy Manager and `servy-cli` extract `Servy.Host.exe` into `%ProgramData%\Servy` and call `ServyHostInstaller.EnsureInstalledAndRunningAsync`, which creates the service when it is missing (Automatic start, `LocalSystem`), points it at their own executable when it runs another one, and starts it. `ServyHostInstaller.EnsureServicesDependOnHostAsync` adds the dependency on `Servy` to services installed by an older version, and `ServiceManager` adds it to every service it installs. The name `Servy` is reserved and cannot be used for a user service. The uninstaller removes the `Servy` service only when no Servy-managed service remains.
**Logging:** The host writes `Servy.Host.log` to `%ProgramData%\Servy\logs\`. Each wrapper writes `Servy.Service.log` (and its restarter `Servy.Restarter.log`) to its own folder, `logs\services\\`.
For the trust model this enforces, see [The Servy Trust Boundary](https://github.com/aelassas/servy/wiki/Security#the-servy-trust-boundary).
### Servy.Restarter (Restart Manager)
Out-of-process utility component dedicated to orchestrating service restarts. A running Windows service cannot safely manage its own stop-and-start cycle through the Service Control Manager (SCM), so `ServiceHelper.RestartService` launches `Servy.Restarter.exe` as an independent process to handle the lifecycle transition cleanly without deadlocking.
**Key Responsibilities:**
* **Out-of-Process Execution:** Takes the target service name and optional log directory override via command-line arguments (`args[0]` and `args[1]`), decoupling the restart sequence from the stopping host process.
* **SCM Lifecycle Orchestration:** Drives the SCM through Servy.Core's `IServiceControllerWrapper` (the `ServiceControllerWrapper` adapter over `System.ServiceProcess.ServiceController`), behind the `IServiceRestarter` interface, to issue stop commands, wait for complete service teardown, and execute a fresh start.
* **Bootstrapping & Mitigation Controls:** Initializes an isolated event-log source, loads `appsettings.restarter.json` (configuring properties like `RestartTimeoutSeconds`), applies the SQLite CVE-2025-6965 mitigation check, and sets up dedicated service-scoped logging.
* **Exit Code Signaling:** Returns a non-zero exit code to report failure back to `ServiceHelper` if the service fails to transition within the configured timeout threshold.
* **Bounded Lifespan:** Operates under a strict execution window - the host process force-kills the restarter if the orchestration exceeds 240 seconds.
## Integration Flow
```mermaid
sequenceDiagram
participant User
participant WPF as Servy WPF App
participant Core as Servy.Core
participant SCM as Windows SCM
participant Host as Servy.Host
participant Service as Servy.Service
participant Target as Target Process
participant Restarter as Servy.Restarter
WPF->>SCM: Install and start the Servy service if needed
SCM->>Host: OnStart()
Host->>Host: Build pipe DACL and start listeners
User->>WPF: Configure service
WPF->>Core: Validate configuration
Core->>WPF: Validation result
WPF->>SCM: Install service (depends on Servy)
SCM->>Service: Create service entry
WPF->>Host: RefreshPipeAccess (custom account)
WPF->>SCM: Start service
SCM->>Service: OnStart()
Service->>Host: GetByName over SERVY_HOST_IPC_PIPE
Host->>SCM: Process ID of the service
Host->>Host: Authorize caller, read Servy.db
Host-->>Service: Own configuration, without the password
Service->>Target: Start process
Service->>Host: UpdateRuntimeState (PID, log paths)
Service->>Service: Begin monitoring
Service->>Service: Health check
Note over Target: Process runs
Service->>Service: Failure detected
Service->>Host: GetRestartAttempts / UpdateRestartAttempts
Service->>Target: Restart process
Service->>Restarter: Restart service
```
Since v10.2, every exchange between `Servy.Service` and the database goes through `Servy.Host`. The wrapper never opens `Servy.db` or the encryption key, and the host answers it only about its own service.
## Design Patterns
The Servy architecture makes use of several design patterns.
Servy uses **MVVM** to separate the UI (View) from the business logic (Model) through ViewModels. This pattern is used heavily in both the Servy UI and the Servy Manager projects, especially for data binding and command handling.
The **Factory Method** pattern appears in many parts of the system. It is used to create instances of interfaces such as `IServiceControllerWrapper`, `IProcessWrapper`, `IStreamWriter`, `ITimer`, and the various Dapper-based repository objects. This helps keep client code decoupled from the actual implementations.
The **Adapter** pattern is used in multiple places to wrap system classes or internal utilities. For example, `ServiceControllerWrapper` adapts `System.ServiceProcess.ServiceController` to `IServiceControllerWrapper`. `RotatingStreamWriterAdapter` wraps `Servy.Core.IO.RotatingStreamWriter` behind the `IStreamWriter` interface. `TimerAdapter` adapts `System.Timers.Timer` to `ITimer`. The Dapper repositories also act as adapters by mapping raw SQL queries to strongly-typed DTOs.
The **Strategy** pattern is used throughout the `Service` class. It depends on interfaces like `IServyLogger`, `IProcessFactory`, `IStreamWriterFactory`, `ITimerFactory`, and `IPathValidator`, each representing a different strategy for logging, process creation, stream writing, timing, or path validation. These strategies can be swapped at runtime. The Dapper repositories also use different query strategies depending on the data operation being performed.
The architecture applies the **Observer** pattern as well. The `IProcessWrapper` interface defines events such as `OutputDataReceived`, `ErrorDataReceived`, and `Exited`, allowing other components to observe and react to changes in process state.
Servy employs **Dependency Injection** across many parts of the codebase. Constructors for classes like `ServiceManager`, `ServiceCommands`, `Service`, and repository classes take interfaces as parameters, promoting loose coupling and making the system easier to test. This approach is used across the `Servy.Core`, `Servy.Service`, `Servy.Host`, `Servy.Infrastructure`, and `Servy` projects. In `Servy.Host`, the machine-touching startup calls (event source, configuration, SQLite version check, data stack, Service Control Manager API, pipe caller identification) sit behind the `IServiceBootstrapEnvironment` seam, so the host's constructor can be tested without a real machine setup.
Finally, the **Repository** pattern provides a clean abstraction over the SQLite database. Dapper queries and commands are wrapped inside repository classes, so the Core and Host layers operate on strongly-typed objects without needing to know any SQL details.
Starting from v10.2, the service wrapper and the host follow a **Client-Server** model over a local named pipe. `INamedPipesService` acts as a **Proxy** for the data the wrapper needs: `GetByName`, `UpdateRuntimeState`, `GetRestartAttemptsAsync` and `UpdateRestartAttemptsAsync` mirror repository operations, but `NamedPipesService` serializes each call into an `IpcRequestDto`, sends it to the `Servy` service and returns the `IpcResponseDto` it gets back. On the server side, `Servy.Host` dispatches each request by its action name (a **Command**-style message), and only after an authorization check, so the access policy lives in one place (`IsAuthorized`) rather than in every caller.
## Tests
Servy's stability and reliability are ensured through a comprehensive suite of unit and integration tests. These test projects prevent regressions and provide the confidence needed to introduce new features or refactor existing logic.
| Project | Description |
| --- | --- |
| **Servy.UnitTests** | Tests for the main user interface used to configure and manage services. |
| **Servy.Manager.UnitTests** | Tests for the interface that manages and monitors installed services. |
| **Servy.UI.UnitTests** | Tests for shared UI components, helper services, and WPF utilities. |
| **Servy.CLI.UnitTests** | Tests for the command-line interface used for service automation and scripting. |
| **Servy.Core.UnitTests** | Tests for the shared logic, utilities, and data models used across the whole project. |
| **Servy.Infrastructure.UnitTests** | Tests for data access, persistence, and SQLite database integrations. |
| **Servy.Service.UnitTests** | Tests for the Windows service executable that wraps and runs target applications. |
| **Servy.Restarter.UnitTests** | Tests for the utility responsible for performing service restarts. |
| **Servy.Host.UnitTests** | Tests for the `Servy` service: request handling, caller authorization, pipe DACL rebuilds, and the bootstrap seam. |
| **Servy.Core.IntegrationTests** | Integration tests focusing on the interaction between core logic and external system dependencies. |
| **Servy.Infrastructure.IntegrationTests** | Integration tests for data persistence against a real SQLite database (schema initialization and Dapper execution). |
| **Servy.Service.IntegrationTests** | Integration tests for the full service lifecycle and process management behaviors. |
| **Servy.Host.IntegrationTests** | Integration tests that run the `Servy` service's named pipe listener for real (production pipe instances with the host's DACL, a SQLite repository, and the production `NamedPipesService` client); skipped when the run is not elevated. |
| **Servy.UI.IntegrationTests** | Integration tests for UI workflow orchestration and navigation logic. |
| **Servy.CLI.IntegrationTests** | Integration tests for the command-line interface against real CLI process execution. |
| **Servy.Testing** | A shared utility project containing common test utilities used by all other test projects. |
### Continuous Integration and Coverage
Every push to `main` triggers the automated test workflow (`test.yml`) via GitHub Actions across an x64 and ARM64 matrix, alongside a manual `workflow_dispatch` entry point. Pull requests are gated by `build.yml` (compilation) and `security.yml` (vulnerable-package scan); the full test suite runs once the change lands on `main`. Code coverage is measured across the solution's production projects - covering core libraries, infrastructure modules, UI components, and service wrappers - while excluding dedicated test suites (`-*.UnitTests;-*.IntegrationTests;-Servy.Testing`).
The workflow integrates with Codecov and Coveralls to provide a historical view of test health and coverage trends based on the primary x64 execution matrix node as the codebase evolves. This CI/CD pipeline ensures that merged changes are continuously validated against the project's quality baseline, maintaining the stability and reliability expected of the platform.
## Contributing
This architecture documentation is part of the open-source Servy project. Contributions to improve the documentation or codebase are welcome through GitHub issues and pull requests.
For more information about using Servy, see the main [README](https://github.com/aelassas/servy/blob/main/README.md) file.
---
# Document: Comparison with Alternatives
> Source: https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives#introduction)
1. [The Contenders](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives#the-contenders)
1. [Why the Wrapper Matters](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives#why-the-wrapper-matters)
1. [The Comparison: Feature Breakdown](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives#the-comparison-feature-breakdown)
1. [The Servy Advantage](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives#the-servy-advantage)
1. [Conclusion](https://github.com/aelassas/servy/wiki/Comparison-with-Alternatives#conclusion)
## Introduction
If you have worked in the Windows ecosystem for a while, you know the situation: you have a high-performance Node.js, Python, Java, or Go application that needs to run as a background service. Most developers turn to NSSM or WinSW to get the job done.
These tools have stood the test of time, but their age shows. They lack security, modern monitoring, robust process handling, and flexible automation.
## The Contenders
* **NSSM (Non-Sucking Service Manager):** The "classic." It's incredibly lightweight but hasn't seen a stable update in over a decade. It is not secure for production environments: it keeps the arguments and environment variables of every service in plaintext under `HKLM\SYSTEM\CurrentControlSet\Services\\Parameters`, where any local user can read them. It also lacks modern observability.
* **WinSW (Windows Service Wrapper):** A staple for Jenkins users. It's powerful and XML-driven, but it's currently in maintenance limbo and lacks a graphical interface for quick troubleshooting.
* **Servy:** A secure, actively maintained, open-source alternative. It features a lightweight Desktop UI alongside a robust CLI and PowerShell module for seamless CI/CD automation.
## Why the Wrapper Matters
A service wrapper is not just a starter script. In production, it is the heartbeat of your application. If your wrapper cannot handle graceful signal propagation or proper process cleanup, you risk leaked resources and orphaned processes that manual reboots cannot always fix.
## The Comparison: Feature Breakdown
The table below shows a feature comparison between Servy, NSSM, and WinSW.
Servy provides enterprise-grade per-service isolation between custom accounts, authenticated encryption (DPAPI, HKDF, AES-256 + HMAC-SHA256), and automated vault ACL hardening. All releases feature signed binaries, SBOMs, and continuous vulnerability scanning. See [Security](https://github.com/aelassas/servy/wiki/Security) and [Architecture](https://github.com/aelassas/servy/wiki/Architecture) for details.
**Legend:** ✅ Full Support · ⚪ Partial / Basic Support · ❌ Not Supported
| Feature | Servy | NSSM | WinSW |
| --- | --- | --- | --- |
| GUI Management | ✅ Real-time monitoring and config | ⚪ Basic installer GUI | ❌ No GUI |
| CLI / Automation | ✅ PowerShell, CLI and CI/CD support | ✅ CLI | ✅ CLI only |
| Per-Service Security Isolation | ✅ Per-service named-pipe boundary (v10.2+) | ❌ Shared trust boundary | ❌ Shared trust boundary |
| Encrypted Credentials & Arguments | ✅ DPAPI, HKDF, AES-256 + HMAC-SHA256 | ❌ Arguments and environment variables in plaintext registry, readable by any local user | ❌ Plaintext password and environment variables in XML |
| Automated Directory & Binary Hardening | ✅ Automatic least-privilege ACLs | ❌ Inherits OS defaults | ❌ Manual ACLs required |
| Isolated Per-Service Log Folders | ✅ Isolated subfolders per service | ❌ Shared log paths | ❌ Shared log paths |
| Supply Chain Verification | ✅ Signed, SBOM & VirusTotal scanned | ❌ Unsigned / No SBOM | ⚪ Signed binaries |
| Pre/Post Launch Hooks | ✅ Advanced with retries and timeouts | ❌ No support | ⚪ Basic |
| Pre/Post Stop Hooks | ✅ Advanced pulsed cleanup | ❌ No support | ⚪ Basic |
| Performance Tracking | ✅ Real-time CPU and RAM graphs | ❌ No support | ❌ No support |
| CPU Affinity | ✅ Advanced | ⚪ Basic | ❌ No support |
| Service Accounts | ✅ Local System, local, domain, and gMSA accounts | ⚪ Limited | ⚪ Limited |
| Logging and Rotation | ✅ Advanced size and date rotation | ⚪ Basic | ⚪ Basic |
| Live Console Preview | ✅ Real-time stdout and stderr streaming | ❌ No support | ❌ No support |
| Dependencies Preview | ✅ Advanced with statuses | ❌ No support | ❌ No support |
| Process Tree Safety | ✅ Graceful Stop, Ctrl+C, Force Kill | ⚪ Basic | ⚪ Basic |
| Zombie Prevention | ✅ Recursive tree cleanup | ⚪ Basic | ❌ No support |
| Health and Recovery | ✅ Built-in monitoring and auto-restart | ⚪ Restart on exit | ⚪ Limited |
| Heartbeat Ping URL | ✅ Out-of-band pings with `/start` & `/fail` flags | ❌ No support | ❌ No support |
| Notifications | ✅ Windows OS alerts and Email, plus Slack, Teams, WhatsApp and more via Heartbeat URL providers | ❌ No support | ❌ No support |
| Config Portability | ✅ Export and Import (XML/JSON) | ❌ No support | ❌ No support |
| Actively Maintained | ✅ Yes | ❌ No (Inactive) | ❌ No (Inactive) |
## The Servy Advantage
Servy was designed to address the limitations of legacy wrappers and provide a robust, reliable solution for modern services. Beyond basic service management, it supports scenarios that NSSM and WinSW cannot handle efficiently:
1. **Observability and Diagnostics:** Servy provides live CPU and RAM graphs, while streaming standard output and error logs in real time. NSSM and WinSW require external monitoring tools to achieve this level of visibility.
2. **Advanced Lifecycle Hooks:** Pre-launch, post-launch, pre-stop, and post-stop hooks with retry logic, timeouts, and failure handling allow complex orchestration. Legacy wrappers either lack hooks or offer only minimal functionality.
3. **Process Tree Management:** Servy safely propagates stop signals and kills descendant processes recursively, preventing orphaned processes from consuming system resources. This ensures clean teardowns even in nested process trees.
4. **Self-Healing and Notifications:** Built-in health checks and automatic recovery (including service auto-restart) maintain uptime. Servy triggers real-time alerts via Windows notifications or email when failures occur, which NSSM and WinSW do not support. Point the Heartbeat URL at a provider such as [healthchecks.io](https://healthchecks.io/) to route alerts to Slack, Microsoft Teams, Discord, Telegram, WhatsApp, Signal, Google Chat and more (see [Service Event Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications)).
5. **Flexible Accounts and Security:** Servy runs services under Local System, local, domain, or gMSA accounts, and each account reaches only its own configuration and logs. Secrets are encrypted at rest, and the vault ACLs are hardened automatically. Legacy wrappers are limited in account support and security features.
6. **Portable Configurations:** Servy supports exporting and importing configurations in XML and JSON formats, enabling easy replication, migration, and backup of service setups.
7. **Cross-Version Compatibility:** Servy provides builds for modern and legacy Windows systems, allowing organizations with mixed environments to standardize on a single wrapper.
In short, Servy solves the pain points that legacy wrappers leave unresolved and empowers teams with visibility, reliability, and control.
## Conclusion
While NSSM and WinSW served the community for years, they belong to a different era of system administration. For teams that require security, real-time monitoring, process safety, and deep integration with modern CI/CD pipelines, Servy is the clear successor. It's free, open-source, and fully supported on Windows 10 (1809+), Windows 11 and Windows Server 2016+, with dedicated builds available for legacy releases like Windows 7 SP1+ and Windows Server 2008 R2+.
---
# Document: Building from Source
> Source: https://github.com/aelassas/servy/wiki/Building-from-Source
## Table of Contents
1. [Source Code](https://github.com/aelassas/servy/wiki/Building-from-Source#source-code)
1. [Build Instructions](https://github.com/aelassas/servy/wiki/Building-from-Source#build-instructions)
1. [Requirements](https://github.com/aelassas/servy/wiki/Building-from-Source#requirements)
1. [Steps](https://github.com/aelassas/servy/wiki/Building-from-Source#steps)
1. [Running the Applications](https://github.com/aelassas/servy/wiki/Building-from-Source#running-the-applications)
1. [Running the Tests](https://github.com/aelassas/servy/wiki/Building-from-Source#running-the-tests)
## Source Code
Servy is available in two versions, each maintained in a separate branch:
* **.NET 10.0+ version:** located on the [`main`](https://github.com/aelassas/servy/tree/main) branch
* **.NET Framework 4.8 version:** located on the [`net48`](https://github.com/aelassas/servy/tree/net48) branch
## Build Instructions
### Requirements
To build and run either version locally, you will need:
* Visual Studio 2026 or later
* .NET SDK matching the target version:
* [.NET 10.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) for the `main` branch. The exact SDK build required is specified in `global.json` at the repository root (`sdk.version`). Because `rollForward` is set to `disable`, you must install that precise SDK version from the [.NET release archives](https://dotnet.microsoft.com/en-us/download/dotnet/10.0).
* .NET Framework 4.8 Developer Pack (included with Visual Studio) for the `net48` branch. (Not governed by `global.json`).
> [!NOTE]
> You will need to run Visual Studio as **Administrator** to be able to manage Windows Services.
### Steps
1. Clone the repository:
```bash
git clone https://github.com/aelassas/servy.git
```
2. Checkout the desired branch:
```bash
# For .NET 10.0+ version
git checkout main
# For .NET Framework 4.8 version
git checkout net48
```
3. Open the `Servy.sln` file in Visual Studio 2026
4. Restore NuGet packages
5. Build the solution
> [!IMPORTANT]
> On `main`, the executable projects (`Servy`, `Servy.CLI`, `Servy.Host`, `Servy.Manager`, `Servy.Restarter`, `Servy.Service`) declare `win-x64;win-arm64`; the shared libraries (`Servy.Core`, `Servy.Infrastructure`, `Servy.UI`) do not pin a RID. Select the desired RID (e.g. `win-x64`) when publishing.
>
> On `net48`, **x64** is mandatory because `SourceGear.sqlite3` does not provide an **Any CPU** build.
### Running the Applications
* To run the desktop app: set the startup project to **Servy** and run
* To run the Manager app: set the startup project to **Servy.Manager** and run
* To run the CLI app: set the startup project to **Servy.CLI** and run
### Running the Tests
On `main`, the test runner uses Microsoft.Testing.Platform (MTP) as configured in `global.json`.
* To run the complete test suite (unit tests and integration tests) with code coverage:
```powershell
.\tests\test.ps1
```
* To run unit tests via standard .NET CLI:
```powershell
dotnet test
```
* Alternatively, run individual test executables directly:
```powershell
dotnet run --project tests\Servy.Core.UnitTests\Servy.Core.UnitTests.csproj
```
---
# Document: Troubleshooting
> Source: https://github.com/aelassas/servy/wiki/Troubleshooting
## Table of Contents
1. [Introduction](https://github.com/aelassas/servy/wiki/Troubleshooting#introduction)
1. [Blank Screen on Remote Management Tools](https://github.com/aelassas/servy/wiki/Troubleshooting#blank-screen-on-remote-management-tools)
1. [Service Won't Start](https://github.com/aelassas/servy/wiki/Troubleshooting#service-wont-start)
1. ["Access Denied" Errors](https://github.com/aelassas/servy/wiki/Troubleshooting#access-denied-errors)
1. [Logs Not Appearing](https://github.com/aelassas/servy/wiki/Troubleshooting#logs-not-appearing)
1. [Health Checks Failing Too Soon](https://github.com/aelassas/servy/wiki/Troubleshooting#health-checks-failing-too-soon)
1. [Process Starts but Exits Immediately](https://github.com/aelassas/servy/wiki/Troubleshooting#process-starts-but-exits-immediately)
1. [Service Installed but Not Visible in Windows Services](https://github.com/aelassas/servy/wiki/Troubleshooting#service-installed-but-not-visible-in-windows-services)
1. [Service Won't Uninstall](https://github.com/aelassas/servy/wiki/Troubleshooting#service-wont-uninstall)
1. [High CPU or Memory Usage](https://github.com/aelassas/servy/wiki/Troubleshooting#high-cpu-or-memory-usage)
1. [Changes Not Applying After Updating Configuration](https://github.com/aelassas/servy/wiki/Troubleshooting#changes-not-applying-after-updating-configuration)
1. [Service Using a Domain Account Does Not Start After Reboot](https://github.com/aelassas/servy/wiki/Troubleshooting#service-using-a-domain-account-does-not-start-after-reboot)
1. [Servy "Check for Updates..." Not Working on Windows 7](https://github.com/aelassas/servy/wiki/Troubleshooting#servy-check-for-updates-not-working-on-windows-7)
## Introduction
This page lists the most common issues people run into when using Servy, along with practical fixes. Start here when something is not working as expected; the solution may already be covered below.
Most problems fall into a few categories: permissions, startup configuration, health checks, log capture, or Windows policy settings. Use the sections below to narrow the issue quickly before making changes.
## Blank Screen on Remote Management Tools
If you get a blank screen on remote management tools (like MeshCentral, TeamViewer, or AnyDesk), run the desktop app from an admin command prompt with the following command:
```cmd
Servy.exe --force-sr
```
And the Manager app with the following command:
```cmd
Servy.Manager.exe --force-sr
```
## Service Won't Start
This is one of the most common problems when wrapping an app as a Windows service.
Possible causes & fixes:
* **Access Denied / Permissions Issue:** If the service runs under an account other than **LocalSystem**, check the Servy log: since v10.2, Servy hardens the vault for the account automatically when the service is installed, which gives the account access to the database, logs and recovery folders and to the files it reads, and logs why if that did not succeed. Fix the cause and install the service again to re-apply it. On Servy 10.1 and earlier, run `Set-ServyExePermissions.ps1 -TargetAccount "domain\user"` from an elevated PowerShell session (see [Servy 10.1 and Earlier](https://github.com/aelassas/servy/wiki/Security#servy-101-and-earlier)). See [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening).
* **Missing working directory:**
Many apps (Node.js, Python, .NET, Java…) rely on relative paths.
*Real case:* A user had a Node app that read `./config.json`. It worked manually but failed as a service because Servy launched it from `C:\Program Files\nodejs\`. Setting the working directory fixed it.
* **File not found or missing dependencies:**
Ensure all DLLs, configs or runtimes exist where your app expects them.
*Example:* .NET apps missing runtime dependencies will silently fail.
* **App crashes instantly:**
Open Servy Manager: Logs, check the rotating log files, or check `%ProgramData%\Servy\logs\services\\Servy.Service.log`. You'll usually see the exception there.
* **App crashes with Console/UI handle errors:**
If your app tries to visually update the command prompt (like clearing the screen or moving the cursor), it will crash when running as a background service since services don't have visible windows. To fix this without changing your code, enable the `--enableConsoleUI` option in the service configuration when installing your service.
* **Environment variables missing:**
If your app relies on variables like `PATH`, `NODE_ENV`, `ASPNETCORE_ENVIRONMENT`, etc., define them in the service config.
## "Access Denied" Errors
These issues typically happen when Windows blocks something.
Possible causes & fixes:
* **Servy not running as Administrator:** Some operations (installing a service, binding ports <1024, writing to protected locations) require elevation.
* **Logs or working directory located in a protected folder:**
*Real case:* Writing logs to `C:\Program Files\MyApp\logs` caused access issues.
Fix: Use a writable path like `C:\ServyData\logs` or adjust permissions.
* **The wrapped app itself needs admin rights:** Example: Apps that manage firewall rules or interact with drivers.
## Logs Not Appearing
If your `stdout`/`stderr` logs are empty while Servy is running, your app's output isn't being captured.
Possible causes & fixes:
* **Log directory read-only or blocked by antivirus:**
*Real case:* Windows Defender blocked file writes for an EXE that looked "suspicious." Adding an exclusion fixed it.
* **Unbuffered stdout/stderr output:**
Some apps buffer logs until the process ends.
Fix: Enable unbuffered output or add explicit flush calls.
* **App logs directly to its own file:** Check your internal application logging settings to ensure output is written to `stdout`/`stderr`.
* **Console UI mode is enabled (`--enableConsoleUI`):** `stdout`/`stderr` redirection is intentionally disabled when Console UI is on. Disable `--enableConsoleUI` if you need Servy to capture the app's output to the configured log files.
## Health Checks Failing Too Soon
This happens when Servy thinks the app is unhealthy even though it's slow to start or performs heavy initialization.
Possible causes & fixes:
* **Wrapped process exiting on startup:** Servy's heartbeat checks whether the wrapped process is still running, not an HTTP health endpoint. If the heartbeat trips, it almost always means the process crashed or exited. Check `stdout`/`stderr` logs for the actual error.
* **Startup time exceeds initialization window:** Default is `30s × 3 = 90s` before recovery fires. If your app needs longer, increase `HeartbeatInterval` or `MaxFailedChecks`.
* **Slow warm-up or initialization:** If your app fails to come up within the heartbeat budget because of a heavy initialization step (loading ML models, opening a SQL connection pool, etc.), move that step to a Pre-Launch hook so it completes BEFORE the main process is considered started.
## Process Starts but Exits Immediately
Possible causes & fixes:
* **App expects user interaction:** GUI apps or apps that open a console window will quit instantly when run headless.
* **Missing dependencies or runtime errors:** Check logs for missing DLLs, config errors, or runtime mismatches.
* **Bad command-line arguments:** Incorrect flags or environment-specific paths can cause instant termination.
## Service Installed but Not Visible in Windows Services
Possible causes & fixes:
* **Elevation context mismatch:** Installing a service as admin in one user session, then checking Services under a restricted user showed nothing.
* **Outdated view:** Refresh the list or reopen `services.msc`.
* **Name collision:** Ensure the service name doesn't conflict with an existing Windows service.
## Service Won't Uninstall
Possible causes & fixes:
* **Active process locks:** Another tool (Task Manager, monitoring agent, antivirus) might still be holding a handle to the process. Stop the service manually first.
* **Insufficient privileges:** Run Servy Manager in administrator mode.
* **Service marked for deletion:** Check if Windows marked the service for deletion; if so, a reboot is required before it can be reinstalled.
## High CPU or Memory Usage
Possible causes & fixes:
* **Unexpected background behavior:** Your app might behave differently as a background service (e.g., relative paths may cause it to load or write huge data files).
* **Missing working directory setting:** Use the correct working directory to prevent accidental file creation in system folders.
## Changes Not Applying After Updating Configuration
Possible causes & fixes:
* **Pending service restart:** Restarting the Windows service is required for config changes to take effect.
* **Cached application configs:** Some apps read config only at startup.
* **Syntax or validation errors:** Ensure there are no trailing commas, broken JSON, or duplicated fields in your config file.
## Service Using a Domain Account Does Not Start After Reboot
If a service configured to run under a domain account starts successfully after installation but fails to start following a server reboot, the most common cause is that the account has lost the "Log on as a service" right.
During service installation, Servy grants this right locally to the specified account. However, if a Domain Group Policy defines the "Log on as a service" setting, it overrides the local configuration during startup or Group Policy refresh. After the reboot, the domain policy replaces the local assignment, the account no longer has the required permission, and Windows prevents the service from starting.
To resolve this issue, add the service account to the "Log on as a service" policy at the domain level. Open the Group Policy Management Console by running `gpmc.msc`, edit the appropriate Group Policy Object that applies to the target server, then navigate to Computer Configuration, Windows Settings, Security Settings, Local Policies, User Rights Assignment, and select Log on as a service. Add the domain service account to the list and apply the changes. Run `gpupdate /force` on the server or wait for the next policy refresh cycle, and reboot if necessary.
If the server is not joined to a domain, you can configure the setting locally using `secpol.msc` and navigating to the same User Rights Assignment location.
When this issue occurs, Windows records the failure in Event Viewer (`eventvwr.msc`) under Windows Logs, System, with the source set to Service Control Manager. The log entry typically states that the service could not log on because the user does not have the requested logon type on the computer. This confirms that the issue is related to Windows security policy rather than Servy itself.
Also, check the Servy log for the automatic hardening of the domain account (v10.2+); it is what gives the account access to the database, logs and recovery folders under `%ProgramData%\Servy`. See [Executable Permission Hardening](https://github.com/aelassas/servy/wiki/Security#executable-permission-hardening).
## Servy "Check for Updates..." Not Working on Windows 7
Windows 7 (especially without updates) does not enable TLS 1.2 by default. That's the root cause.
To fix this, create a file named `enable-tls12-win7.reg` and run it as Administrator:
```text
Windows Registry Editor Version 5.00
; WinHTTP TLS 1.2
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp]
"DefaultSecureProtocols"=dword:00000a00
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp]
"DefaultSecureProtocols"=dword:00000a00
; SCHANNEL TLS 1.2 client
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
"Enabled"=dword:00000001
"DisabledByDefault"=dword:00000000
; .NET strong crypto
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001
"SystemDefaultTlsVersions"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001
"SystemDefaultTlsVersions"=dword:00000001
```
Then reboot your machine.
---
# Document: FAQ
> Source: https://github.com/aelassas/servy/wiki/FAQ
## Table of Contents
### [Introduction](https://github.com/aelassas/servy/wiki/FAQ#introduction-1)
### [General Overview](https://github.com/aelassas/servy/wiki/FAQ#general-overview-1)
1. [What's the use case for Servy?](https://github.com/aelassas/servy/wiki/FAQ#whats-the-use-case-for-servy)
1. [Should I run an app as a service or a tray app?](https://github.com/aelassas/servy/wiki/FAQ#should-i-run-an-app-as-a-service-or-a-tray-app)
1. [Things that should be a service should already be a service?](https://github.com/aelassas/servy/wiki/FAQ#things-that-should-be-a-service-should-already-be-a-service)
1. [Why choose a Windows service instead of building outside the Windows ecosystem?](https://github.com/aelassas/servy/wiki/FAQ#why-choose-a-windows-service-instead-of-building-outside-the-windows-ecosystem)
### [Comparisons](https://github.com/aelassas/servy/wiki/FAQ#comparisons-1)
1. [Is Servy the same as Windows Task Scheduler?](https://github.com/aelassas/servy/wiki/FAQ#is-servy-the-same-as-windows-task-scheduler)
1. [How does Servy compare to sc.exe?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-compare-to-scexe)
1. [How does Servy compare to NSSM?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-compare-to-nssm)
1. [How does Servy compare to WinSW?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-compare-to-winsw)
1. [How does Servy compare to SrvAny?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-compare-to-srvany)
1. [How does Servy compare to Docker Desktop?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-compare-to-docker-desktop)
1. [How does Servy compare to Tanuki's wrapper and is it compatible with Waratek RASP?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-compare-to-tanukis-wrapper-and-is-it-compatible-with-waratek-rasp)
1. [Can Servy replace PM2 for Node.js on Windows?](https://github.com/aelassas/servy/wiki/FAQ#can-servy-replace-pm2-for-nodejs-on-windows)
1. [Why not Microsoft.Extensions.Hosting.WindowsServices?](https://github.com/aelassas/servy/wiki/FAQ#why-not-microsoftextensionshostingwindowsservices)
### [Technical Configuration](https://github.com/aelassas/servy/wiki/FAQ#technical-configuration-1)
1. [What types of applications can Servy run as a service?](https://github.com/aelassas/servy/wiki/FAQ#what-types-of-applications-can-servy-run-as-a-service)
1. [How do I run scripts like batch or PowerShell with Servy?](https://github.com/aelassas/servy/wiki/FAQ#how-do-i-run-scripts-like-batch-or-powershell-with-servy)
1. [Does Servy support both console apps and GUI apps?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-both-console-apps-and-gui-apps)
1. [Can I set a custom working directory?](https://github.com/aelassas/servy/wiki/FAQ#can-i-set-a-custom-working-directory)
1. [Does Servy support running services under custom user accounts?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-running-services-under-custom-user-accounts)
1. [How do I update a service configuration?](https://github.com/aelassas/servy/wiki/FAQ#how-do-i-update-a-service-configuration)
1. [Can I use Servy in automated deployments (CI/CD)?](https://github.com/aelassas/servy/wiki/FAQ#can-i-use-servy-in-automated-deployments-cicd)
### [Service Lifecycle & Hooks](https://github.com/aelassas/servy/wiki/FAQ#service-lifecycle--hooks-1)
1. [Does Servy support automatic restarts?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-automatic-restarts)
1. [What happens if the monitored process crashes?](https://github.com/aelassas/servy/wiki/FAQ#what-happens-if-the-monitored-process-crashes)
1. [Does Servy support running a script on service failure?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-running-a-script-on-service-failure)
1. [What is the pre-launch hook in Servy?](https://github.com/aelassas/servy/wiki/FAQ#what-is-the-pre-launch-hook-in-servy)
1. [What happens if the pre-launch hook fails?](https://github.com/aelassas/servy/wiki/FAQ#what-happens-if-the-pre-launch-hook-fails)
1. [Can I pass arguments and environment variables to the pre-launch process?](https://github.com/aelassas/servy/wiki/FAQ#can-i-pass-arguments-and-environment-variables-to-the-pre-launch-process)
1. [Is the pre-launch hook synchronous or asynchronous?](https://github.com/aelassas/servy/wiki/FAQ#is-the-pre-launch-hook-synchronous-or-asynchronous)
1. [What is the post-launch hook in Servy?](https://github.com/aelassas/servy/wiki/FAQ#what-is-the-post-launch-hook-in-servy)
1. [When is the post-launch process executed?](https://github.com/aelassas/servy/wiki/FAQ#when-is-the-post-launch-process-executed)
1. [What happens if the service stops while the post-launch process is waiting?](https://github.com/aelassas/servy/wiki/FAQ#what-happens-if-the-service-stops-while-the-post-launch-process-is-waiting)
1. [Can I pass arguments to the post-launch process?](https://github.com/aelassas/servy/wiki/FAQ#can-i-pass-arguments-to-the-post-launch-process)
1. [Can I use post-launch for long-running scripts?](https://github.com/aelassas/servy/wiki/FAQ#can-i-use-post-launch-for-long-running-scripts)
1. [What is the difference between a Pre-Stop and a Post-Stop hook?](https://github.com/aelassas/servy/wiki/FAQ#what-is-the-difference-between-a-pre-stop-and-a-post-stop-hook)
1. [If the timeout is 0, does the Pre-Stop hook still run?](https://github.com/aelassas/servy/wiki/FAQ#if-the-timeout-is-0-does-the-pre-stop-hook-still-run)
1. [What is the danger of a 0-second timeout on a Pre-Stop hook?](https://github.com/aelassas/servy/wiki/FAQ#what-is-the-danger-of-a-0-second-timeout-on-a-pre-stop-hook)
1. [Is the service recreated after config change?](https://github.com/aelassas/servy/wiki/FAQ#is-the-service-recreated-after-config-change)
### [Logging & Monitoring](https://github.com/aelassas/servy/wiki/FAQ#logging--monitoring-1)
1. [Can I capture stdout and stderr logs?](https://github.com/aelassas/servy/wiki/FAQ#can-i-capture-stdout-and-stderr-logs)
1. [Is there a way to view service logs in real time?](https://github.com/aelassas/servy/wiki/FAQ#is-there-a-way-to-view-service-logs-in-real-time)
1. [Can Servy send notifications or emails on service failures or restarts?](https://github.com/aelassas/servy/wiki/FAQ#can-servy-send-notifications-or-emails-on-service-failures-or-restarts)
1. [Does Servy provide CPU and RAM monitoring?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-provide-cpu-and-ram-monitoring)
1. [How do I monitor my service using a Heartbeat URL?](https://github.com/aelassas/servy/wiki/FAQ#how-do-i-monitor-my-service-using-a-heartbeat-url)
1. [How can I get failure alerts sent to Slack, Microsoft Teams, Phone Call, or WhatsApp?](https://github.com/aelassas/servy/wiki/FAQ#how-can-i-get-failure-alerts-sent-to-slack-microsoft-teams-phone-call-or-whatsapp)
### [Shutdown & Process Handling](https://github.com/aelassas/servy/wiki/FAQ#shutdown--process-handling-1)
1. [How does Servy stop the app?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-stop-the-app)
1. [How does Servy prevent zombie processes?](https://github.com/aelassas/servy/wiki/FAQ#how-does-servy-prevent-zombie-processes)
1. [Will Java shutdown hooks run when a Servy service running a Java app shuts down?](https://github.com/aelassas/servy/wiki/FAQ#will-java-shutdown-hooks-run-when-a-servy-service-running-a-java-app-shuts-down)
1. [Does Servy make requests for additional service shutdown time?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-make-requests-for-additional-service-shutdown-time)
1. [When exactly does the Pre-Shutdown signal occur?](https://github.com/aelassas/servy/wiki/FAQ#when-exactly-does-the-pre-shutdown-signal-occur)
1. [What happens if the OS shuts down or reboots?](https://github.com/aelassas/servy/wiki/FAQ#what-happens-if-the-os-shuts-down-or-reboots)
### [Environment & Compatibility](https://github.com/aelassas/servy/wiki/FAQ#environment--compatibility-1)
1. [Why do I need admin privileges?](https://github.com/aelassas/servy/wiki/FAQ#why-do-i-need-admin-privileges)
1. [Is Servy compatible with older Windows versions?](https://github.com/aelassas/servy/wiki/FAQ#is-servy-compatible-with-older-windows-versions)
1. [Does Servy work on offline servers?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-work-on-offline-servers)
1. [Can Servy be used as part of an installer without requiring extra dependencies?](https://github.com/aelassas/servy/wiki/FAQ#can-servy-be-used-as-part-of-an-installer-without-requiring-extra-dependencies)
1. [What happens if I uninstall Servy?](https://github.com/aelassas/servy/wiki/FAQ#what-happens-if-i-uninstall-servy)
### [Advanced & Specific Scenarios](https://github.com/aelassas/servy/wiki/FAQ#advanced--specific-scenarios-1)
1. [Does Servy support environment variables for the wrapped process?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-environment-variables-for-the-wrapped-process)
1. [Does Servy support service dependencies?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-service-dependencies)
1. [Does Servy support gMSA and AD log on?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-gmsa-and-ad-log-on)
1. [Why does Excel COM automation fail when my application runs as a Windows service?](https://github.com/aelassas/servy/wiki/FAQ#why-does-excel-com-automation-fail-when-my-application-runs-as-a-windows-service)
1. [Can Servy run OneDrive as a Service using Azure Login?](https://github.com/aelassas/servy/wiki/FAQ#can-servy-run-onedrive-as-a-service-using-azure-login)
1. [Does Servy support ClickOnce-like update behavior?](https://github.com/aelassas/servy/wiki/FAQ#does-servy-support-clickonce-like-update-behavior)
1. [Can Servy run Windows Update from a PowerShell script as a service?](https://github.com/aelassas/servy/wiki/FAQ#can-servy-run-windows-update-from-a-powershell-script-as-a-service)
1. [How and why should I use CPU affinity with Servy?](https://github.com/aelassas/servy/wiki/FAQ#how-and-why-should-i-use-cpu-affinity-with-servy)
## Introduction
Welcome to the Servy Frequently Asked Questions guide. Servy is a Windows service wrapper designed to run non-service applications, including those written in Node.js, Python, Go, Java, or .NET, as reliable and first-class Windows services.
Many background applications begin life as simple console processes. In production environments, however, they often require structured lifecycle management such as automatic restarts, graceful shutdowns, and continuous monitoring. Servy bridges the gap between portable application code and the Windows Service Control Manager (SCM) by providing a centralized way to manage logs, environment variables, process health, and recovery behavior without requiring changes to the application itself.
This FAQ covers topics ranging from initial setup and configuration to advanced lifecycle hooks and process tree management.
## General Overview
### What's the use case for Servy?
The main use case is running non-service apps as proper Windows services in a reliable and observable way.
This is useful when you have background apps like web servers, workers, schedulers, or long-running tools written in Node.js, Python, Go, or .NET. For example, an internal REST API, a background job processor, a file watcher that syncs data, a message queue consumer, a local build agent, a monitoring or metrics collector, or an automation tool that needs to run continuously in the background.
These types of applications usually need to start automatically on boot, restart if they crash or hang, run under specific local or domain accounts, expose logs for troubleshooting, and shut down cleanly during reboots or deployments without leaving orphaned processes behind.
Servy is intended for developers, IT administrators, and power users who want more control and visibility than basic service wrappers provide, especially around logging, lifecycle hooks, health checks, and monitoring.
### Should I run an app as a service or a tray app?
Tray apps work well when the app is tied to an interactive user session, but a Windows service addresses a different set of needs. Services are useful when an app must run independently of a logged-in user. They start at system boot and continue running even when no one is signed in, which is important for background workloads or machines accessed over remote sessions.
Services also integrate with SCM (Service Control Manager), which provides predictable startup and shutdown behavior, automatic restart on failure, and consistent handling during system updates or reboots. Tray apps often stop when a user logs out or a session disconnects, while services continue running uninterrupted. This makes them more reliable for long-running processes such as local APIs, background workers, schedulers, or monitoring agents.
Security is another important factor. Services can run under dedicated accounts with limited permissions, which is not always practical for user-session applications. In practice, tray apps make sense for user-facing tools that require interaction, while services are better suited for infrastructure-style workloads that need to operate consistently regardless of who is logged in.
### Things that should be a service should already be a service?
Ideally, anything that is intended to run long term on a server should be implemented as a proper Windows service from the start.
In practice, though, wrappers and service managers exist because real environments are not always ideal. A lot of software that ends up running on Windows Server was not originally written as a service. That includes legacy internal tools, third-party software where the source is not available, cross-platform apps that were designed to run as console processes, and vendor utilities that assume a Linux-style supervisor rather than the Windows Service Control Manager.
This is not limited to any one language. Node.js, Python, Java, Go, and some legacy apps often start life as console applications because that is the most portable and simplest execution model. Rewriting them as native Windows services is not always feasible or cost-effective, especially when the application already works reliably and only needs predictable startup, shutdown, and recovery behavior.
### Why choose a Windows service instead of building outside the Windows ecosystem?
Windows services are still relevant in enterprise, regulated, and on-prem setups where Windows remains the standard. A lot of production workloads are not cloud native and cannot be moved to containers or Linux.
Servy is not about promoting Windows over other platforms. It is about solving a real, recurring problem for teams that already run on Windows and need reliable background processes with proper lifecycle management, logging, and recovery. For those environments, a Windows service is still the correct and supported solution.
## Comparisons
### Is Servy the same as Windows Task Scheduler?
No. While it might be possible to recreate a similar setup using Task Scheduler, you would need to combine several different tools and scripts for process supervision, log rotation, and performance monitoring.
Servy provides a single, centralized platform to install, configure, manage, and monitor your Windows services in real time. Instead of juggling multiple tools, Servy handles process supervision, automatic restarts, live CPU/RAM usage tracking, stdout/stderr log streaming, and failure alerts out of the box.
### How does Servy compare to sc.exe?
`sc.exe` doesn't wrap any app as a service; it only manages binaries already specifically written to communicate with the Windows Service API. If you try to force a regular executable through it, the service will usually time out and fail because it doesn't know how to talk to the Service Control Manager (SCM). Servy acts as that necessary bridge, allowing you to run any app as a service without rewriting a single line of code.
Beyond staying alive, Servy replaces many tools with a single dashboard. Instead of jumping between Task Manager for RAM, Event Viewer for system errors, and raw text files for logs, you get live telemetry and searchable stdout/stderr in one view. It also handles complex lifecycle logic like running cleanup scripts, post-stop hooks, checking dependencies or pre-launch hooks, that `sc.exe` isn't built to handle.
### How does Servy compare to NSSM?
Servy and NSSM let you run any app as a native Windows service, but they solve related but slightly different problems.
Where Servy differs is visibility and day-to-day operations. With Servy you can see what a service is doing in real time, including CPU and RAM usage, live stdout and stderr output, dependency tree, and searchable logs, all from one place. This avoids jumping between Event Viewer, log files, and Task Manager when diagnosing issues.
Servy also treats service lifecycle as a first class concept. It allows running pre-launch, post-launch, pre-stop, and post-stop actions with proper logging, timeouts, and failure handling.
### How does Servy compare to WinSW?
WinSW is a solid tool but lacks some flexibility. It doesn't have a graphical user interface and is primarily an XML-configured wrapper. While WinSW supports setting a working directory, its health check and automatic recovery features are limited compared to Servy. WinSW's restart strategies rely mainly on exit codes but don't provide active health checks (like heartbeat monitoring) or advanced recovery options such as restarting the child process or the entire system after repeated failures.
### How does Servy compare to SrvAny?
For very simple scenarios, SrvAny can be sufficient when the goal is to start a process as a service. The trade-offs tend to appear around the service lifecycle and day-to-day operations. Once clean startup and shutdown behavior, recovery actions, and consistent logging become important, the limitations of SrvAny show. Servy provides the operational control and visibility that SrvAny lacks.
### How does Servy compare to Docker Desktop?
Docker Desktop gives you a visual dashboard for container logs, resource usage, and lifecycle management. Servy provides the same kind of visibility and control for the Windows Service Control Manager, so you don't have to hunt through system tools like Event Viewer or `services.msc` to see if your app is behaving.
The key difference is that while Docker provides an isolated environment (containers), Servy provides observability and control for native processes. It's a good fit for scenarios where you want the Docker-like visibility of live stdout/stderr and resource telemetry, but you need your application to run directly on the host OS with full access to the Windows environment.
### How does Servy compare to Tanuki's wrapper and is it compatible with Waratek RASP?
Tanuki's wrapper is tightly focused on the JVM and Java process lifecycle, often injecting native libraries. Servy is language-agnostic and treats the application as an external executable, staying outside the JVM boundary.
Because Servy does not instrument or modify the JVM, it avoids many compatibility issues with tools like Waratek RASP. As long as the JVM is launched with the correct options, Servy manages the service lifecycle without interfering with internal Java operations.
### Can Servy replace PM2 for Node.js on Windows?
Yes. Servy can replace PM2 on Windows by running `node.exe` directly as a native Windows service. Instead of keeping PM2 alive, your Node.js backend is managed by the Windows Service Control Manager (SCM), which is generally more reliable on Windows.
Where Servy helps most is stability and lifecycle management: native startup and shutdown, predictable restarts, logging, recovery, hooks, environment variables, service dependencies, and resource monitoring. This avoids many of the reliability issues PM2 users encounter on Windows.
If your goal is to keep a Node.js backend running reliably on a Windows server, replacing PM2 with Servy is a straightforward solution.
### Why not Microsoft.Extensions.Hosting.WindowsServices?
`Microsoft.Extensions.Hosting.WindowsServices` is aimed at apps designed to be Windows services from the ground up.
Servy is for the opposite situation: when you already have an app and you can't or don't want to rewrite it. You point Servy to an exe, and it adds practical operational features like logging, restart policies, and lifecycle hooks without changing the application code.
## Technical Configuration
### What types of applications can Servy run as a service?
Servy can run any executable, including Node.js, Python, .NET apps, batch scripts, and PowerShell scripts.
### How do I run scripts like batch or PowerShell with Servy?
Run them via their interpreters (`cmd.exe` or `powershell.exe`) by specifying the interpreter as the executable and the script path as a parameter.
### Does Servy support both console apps and GUI apps?
Yes. While services usually run background tasks, Servy can also host GUI apps as services if needed, though GUI interaction will be limited in service contexts (Session 0). This is generally discouraged by Microsoft and should only be used for legacy or transitional scenarios.
### Can I set a custom working directory?
Yes, Servy allows you to specify the startup directory to avoid path issues common with Windows services.
### Does Servy support running services under custom user accounts?
Yes, you can run a service under LocalSystem, domain accounts, AD, gMSA, or any custom user account with the required permissions.
### How do I update a service configuration?
If a service is already installed, you can update its configuration through Servy Manager. Open the service's configuration, make your changes, and click **Install** to apply them. Finally, restart the service to ensure all changes take effect.
### Can I use Servy in automated deployments (CI/CD)?
Absolutely. Servy provides a CLI and a PowerShell module, making it easy to script service installation and configuration as part of automated workflows.
This makes it suitable for tools like Azure DevOps agents, self-hosted runners, or internal build workers.
## Service Lifecycle & Hooks
### Does Servy support automatic restarts?
Yes, it includes health checks and configurable automatic recovery and restart policies.
### What happens if the monitored process crashes?
Servy executes the configured recovery action (restart service, restart process, restart computer, or none). Independently, an optional failure program can be configured to run after all recovery attempts have failed.
### Does Servy support running a script on service failure?
Yes, Servy provides an optional failure program to run after all recovery attempts have failed (and, when health-monitoring is disabled, when the child process exits with a non-zero code).
### What is the pre-launch hook in Servy?
The pre-launch hook is an optional script or executable that runs before the main service process starts. It can be used to prepare the environment or validate dependencies.
### What happens if the pre-launch hook fails?
If the pre-launch hook exits with an error, Servy stops the service startup to prevent running the main process in an invalid state, unless the **Ignore Failure** option is enabled.
### Can I pass arguments and environment variables to the pre-launch process?
Yes, you can configure both arguments and environment variables for the pre-launch process. Servy expands any environment variables before running the process.
### Is the pre-launch hook synchronous or asynchronous?
The pre-launch hook runs synchronously by default. If the pre-launch timeout is set to `0`, it starts in fire-and-forget mode (asynchronously).
### What is the post-launch hook in Servy?
The post-launch hook is an optional script or executable that runs after the main service process has started successfully and survived the startup health check.
### When is the post-launch process executed?
Servy waits for the service's **Start Timeout** (default 10 seconds, configurable via the Start Timeout setting, `--startTimeout` in the CLI, or `-StartTimeout` in the PowerShell module) to ensure the main process does not exit prematurely. If the process is still alive after that window, the post-launch action is executed asynchronously.
### What happens if the service stops while the post-launch process is waiting?
If the service stops before the post-launch process runs, the wait is canceled and the post-launch process will not run.
### Can I pass arguments to the post-launch process?
Yes, you can configure both arguments and working directory for the post-launch process.
### Can I use post-launch for long-running scripts?
Yes, the post-launch process runs independently of the main service process.
### What is the difference between a Pre-Stop and a Post-Stop hook?
A **Pre-Stop hook** executes *before* the main stop signal (a `Ctrl+C` console control event, `CTRL_C_EVENT`) is sent to the process. It is used for tasks like removing a node from a load balancer or finishing current work. A **Post-Stop hook** runs *after* the process has fully exited, typically used for cleaning up temporary files, releasing locks, or sending final logs.
### If the timeout is 0, does the Pre-Stop hook still run?
Yes, the hook is triggered, but the orchestrator does not block or wait for it to complete. It immediately proceeds to send the stop signal (`Ctrl+C` first, falling back to `TerminateProcess` after the stop timeout) to the main process. This is useful for "notification-only" hooks where you want to log an event but don't need to clean up before the process dies.
### What is the danger of a 0-second timeout on a Pre-Stop hook?
The main risk is race conditions. If your Pre-Stop hook is meant to "drain" traffic or save a state, and the main process exits 10 milliseconds later, the hook will likely be terminated mid-execution. Use a 0 timeout only for non-critical side effects (like sending a "Goodbye" message to a Slack channel).
### Is the service recreated after config change?
No. Servy acts as a service wrapper, where the registered Windows service executable is `Servy.Service.exe`. Servy stores your application path, parameters, working directory, and runtime options in its internal configuration and updates them in place when changed.
Because the Service Control Manager entry is modified in place rather than recreated, the service name remains unchanged. Windows derives the Service SID directly from the service name, so any permissions granted to `NT SERVICE\YourService` on files, folders, or system resources remain completely stable across updates.
The Service SID will only change if you:
* Delete and reinstall (uninstall then install) the service
* Recreate the service under a different name
* Explicitly change the service name
In these cases, Windows generates a new SID, and permissions assigned to the old SID will not carry over.
## Logging & Monitoring
### Can I capture stdout and stderr logs?
Yes, Servy can redirect `stdout/stderr` to log files with automatic rotation based on file size or date. Logs are stored as files and are separate from the Windows Event Log. They can be viewed through the Servy Manager with live tailing.
### Is there a way to view service logs in real time?
Yes, Servy Manager includes a log viewer with filtering and search, so you can quickly diagnose issues without leaving the app.
### Can Servy send notifications or emails on service failures or restarts?
Yes, Servy can generate notifications and send emails when an error occurs, helping you stay informed about service health. See [Service Event Notifications](https://github.com/aelassas/servy/wiki/Service-Event-Notifications) for more details.
### Does Servy provide CPU and RAM monitoring?
Yes, Servy Manager includes CPU and RAM monitoring accessible in two views:
* **Services Tab (Grid View):** Provides an overview of resource usage across your configured services in real time.
* **Performance Tab (Real-Time Graphs):** Displays CPU and RAM usage in real time for detailed monitoring.
Metrics details include:
* **CPU:** Usage is reported as a percentage of total machine capacity, which aligns with Windows Task Manager's CPU column behavior.
* **RAM:** Servy reports the process's **committed private memory** (the total unshared memory requested by the service, including memory currently paged to disk).
* **Process Tree Aggregation:** Servy dynamically aggregates metrics across the entire process tree. This means the reported performance data includes the core wrapped service process as well as all active child and descendant processes spawned by it.
**Note on RAM values:** Operators comparing Servy to Task Manager will notice that Servy's RAM values are generally higher than Task Manager's default "Memory" column. This is intentional. Task Manager defaults to showing the *Private Working Set* (only the memory currently sitting in physical RAM). Because Windows aggressively pages background service memory to disk under pressure, Working Set is an unreliable metric for monitoring background services. Servy uses the Commit Size to ensure you see the true memory footprint of your service, making it much easier to detect memory leaks and actual resource consumption.
### How do I monitor my service using a Heartbeat URL?
Servy includes built-in support for sending HTTP GET heartbeat pings to external monitoring platforms like [healthchecks.io](https://healthchecks.io/) or Uptime Kuma.
Configure these fields in your service settings:
* **Heartbeat URL:** Set your monitoring endpoint (e.g., `https://hc-ping.com/your-uuid`).
* **Heartbeat URL Timeout:** Set the HTTP request timeout between 2 and 30 seconds (default is 10 seconds).
* **Heartbeat URL Flags:** Enable this option to append `/start` when the service starts and `/fail` when process recovery fails.
When configured, Servy handles pings automatically:
* **Startup:** Sends a GET request to `https://hc-ping.com/your-uuid/start` (if flags are enabled).
* **Healthy Execution:** Sends a GET request to `https://hc-ping.com/your-uuid` on a steady-state health check pass, or to `.../start` if the pass follows one or more failed checks (if flags are enabled).
* **Failure or Crash:** Sends a GET request to `https://hc-ping.com/your-uuid/fail` when a crash occurs or recovery attempts are exhausted (if flags are enabled).
### How can I get failure alerts sent to Slack, Microsoft Teams, Phone Call, or WhatsApp?
Servy relies on out-of-band monitoring services to deliver real-time incident alerts. Point Servy's **Heartbeat URL** to a ping provider like [healthchecks.io](https://healthchecks.io/), which natively connects to downstream notification platforms.
1. Create a check in your monitoring service (e.g., `healthchecks.io`) and copy its unique ping URL.
2. Enter the URL into Servy's **Heartbeat URL** setting and enable **Heartbeat URL Flags**.
3. In your monitoring service dashboard, attach your preferred integration channels.
When Servy sends a `/fail` signal or stops pinging because the host machine went down, the monitoring platform triggers alerts to your configured channels, including:
* **Chat & Collaboration:** Slack, Microsoft Teams, Discord, Telegram, WhatsApp, Signal, Google Chat, Matrix, Mattermost, Rocket.Chat, Zulip.
* **Incident Management & Webhooks:** PagerDuty, Opsgenie, Splunk On-Call, Spike.sh, PagerTree, custom Webhooks.
* **Direct Notifications:** Email, SMS, Phone Call, Pushbullet, Pushover, ntfy, Gotify.
* **Issue & Event Tracking:** GitHub Issues, Trello, Prometheus.
> [!TIP]
> For local or isolated alerting without an external ping service, you can also use Servy's **Failure Program Path** to execute a local script (such as a PowerShell script calling a Slack Webhook) after all recovery attempts fail.
## Shutdown & Process Handling
### How does Servy stop the app?
If the child process is a console app, Servy sends a `Ctrl+C` signal to stop it gracefully. It waits a few seconds (configurable) for the process to exit; if it doesn't, Servy forces a kill. This procedure is repeated for each child process and its descendants recursively.
### How does Servy prevent zombie processes?
Servy tracks the full process tree it creates and manages it explicitly. When a service is stopped or restarted, Servy first attempts a graceful shutdown of the main process by propagating a `Ctrl+C` signal to the main process and all of its descendants, allowing applications to exit cleanly. If the process does not exit within the configured timeout, it is forcefully terminated.
This shutdown procedure is applied recursively to all child processes and their descendants, ensuring no orphaned processes are left behind. Any pre-launch or post-launch processes are also terminated with their entire process trees when the service stops.
As a result, Servy prevents zombie or runaway processes that continue consuming CPU or RAM after the service has been stopped.
### Will Java shutdown hooks run when a Servy service running a Java app shuts down?
Yes. When the service receives a stop request, Servy sends a `Ctrl+C` console control event to the Java process. The JVM interprets this as an interrupt signal, triggers the normal shutdown sequence and executes all registered hooks.
### Does Servy make requests for additional service shutdown time?
Yes. Servy explicitly requests extra time from the SCM when the configured start/stop timeout exceeds a threshold (configurable) using the standard `RequestAdditionalTime` mechanism.
### When exactly does the Pre-Shutdown signal occur?
It occurs when the Windows Service Control Manager (SCM) detects that the OS is shutting down or rebooting. It is sent before the standard "Stop" command. Windows waits for services that have registered for Pre-Shutdown to finish before it even begins the standard service shutdown phase. When a system shutdown or reboot is detected, Servy executes a specialized teardown workflow designed to ensure data integrity and a graceful exit.
### What happens if the OS shuts down or reboots?
When a system-level shutdown or reboot is initiated, Servy intercepts the Windows Pre-Shutdown signal, triggering a specialized teardown workflow that prioritizes data integrity. Servy silences the health monitor to prevent redundant recovery attempts and utilizes "Wait Hints" to grant the child process an extended window to flush buffers and commit transactions. This high-priority sequence ensures that even during a reboot, the service orchestrates a graceful departure rather than a forced termination.
## Environment & Compatibility
### Why do I need admin privileges?
Creating and controlling Windows services requires elevated permissions.
### Is Servy compatible with older Windows versions?
The modern build supports **Windows 10 (1809+)**, **Windows 11**, and **Windows Server 2016+**. Legacy systems such as Windows 7 SP1 and Server 2008 R2 are also supported via a dedicated **.NET Framework 4.8 build**. See the [Installation Guide](https://github.com/aelassas/servy/wiki/Installation-Guide#version-comparison) to choose the right version for your OS.
### Does Servy work on offline servers?
Yes. Servy works fully offline: installation does not require internet access, and there are no external dependencies to download.
### Can Servy be used as part of an installer without requiring extra dependencies?
Yes. The portable `servy-cli.exe` is a single self-contained executable and requires no additional frameworks.
### What happens if I uninstall Servy?
Services installed with Servy remain registered in Windows and working until you explicitly uninstall them. You can remove them safely through Servy Manager or the CLI. The service continues to function because it is a standard SCM-registered service, not dependent on the Servy UI or CLI.
## Advanced & Specific Scenarios
### Does Servy support environment variables for the wrapped process?
Yes, Servy provides full support for environment variables. You can configure these through the Advanced tab in the Servy Desktop App, the `--envVars` option in the Servy CLI, or the `-EnvVars` parameter within the PowerShell module. Furthermore, Servy allows for environment variable expansion within process paths, working directories, and parameters, ensuring your configuration remains dynamic and flexible across different environments.
### Does Servy support service dependencies?
Yes, service dependencies can be managed within the ecosystem. You can define these dependencies via the Advanced tab in the Servy Desktop App, the `--deps` option in the CLI, or the `-Deps` parameter in the PowerShell module. For better oversight, the Servy Manager allows you to visualize these relationships alongside real-time status indicators, where green signifies a running service, red indicates a stopped service, and orange warns of a dependency cycle.
### Does Servy support gMSA and AD log on?
Yes, Servy provides support for Group Managed Service Accounts (gMSA) and Active Directory (AD) logons, alongside standard domain and local accounts. It is also compatible with passwordless accounts and built-in Windows service identities such as NetworkService and LocalService. You can configure these credentials within the Log On tab of the Servy Desktop App, by using the `--user` and `--password` options in the CLI, or via the `-User` and `-Password` parameters in the PowerShell module. For gMSA and passwordless accounts, the password field should be left blank, allowing Servy to manage the authentication handshake automatically with the domain controller.
### Why does Excel COM automation fail when my application runs as a Windows service?
This is expected behavior. Microsoft Excel requires an interactive user session. When an application runs as a service, it executes in Session 0, which has no interactive desktop. The recommended approach is to use libraries like Open XML SDK or EPPlus that do not rely on COM.
### Can Servy run OneDrive as a Service using Azure Login?
OneDrive usually requires interactive login, which services cannot perform. However, you can use Servy to run a PowerShell/Graph API script that handles synchronization as a background service.
### Does Servy support ClickOnce-like update behavior?
Yes, in a service-safe way. The recommended approach is to use a **pre-launch hook** that checks for updates before the service starts. This ensures the update happens without file locking or race conditions.
### Can Servy run Windows Update from a PowerShell script as a service?
Yes, it is technically feasible, but you must ensure the script has the required administrative privileges (running under LocalSystem) and handles reboots and idempotency cleanly.
### How and why should I use CPU affinity with Servy?
**Why use CPU affinity?**
By default, the Windows OS scheduler distributes a process across all available logical CPU cores. Setting **CPU Affinity** binds the wrapped process to run on a specific subset of CPU cores. This is particularly useful for:
* **Resource Isolation:** Preventing high-CPU or compute-heavy background services from starving critical system processes or other co-hosted services.
* **Performance Optimization:** Keeping cache-sensitive or single-threaded workloads pinned to dedicated cores to reduce context switching and L1/L2/L3 cache thrashing.
* **Licensing Compliance:** Restricting legacy or proprietary software to a fixed number of licensed cores.
**How to set CPU affinity in Servy**
Servy accepts CPU affinity specifications in **core ranges**, **comma-separated lists**, or **hexadecimal bitmasks**:
* **Core List / Ranges:** `"0-3,8"` (Cores 0, 1, 2, 3, and 8) or `"0,2,4"` (Cores 0, 2, and 4).
* **Hexadecimal Bitmask:** `"0xFF00"` (Pins process to Cores 8 through 15).
* **Universal CPU 0:** `"0"` or `"0x1"` (Pins process to Core 0 across any machine).
#### Example Configurations
##### 1. Servy Desktop App (GUI)
When adding or editing a service in the **Servy Desktop App**:
1. Navigate to the **Main** tab.
2. Locate the **CPU Affinity** field.
3. Enter your desired CPU mask or core list (e.g., `0-3,8`, `0,2,4`, or `0xFF00`).
4. Click **Install** then **Restart**.
##### 2. CLI Usage
```cmd
servy-cli install --name="MyService" --path="C:\Apps\Worker.exe" --cpuAffinity="0-3,8"
```
##### 3. PowerShell Automation Script
```powershell
Import-Module "C:\Program Files\Servy\Servy.psm1" -Force
$installParams = @{
Name = "MyService"
Path = "C:\Apps\Worker.exe"
CpuAffinity = "0,2,4" # Binds process specifically to Cores 0, 2, and 4
}
Install-ServyService @installParams
```
##### 4. MyService.json Config File
```json
{
"Name": "MyService",
"ExecutablePath": "C:\\Apps\\Worker.exe",
"CpuAffinity": "0xFF00"
}
```
##### 5. MyService.xml Config File
```xml
MyService
C:\Apps\Worker.exe
0-3,8
```
#### How the Hexadecimal Bitmask Works
Here is a breakdown of how the hexadecimal bitmask `"0xFF00"` maps to **Cores 8 through 15**, along with the exact step-by-step math behind it.
##### 1. Convert Hexadecimal to Binary
In Windows CPU affinity bitmasks, **each bit position corresponds directly to a CPU core index**:
* **Bit 0** (rightmost bit) → Core 0
* **Bit 1** → Core 1
* ...
* **Bit** *N* → Core $N$
To see which cores are enabled, expand the hexadecimal value `0xFF00` into its 16-bit binary representation (since each hex digit represents 4 bits):
| Hex Digit | `F` | `F` | `0` | `0` |
| --- | --- | --- | --- | --- |
| **Binary** | `1111` | `1111` | `0000` | `0000` |
Putting it together as a 16-bit number:
$$\text{Binary: } 1111\ 1111\ 0000\ 0000_2$$
##### 2. Map Binary Bits to Processor Cores
Aligning the 16 bits with their zero-based core indices from right to left:
```text
Bit Position (Core): 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0
Binary Bit Value: 1 1 1 1 1 1 1 1 | 0 0 0 0 0 0 0 0
|_____________________| |_____________________|
Cores 8 to 15 ON Cores 0 to 7 OFF
```
* **Bits 0 through 7** are set to `0` $\rightarrow$ Cores 0-7 are **disabled**.
* **Bits 8 through 15** are set to `1` $\rightarrow$ Cores 8-15 are **enabled**.
Because bits 8 through 15 are all `1`, any process configured with this affinity mask is pinned exclusively to execute across **Cores 8, 9, 10, 11, 12, 13, 14, and 15**.
##### 3. Mathematical Calculation
To calculate the bitmask value programmatically, sum the powers of two ($2^k$) for every enabled core index $k$:
$$\text{AffinityMask} = \sum_{k=8}^{15} 2^k$$
```math
\begin{aligned}
\text{AffinityMask} &= 2^8 + 2^9 + 2^{10} + 2^{11} + 2^{12} + 2^{13} + 2^{14} + 2^{15} \\
&= 256 + 512 + 1024 + 2048 + 4096 + 8192 + 16384 + 32768 \\
&= 65,280_{10}
\end{aligned}
```
Converting decimal $65,280$ to hexadecimal yields `0xFF00`:
* $65,280 / 4096 = \mathbf{15} \rightarrow \mathbf{F}$
* $(65,280 \pmod{4096}) / 256 = \mathbf{15} \rightarrow \mathbf{F}$
* Remaining remainder $= 0 \rightarrow \mathbf{00}$
Result: **`0xFF00`**
---