Some Windows services are the silent load-bearing walls of a system. A backup agent, a database engine, a web server, a monitoring client: when one of these stops, the failure often goes unnoticed until something downstream breaks visibly, by which time hours of backups may be missed or a site may have been offline since the small hours. The frustrating truth is that many such stoppages are trivially recoverable; the service merely needed to be started again. An automated monitor that watches critical services and restarts them when they stop closes that gap, turning a silent outage into a self-healing blip.

The motivation is the familiar one behind all automation: machines are tireless and consistent where people are intermittent and forgetful. Administrators have all seen a service end up in a bad state after an update, with monitoring staying dark until someone happened to notice. Restarting a service on its first failure is frequently enough to restore a healthy state without waiting for an administrator to log in, which for business-critical but occasionally fragile services saves a great deal of pointless manual intervention. The goal is to make recovery automatic for the easy cases and to alert a human only when automation cannot fix it.

Choosing What to Monitor and Reading Its State

The first design decision is restraint: monitor only the services that genuinely matter. Watching every service on a machine wastes effort and generates noise, so a good monitor begins with a deliberate list of the handful of services critical to the system's purpose. Keeping that list short and explicit, and ideally storing it in a separate file so it can be updated without editing the script, keeps the monitor focused and easy to maintain.

Reading a service's state in PowerShell is straightforward, and the state itself is more nuanced than a simple running-or-not. A service can be running, stopped, or caught in a transitional state such as starting or stopping that never resolves. The basic check retrieves the service and inspects its status, treating anything other than running as a condition that may need action.

$service = Get-Service -Name 'BackupService'
if ($service.Status -ne 'Running') {
    Start-Service -Name 'BackupService'
}

That simple form handles the common case of a cleanly stopped service, but the transitional states deserve special care. A service stuck in a starting or stopping state will not respond to a plain start command, and the monitor must recognize this stuck condition as distinct from a clean stop. Distinguishing a service that has crashed and stopped from one that is wedged mid-transition is what separates a naive monitor from a reliable one, because the two conditions call for different remedies.

Restarting Cleanly and Handling Stuck Services

For a service that has simply stopped, starting it again is enough, and PowerShell offers a dedicated command that sends a stop followed by a start, which also works as a plain start if the service was already down. This restart command is the workhorse for recovering a service that crashed and left itself stopped, returning it to a running state in one step.

$critical = @('BackupService','MSSQLSERVER','W3SVC')
foreach ($name in $critical) {
    $svc = Get-Service -Name $name -ErrorAction SilentlyContinue
    if ($svc -and $svc.Status -ne 'Running') {
        Restart-Service -Name $name -Force -ErrorAction SilentlyContinue
    }
}

A service wedged in a transitional state needs firmer handling, because a normal restart command will wait politely for a transition that never completes. The remedy is to force the issue, which may mean stopping the service forcefully and starting it fresh, and in stubborn cases identifying and ending the underlying process so the service controller can start a clean instance. Treating a stuck service exactly like a stopped one is a common mistake that leaves the monitor spinning uselessly while the service remains unavailable.

A vital safeguard applies to all automatic restarting: it must be bounded. A monitor that blindly restarts a service every time it finds it stopped can create an endless loop, where a service that crashes immediately on start is restarted, crashes again, is restarted again, forever. The defensive design limits restarts to a set number of attempts within a window, and once that limit is reached it stops trying and escalates to a human instead, on the sound principle that a service which will not stay up after several tries has a real problem that automation cannot paper over.

Using the Built-in Service Recovery Options

Before reaching for a custom monitor at all, it is worth remembering that Windows has built-in recovery options for services, designed precisely for crash scenarios. The service control manager can be told what to do when a service terminates unexpectedly, restarting it after the first, second, or subsequent failure, and these settings live in the service's own recovery configuration. For services that crash outright, these native options are simpler and more immediate than any external script.

Configuring them from a script is most reliably done by calling the legacy service-control tool, which accepts the failure actions and the interval at which the failure count resets. A typical configuration restarts the service shortly after the first two failures and then, on a third, takes no further automatic action so that a persistently failing service does not loop indefinitely.

sc.exe failure "Spooler" reset= 86400 actions= restart/60000/restart/60000/""/0

The crucial limitation to understand is that these built-in recovery options react only to a service that terminates unexpectedly, a genuine crash, and not to one that is stuck or unresponsive while technically still alive. A service hung in a transitional state, or one that the controller believes is running but which has stopped doing useful work, falls outside what native recovery can detect. This is exactly where a custom monitoring script earns its place, covering the stuck and unresponsive cases that the built-in mechanism cannot see.

Handling Dependent Services and Start Order

Services rarely stand alone. Many depend on others to be running first, so a database front-end may rely on the database engine, and a web application on both a runtime service and a network listener. When a critical service stops, restarting it in isolation can fail if the services it depends on are themselves down, and restarting it can in turn require restarting the services that depend on it. A monitor that ignores these relationships will sometimes restart a service into an environment where it cannot possibly run.

The remedy is to respect dependency order during recovery. PowerShell can reveal a service's dependencies and dependents, so a thorough restart routine checks that the services a target depends on are running before attempting to start it, and is prepared to cycle the dependent services afterward if they were affected. Restarting in the wrong order produces a confusing cascade of failures that looks like a deeper fault but is really just a sequencing mistake.

$svc = Get-Service -Name 'AppService'
$svc.ServicesDependedOn | Where-Object { $_.Status -ne 'Running' } |
    ForEach-Object { Start-Service -Name $_.Name }
Start-Service -Name 'AppService'

Forcing a restart with the appropriate option will also stop and restart dependent services automatically, which is convenient but worth doing knowingly rather than by accident, since it widens the disruption beyond the single named service. A monitor that understands these chains recovers a tangled set of related services cleanly, while one that treats every service as independent leaves an administrator to untangle a half-started mess by hand. Mapping out the dependency relationships of the critical services in advance is what lets the monitor act in the right order when it matters.

Logging and Alerting When Recovery Happens

A monitor that silently restarts services is better than nothing, but a monitor that records its actions is far more valuable, because the pattern of restarts is itself diagnostic information. A service that needs restarting once a month is a minor annoyance; one that needs restarting every few hours is a symptom of a deeper fault that the restarts are merely masking. Writing each restart event, with a timestamp and the service name, to a log or to the event log builds the history that reveals which it is.

Recording restart events into the Windows registry or the event log provides a lightweight audit trail suitable for later review or collection by a management system. Beyond passive logging, the monitor should actively alert a human when automatic recovery fails, which is to say when the bounded restart attempts are exhausted without success. At that point the script has done all it safely can, and a notification, by email or through whatever channel the team watches, hands the problem to someone who can investigate.

if ($attempts -ge $maxAttempts) {
    $msg = "$name on $env:COMPUTERNAME failed to restart after $maxAttempts attempts."
    Write-EventLog -LogName Application -Source 'ServiceMonitor' `
        -EventId 1001 -EntryType Error -Message $msg
}

The relevant service-control-manager events are themselves worth knowing, since they record both unexpected terminations and failures to recover. An event noting that a service terminated unexpectedly a number of times, or that a corrective action failed, is the system's own account of the same trouble the monitor is fighting, and correlating the monitor's log with these events gives a fuller picture of what is actually going wrong.

Scheduling the Monitor to Run Continuously

A monitor is only useful if it runs often enough to catch a stoppage promptly, which means scheduling it to repeat at short intervals rather than once a day. The task scheduler supports this through a trigger that fires repeatedly, and building the task from PowerShell keeps it reproducible. The pattern combines a daily base trigger with a repetition interval so that the monitor runs every few minutes throughout the day.

$action  = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument '-NoProfile -WindowStyle Hidden -File "C:\Scripts\servicemon.ps1"'
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
    -RepetitionInterval (New-TimeSpan -Minutes 5) `
    -RepetitionDuration (New-TimeSpan -Days 3650)

Register-ScheduledTask -TaskName 'ServiceMonitor' `
    -Action $action -Trigger $trigger -User 'SYSTEM' -RunLevel Highest `
    -Description 'Monitor and restart critical services every 5 minutes'

The interval is a balance: too long, and a stopped service stays down for many minutes before the monitor notices; too short, and the monitor consumes needless resources checking services that are almost always fine. A five-minute cycle is a common compromise that keeps recovery brisk without being wasteful. Running under a system account with elevated rights is essential, because starting and stopping services requires privileges an ordinary account does not have, and without them the monitor would detect problems it could not fix.

Because the script, its service list, and the scheduled task are all expressed in code, the same monitor can be deployed identically across a fleet, ensuring every machine watches its critical services the same way. This uniformity prevents the scattered, inconsistent coverage that results when each server is configured by hand, and it makes the monitoring itself auditable, since the intended behavior is written down rather than improvised per machine.

What Reliable Service Monitoring Really Provides

The central insight is that most service outages are not catastrophes but lapses, and a lapse is exactly the kind of thing a machine can fix faster and more reliably than a person who has to first notice it. A monitor that restarts a stopped service within minutes, around the clock, eliminates a whole category of outages that would otherwise persist until human discovery, often hours later and after real damage to backups, availability, or data integrity.

A mature monitor does more than restart blindly. It watches only the services that matter, distinguishes a clean stop from a stuck transition and treats each correctly, bounds its restart attempts so it never loops forever, leans on the built-in recovery options for plain crashes while covering the stuck cases they miss, and logs every action so that a pattern of failures becomes visible rather than hidden. When automation reaches its limit, it escalates to a human instead of silently giving up.

Ultimately, automated service monitoring is how an administrator buys back both uptime and attention. The easy failures heal themselves and never reach the inbox, while the genuinely hard ones, the services that refuse to stay up, surface as deliberate alerts backed by a log that explains their history. The result is a system that is more resilient and an administrator who is interrupted only when interruption is truly warranted, which is the balance every piece of good automation is meant to strike.