Home / Guides / Restart a Windows service on a schedule
Restart a Windows service on a schedule with Task Scheduler and PowerShell
Some services run better after a regular restart: one that slowly uses more memory, holds on to stale connections or picks up its settings only at start. Windows has no "restart every night" setting for a service, but Task Scheduler and one PowerShell command do the job for free. This guide shows the quick way, a safer script, and where both stop.
Short answer.
Create a scheduled task that runs as SYSTEM at a quiet time and runs
Restart-Service -Name MyService -Force. That is enough for a well-behaved service.
For a service that sometimes hangs while stopping, use the script below instead: it waits a limited time, can end a stuck process, starts the service and its dependents again, writes a log and returns an exit code that Task Scheduler shows as the task's Last Run Result.
The quick way: Restart-Service
Restart-Service sends the service a stop and then a start. Run it from an elevated PowerShell prompt first, so you see
how long the service takes and whether it comes back:
Get-Service -Name MyService | Select-Object Name, DisplayName, Status, DependentServices
Restart-Service -Name MyService -Force
Things worth knowing before you schedule it:
- Use the service name, not the display name.
Get-Serviceshows both; the Name column is what-Nametakes. - Dependent services need
-Force. Without it, a service that other services depend on fails with "Cannot stop service ... because it has dependent services. It can only be stopped if the Force flag is set." In our test, the restart with-Forcestarted the running dependent service again as well. List them first with(Get-Service MyService).DependentServices. - A stopped service is simply started. Microsoft's documentation says
Restart-Servicestarts a service that was already stopped, without an error. - It waits as long as the service takes. If the service hangs in Stop pending, the command keeps waiting, and so does your scheduled task.
Schedule it with Task Scheduler
From an elevated prompt, one schtasks line creates a task that restarts the service every night at 03:00 as SYSTEM,
whether or not anyone is signed in:
schtasks /Create /TN "Restart MyService nightly" /SC DAILY /ST 03:00 /RU SYSTEM /TR "powershell.exe -NoProfile -Command Restart-Service -Name MyService -Force"
Change /SC DAILY to /SC WEEKLY /D SUN for once a week. Pick a time when nobody uses the service and that
does not overlap backups or patching. Check it with schtasks /Query /TN "Restart MyService nightly" /V /FO LIST, and run it once by hand
with schtasks /Run /TN "Restart MyService nightly" to see it work.
A safer script for services that hang
The script below restarts one service and never waits forever. It skips a service that is not running (so it never starts something
you stopped on purpose), stops it with a time limit, and with -KillIfStuck ends the service's process if it is still
stopping after that limit. It refuses to kill a process that hosts more than one service, such as a shared
svchost.exe. It then starts the service and any dependents that were running, logs every run to
%ProgramData%\Restart-ServiceOnSchedule.log, and exits with 1 when something failed. -WhatIf shows what it
would do without touching anything.
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)][string]$Name,
[int]$TimeoutSeconds = 120,
[switch]$KillIfStuck,
[string]$LogFile = "$env:ProgramData\Restart-ServiceOnSchedule.log"
)
function Write-Log([string]$Text) {
Add-Content -Path $LogFile -Value ('{0:s} {1} {2}' -f (Get-Date), $Name, $Text)
}
$svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
if (-not $svc) { Write-Log 'FAILED: no service with that name'; exit 1 }
if ($svc.Status -ne 'Running') { Write-Log "skipped: status is $($svc.Status)"; exit 0 }
if (-not $PSCmdlet.ShouldProcess($Name, 'Restart service')) { exit 0 }
# Services that depend on this one are stopped with it. Remember the running ones.
$dependents = @($svc.DependentServices | Where-Object Status -eq 'Running')
$timeout = [TimeSpan]::FromSeconds($TimeoutSeconds)
# 1. Stop, but never wait forever.
$stopped = $true
try {
Stop-Service -InputObject $svc -Force -NoWait -ErrorAction Stop
$svc.WaitForStatus('Stopped', $timeout)
}
catch { $stopped = $false }
if (-not $stopped) {
$svc.Refresh()
if (-not $KillIfStuck) { Write-Log "FAILED: still $($svc.Status) after $TimeoutSeconds s"; exit 1 }
# Only kill a process that hosts this one service (never a shared svchost.exe).
$procId = (Get-CimInstance Win32_Service -Filter "Name='$Name'").ProcessId
$sharing = @(Get-CimInstance Win32_Service -Filter "ProcessId=$procId")
if ($procId -eq 0 -or $sharing.Count -gt 1) {
Write-Log "FAILED: stop timed out and process $procId is shared or unknown, not killed"; exit 1
}
Stop-Process -Id $procId -Force
try { $svc.WaitForStatus('Stopped', [TimeSpan]::FromSeconds(30)) }
catch { Write-Log "FAILED: killed process $procId but the service did not stop"; exit 1 }
Write-Log "stop timed out after $TimeoutSeconds s, killed process $procId"
}
# 2. Start it again, then the dependents that were running.
try {
$svc.Start()
$svc.WaitForStatus('Running', $timeout)
foreach ($d in $dependents) {
$d.Refresh()
if ($d.Status -ne 'Running') { $d.Start(); $d.WaitForStatus('Running', $timeout) }
}
}
catch { Write-Log "FAILED to start: $($_.Exception.Message)"; exit 1 }
Write-Log ('restarted' + $(if ($dependents) { ' with ' + ($dependents.Name -join ', ') }))
exit 0
Ending a process is the same as the service crashing: whatever it was writing at that moment is lost. Only use
-KillIfStuck for a service you know recovers from that, and give it a time limit well above its normal stop time.
Save it as C:\Scripts\Restart-ServiceOnSchedule.ps1. The task runs it as SYSTEM, so make sure only administrators can
change that folder: anyone who can edit the script can run code as SYSTEM. Then register the task with the ScheduledTasks module,
which is easier to read than a long schtasks line:
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Restart-ServiceOnSchedule.ps1" -Name "MyService" -KillIfStuck'
$trigger = New-ScheduledTaskTrigger -Daily -At '03:00'
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 15)
Register-ScheduledTask -TaskName 'Restart MyService nightly' -Action $action -Trigger $trigger -Principal $principal -Settings $settings
- No
-RunLevelneeded. Microsoft documents that the run level is ignored for tasks that run as Local System; the task already has full rights. - A time limit on the task. Without
-ExecutionTimeLimitTask Scheduler stops a task only after 72 hours. Fifteen minutes is plenty for a restart. - Missed runs are skipped. "Run task as soon as possible after a scheduled start is missed"
(
-StartWhenAvailable) is off by default. Leave it off for restarts: if the server was down at 03:00 you do not want a restart halfway through the morning. - For a weekly restart use
New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At '03:00'.
Check the last run afterwards. A Last Run Result of 0 means the restart worked; 1 means the script logged a failure:
Get-ScheduledTaskInfo -TaskName 'Restart MyService nightly' | Select-Object LastRunTime, LastTaskResult, NextRunTime
Get-Content "$env:ProgramData\Restart-ServiceOnSchedule.log" -Tail 10
We ran every script on this page on a Windows 11 test machine with test services, including one that depends on another and one that
hangs on stop: the restart, the dependent restart, -WhatIf, the skip, the time limit, the kill and both scheduled
tasks ran as described.
Where a scheduled task stops being enough
- One task per service per server. Ten services on five servers is fifty tasks to create, keep in step and find again.
- Nobody hears about it. A failed restart is a log line and a Last Run Result that someone has to look at. A service that stays stopped afterwards stays stopped.
- A restart is not a watchdog. If the service crashes at 10:00, the 03:00 task does not help. You need a separate check that starts it again (see the Recovery tab vs a watchdog guide).
- Maintenance needs handwork. During patching you have to remember to disable the tasks, and to enable them again.
Questions
Does Windows have a built-in setting to restart a service every night?
No. The service's Recovery tab only reacts when the service fails; it has no schedule. A scheduled restart needs Task Scheduler with a command such as Restart-Service, or a tool that does it for you.
Which account should the scheduled task run as?
SYSTEM is the simplest: it can stop and start any service and needs no stored password. A domain account also works if it is allowed to control that service, but then the task needs that account's password, and you have to update the task whenever the password changes.
Will a scheduled restart start a service I stopped on purpose?
Restart-Service will: Microsoft documents that it starts a service that was already stopped. The script on this page skips a service that is not running and logs that it did.
Can I restart a service on another server from one place?
Restart-Service has no -ComputerName parameter. You can run it remotely with Invoke-Command over PowerShell remoting, but a nightly restart is easier to keep reliable as a task on the server that runs the service, so it still runs when the network or the managing machine is down.
How do I know the restart worked?
Look at the task's Last Run Result (Get-ScheduledTaskInfo, or the Task Scheduler window): 0 means success, and the script on this page returns 1 when the restart failed. Its log file shows each run. Nothing tells you by itself; SvcSentinel can email you when it restarts a service you switched notifications on for.
Related
- SvcSentinel: automatic and scheduled restarts for Windows services
- Restart a Windows service automatically: the Recovery tab vs a watchdog
- Get an email when a Windows service stops
- All guides
Sources (Microsoft Learn): Restart-Service, Stop-Service, schtasks create, New-ScheduledTaskPrincipal, Principal.RunLevel, TaskSettings.ExecutionTimeLimit, TaskSettings.StartWhenAvailable.