Repository navigation
Environment Variables
- Introduction
- Environment Variable Expansion
- GUI
- CLI Option:
--envVars - CLI Example
- PowerShell Option:
-EnvVars - PowerShell Example
- Tips
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.
Servy uses a sophisticated expansion engine that allows variables to reference each other. The resolution follows this specific order:
-
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 or changed in Windows is seen without restarting the machine (a removed one is dropped by the apps only, not by the service) - see the note below. -
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. -
Cross-Reference Expansion: Finally, all variables are scanned for
%VAR%placeholders. These placeholders are resolved using the final merged set of variables.
Note
New and changed 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 or changed 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 removed from the registry is not removed from the service: it keeps the value it inherited from the Service Control Manager, which is only rebuilt when the machine starts. A variable also defined as a User variable of the service account keeps the User value, as in Windows, andPathis the SystemPathfollowed 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.
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:
- Immutable Core System Variables (Tier 1): Essential operating system integrity boundaries that can never be overridden under any circumstances.
-
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\<ServiceName>\Servy.Service.log:
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:
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.
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. |
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. |
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
--allowOverriddenRuntimeVarsflag when installing the service (to change the setting on an existing service, runinstallagain):
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
-AllowOverriddenRuntimeVarsswitch toInstall-ServyService. -
Import/Export: Set
AllowOverriddenRuntimeVarstotruein the XML or JSON configuration (see 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:
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.
If your system has TEMP=C:\Windows\Temp and you define:
MY_ROOT=C:\ServyAppMY_LOGS=%MY_ROOT%\logsAPP_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 asDirect cycle detected for variable 'A'; leaving literal placeholder.and the placeholder is left literal (unless the OS already exports a value forA, in which case the inherited value is substituted to mimic WindowsPATH-append semantics). -
Multi-variable cycles (
A=%B%,B=%A%, or longer chains likeA=%B%,B=%C%,C=%A%): not detected by a dedicated check. Two-variable or indirect cycles typically degenerate into a direct self-reference (%A%insideA) during intermediate resolution and surface asDirect 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.
The advanced tab in Servy allows setting environment variables for the service process:
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%, matchingcmd.exebehaviors) (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.
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"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.
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-
Case Insensitivity: Environment variable names are case-insensitive. Defining
node_envwill correctly override an existingNODE_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, definingALERT_MSG=Battery at 100%%will expand correctly toBattery 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):
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"Copyright © Akram El Assas. All rights reserved.
Getting Started
Core Suite
Configuration & Features
- Advanced Configuration
- Logging & Log Rotation
- Health Monitoring & Recovery
- Environment Variables
- Service Dependencies
- Export / Import Services
Lifecycle Hooks
Automation & Production
- Servy Automation & CI/CD
- Embedding in Custom Installers
- Integration with Monitoring Tools
- Service Event Notifications
- Backup / Restore & VM Cloning
Reference & Support