Windows Services and the Service Control Manager
How the Service Control Manager starts, stops, and supervises background processes, and how to configure and debug a service directly.
Every long-running background process on Windows that isn’t tied to a specific logged-in user — a database engine, a web server, an antivirus agent — is almost always registered as a service, supervised by a single system component: the Service Control Manager (SCM, services.exe). It’s Windows’ rough equivalent to systemd’s unit supervision or FreeBSD’s rc.d, with its own distinct configuration model.
Where service configuration actually lives
Every registered service has a corresponding key under the registry, which is the actual source of truth the SCM reads at boot and on demand:
HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Spooler"
Key values here include Start (boot / system / auto / manual / disabled, encoded as an integer), ImagePath (the executable, plus arguments), DependOnService (other services that must start first), and ObjectName (the account the service runs as).
Managing services: sc.exe and PowerShell
The classic command-line tool is sc.exe (Service Control), and modern administration typically uses PowerShell’s *-Service cmdlets instead, both ultimately talking to the same SCM API:
sc.exe query Spooler
Get-Service -Name Spooler | Select-Object Status, StartType, DependentServices
Start-Service Spooler
Stop-Service Spooler
Set-Service Spooler -StartupType Automatic
New-Service -Name "MyService" -BinaryPathName "C:\Apps\myservice.exe" -StartupType Automatic
Start types: when a service actually starts
Windows distinguishes several startup types, roughly analogous to a mix of systemd’s target-based ordering and enable/disable state: Boot/System start drivers extremely early (before most of the SCM’s own normal service-start sequence), Automatic starts at normal boot time, Automatic (Delayed Start) starts shortly after boot to avoid contending with more critical services for I/O and CPU during the busiest startup window, Manual only starts on explicit request or when another service/component starts it as a dependency, and Disabled can never start regardless of dependencies.
sc.exe config wuauserv start= delayed-auto
Dependencies: ordering without a full dependency-graph language
Unlike systemd’s rich After=/Requires=/Wants= vocabulary, the SCM’s dependency model is simpler: a service lists other services (or driver groups) it DependOnServices, and the SCM won’t start it until those are running. There’s no equivalent of systemd’s ordering-without-requiring (After= alone) — a listed dependency is always a hard requirement.
sc.exe qc Spooler
Service accounts and least privilege
A service’s ObjectName determines what it runs as — LocalSystem (full local privileges, avoid unless truly needed), LocalService/NetworkService (restricted built-in accounts), a Managed Service Account (an Active Directory-managed account with automatic password rotation, common for enterprise services needing network credentials), or a plain domain/local user account.
sc.exe config MyService obj= "NT AUTHORITY\LocalService"
Recovery actions: what happens when a service crashes
Each service has configurable recovery actions per failure — restart the service, run a program, or reboot the computer — settable per failure count (first failure, second failure, subsequent failures), which is the SCM’s equivalent to systemd’s Restart=/RestartSec=:
sc.exe failure Spooler reset= 86400 actions= restart/60000/restart/60000/run/1000
Debugging a service that won’t start
The Event Viewer’s System log is the first stop — the SCM logs a specific event (often Event ID 7000 or 7009) when a service fails to start, usually including the exact Win32 error code the service’s own startup routine returned:
Get-WinEvent -LogName System -FilterXPath "*[System[(EventID=7000 or EventID=7009 or EventID=7023)]]" -MaxEvents 10
Running the service’s executable directly from an elevated console, if it supports a debug/console mode, is often the fastest way to see the actual startup exception the Event Log’s summary line only hints at — many Windows services support a -debug or similar flag precisely for this.
The underlying design
Structurally, the SCM plays the same role systemd plays on Linux or launchd plays on macOS — a single, central process supervisor other tools query and command rather than each background daemon managing its own lifecycle independently. Its dependency model is intentionally simpler than systemd’s target/unit graph, but the core idea — declared dependencies, a single source of truth in a structured store (the registry, standing in for systemd’s unit files or launchd’s plists), and a uniform command surface (sc/Get-Service standing in for systemctl/launchctl) — is the same pattern every mainstream OS has converged on for supervising background work.