Guides

Windows Task Scheduler: schtasks, PowerShell and cron for Windows

Windows Task Scheduler from the command line: schtasks /create examples, Register-ScheduledTask, running as SYSTEM, Last Run Result codes, cron compared.

Published ~9 min read
On this page

Short answer: Task Scheduler is the built-in cron for Windows. Open it with taskschd.msc, or create a task from an elevated prompt: schtasks /create /tn "Nightly backup" /tr "C:\scripts\backup.bat" /sc daily /st 03:00 /ru SYSTEM /rl highest. That runs the script every night at 03:00, with full rights, whether anyone is logged on or not.

cmd (Administrator)
schtasks /create /tn "Nightly backup" /tr "C:\scripts\backup.bat" /sc daily /st 03:00 /ru SYSTEM /rl highest
schtasks /run /tn "Nightly backup"
schtasks /query /tn "Nightly backup" /v /fo list

The second line runs the task once right away, the third shows its last result. This guide is written from Microsoft Learn’s schtasks reference, the ScheduledTasks PowerShell module reference and the Task Scheduler error and success constants, as of October 2026. Everything applies to Windows Server 2016 to 2025 and to Windows 10 and 11.

The Task Scheduler window, and what each setting is called elsewhere

Press Win+R, type taskschd.msc. Use Create Task rather than Create Basic Task: the basic wizard hides the security options you need on a server. The same settings exist on the command line and in PowerShell under other names:

GUIschtasks /createPowerShell
Name/tn-TaskName
Action: program and arguments/trNew-ScheduledTaskAction -Execute -Argument
Action: Start in(not available)-WorkingDirectory
Trigger: daily at 03:00/sc daily /st 03:00New-ScheduledTaskTrigger -Daily -At 3am
Trigger: at startup/sc onstart-AtStartup
Trigger: at log on/sc onlogon-AtLogOn
Run as SYSTEM/ru SYSTEMNew-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount
Run with highest privileges/rl highest-RunLevel Highest
Run only when user is logged on/it-LogonType Interactive
Stop the task if it runs longer than(not available)New-ScheduledTaskSettingsSet -ExecutionTimeLimit
Run task as soon as possible after a missed start(not available)-StartWhenAvailable
Replace an existing task/fRegister-ScheduledTask -Force

Two defaults surprise people: a task is stopped after it has run for three days, and if the previous run is still going, a new one is not started. Both are sensible for maintenance jobs and wrong for anything meant to run forever, such as a game server.

schtasks /create examples

cmd (Administrator)
rem every day at 03:00, as SYSTEM, elevated
schtasks /create /tn "Nightly backup" /tr "C:\scripts\backup.bat" /sc daily /st 03:00 /ru SYSTEM /rl highest

rem every 15 minutes
schtasks /create /tn "Health check" /tr "C:\scripts\check.bat" /sc minute /mo 15 /ru SYSTEM

rem every Sunday at 04:30
schtasks /create /tn "Weekly cleanup" /tr "C:\scripts\clean-temp.bat" /sc weekly /d SUN /st 04:30 /ru SYSTEM

rem at every boot, one minute after startup
schtasks /create /tn "Start app" /tr "C:\scripts\start-app.bat" /sc onstart /delay 0001:00 /ru SYSTEM /rl highest

rem inside a folder, replacing a task of the same name
schtasks /create /tn "\Maintenance\Nightly backup" /tr "C:\scripts\backup.bat" /sc daily /st 03:00 /ru SYSTEM /f
  • /sc is the schedule type: minute, hourly, daily, weekly, monthly, once, onstart, onlogon, onidle or onevent; /mo sets the interval within it.
  • /st takes 24-hour HH:mm. /sd and /ed take a date in the server’s regional format, which differs between servers; PowerShell, below, avoids that.
  • /ru SYSTEM needs no password. With a normal account instead, schtasks prompts for it (or takes /rp) and stores it.
  • /tr may be at most 262 characters, and a path with spaces needs escaped inner quotes: /tr "\"C:\Program Files\App\app.exe\" --nightly". A small batch file as the action avoids both problems.
  • /delay uses the format mmmm:ss and works only with onstart, onlogon and onevent.

schtasks /query, /run, /change and /delete

cmd (Administrator)
schtasks /query /fo table
schtasks /query /tn "Nightly backup" /v /fo list
schtasks /run /tn "Nightly backup"
schtasks /end /tn "Nightly backup"
schtasks /change /tn "Nightly backup" /st 02:30
schtasks /change /tn "Nightly backup" /disable
schtasks /change /tn "Nightly backup" /enable
schtasks /delete /tn "Nightly backup" /f

The verbose query is the quickest health check. Read Last Run Time, Last Result (explained below), Next Run Time, Run As User and Logon Mode. Per Microsoft’s reference, a SYSTEM task shows NT AUTHORITY\SYSTEM and Background only; a task created with /it shows Interactive only. /delete without /f asks for confirmation, which stops scripts.

Create a scheduled task in PowerShell

PowerShell (Administrator)
$action    = New-ScheduledTaskAction -Execute 'C:\scripts\backup.bat' -WorkingDirectory 'C:\scripts'
$trigger   = New-ScheduledTaskTrigger -Daily -At 3am
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
$settings  = New-ScheduledTaskSettingsSet -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 2)

Register-ScheduledTask -TaskName 'Nightly backup' -TaskPath '\Maintenance\' -Action $action -Trigger $trigger -Principal $principal -Settings $settings

Start-ScheduledTask   -TaskName 'Nightly backup' -TaskPath '\Maintenance\'
Get-ScheduledTaskInfo -TaskName 'Nightly backup' -TaskPath '\Maintenance\' |
    Select-Object LastRunTime, LastTaskResult, NextRunTime

Each New-ScheduledTask* cmdlet builds one part, and Register-ScheduledTask puts them together. The SYSTEM principal with -LogonType ServiceAccount runs the task whether or not anyone is logged on. -StartWhenAvailable catches up a run that was missed because the server was off at 03:00. -ExecutionTimeLimit replaces the three-day default; ([TimeSpan]::Zero) removes the limit for a process that should run indefinitely.

Other triggers: -AtStartup, -AtLogOn, -Weekly -DaysOfWeek Sunday -At '04:30', -Once -At (Get-Date).AddMinutes(10). To manage existing tasks: Get-ScheduledTask -TaskPath '\Maintenance\', Disable-ScheduledTask, Enable-ScheduledTask and Unregister-ScheduledTask -TaskName NAME -Confirm:$false. LastTaskResult appears as a decimal number; '0x{0:X}' -f (Get-ScheduledTaskInfo -TaskName NAME).LastTaskResult turns it into the hex form used in the table below.

“Run whether user is logged on or not” and the other account options

OptionschtasksPassword storedVisible windowNetwork shares
Run only when user is logged on/ru USER /itNoYes, in that user's sessionAs the user
Run whether user is logged on or not/ru USERYesNoAs the user
... with Do not store password/ru USER /npNoNoNo: local resources only
SYSTEM/ru SYSTEMNoNoAs the computer account

On a server, SYSTEM is the usual choice for maintenance: nothing to expire, nothing to type. Microsoft’s reference is explicit that SYSTEM has no interactive logon rights, so a SYSTEM task never shows a window, even if it starts Notepad. A task with a stored password has a different weak point: schtasks does not check the password, and when the account’s password changes or expires, the task silently stops running until you enter the new one: schtasks /change /tn NAME /ru USER asks for it without showing it.

Running a .bat or .ps1 file

Batch files

Point the action at the .bat file itself. Without a Start in folder the script starts in C:\Windows\System32, so either set -WorkingDirectory or begin the script with cd /d "%~dp0". Never put quotes in the GUI’s Start in field, even for a path with spaces; that alone produces error 0x8007010B. A scheduled script has no console anyone can read, so send its output to a log:

PowerShell (Administrator)
$action = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument '/c "C:\scripts\backup.bat >> C:\scripts\logs\backup.log 2>&1"'

PowerShell scripts

The program is powershell.exe (or C:\Program Files\PowerShell\7\pwsh.exe for PowerShell 7), and the script goes in the arguments:

PowerShell (Administrator)
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\scripts\job.ps1"'
  • Do not make the .ps1 itself the program. Windows opens .ps1 files with Notepad by default, so the task “runs” an invisible editor and your script never does.
  • -ExecutionPolicy Bypass applies to this one process only and leaves the server’s policy alone.
  • End the script with exit 0 or exit 1: that number becomes the task’s Last Run Result.

Task history and Last Run Result codes

Task history is off by default. Turn it on with Enable All Tasks History in the Actions pane, or from the command line:

PowerShell (Administrator)
wevtutil set-log Microsoft-Windows-TaskScheduler/Operational /enabled:true

Get-WinEvent -LogName Microsoft-Windows-TaskScheduler/Operational -MaxEvents 50 |
    Where-Object { $_.Message -like '*Nightly backup*' } |
    Format-Table TimeCreated, Id, Message -Wrap

Event 100 is a task start, 102 a completed task, 201 a completed action with the program’s return code, and 101 a task that failed to start. The Last Run Result column is mostly the exit code of your program, plus a few values of Task Scheduler’s own:

CodeMeaningWhat to check
0x0The program exited with 0: successNothing
0x1The program exited with 1The script’s last command failed, or it ended with exit /b 1. Robocopy returns 1 after a successful copy
0x2Exit code 2, often a file that was not foundThe paths in the action and inside the script
0x41301The task is currently runningNormal while it runs; if it stays, the program hangs
0x41303The task has not yet runNormal for a new task
0x41306The last run was terminated by the userEnded by hand: End, schtasks /end or Stop-ScheduledTask
0x8004131FAn instance of this task is already runningThe previous run is still going
0x800710E0The operator or administrator has refused the requestThe run-as account, its stored password and the task's conditions
0x8007010BThe directory name is invalidThe Start in folder: wrong, or in quotes
0x80070005Access is deniedRights of the run-as account

Any code that starts with 0x8007 wraps a Windows error number. Convert the last four hex digits to decimal and ask Windows: 0x8007010B is 267, and net helpmsg 267 prints The directory name is invalid. For a robocopy job, let the batch file translate its codes, so that 0x1 does not look like a failure: if %ERRORLEVEL% LSS 8 exit /b 0.

cron vs Task Scheduler

cron (Linux)Task Scheduler (Windows)
Where jobs liveOne text line each in a crontabOne XML task each, managed by a service
Command linecrontab -eschtasks, ScheduledTasks module
Schedule syntaxFive time fieldsTriggers: daily, weekly, minute, at startup, on an event
At boot@reboot/sc onstart
On a log eventNo/sc onevent
Run asThe crontab's ownerAny account, or SYSTEM
Missed runs while offSkipped (anacron catches up)Skipped unless -StartWhenAvailable
Overlapping runsAllowedNew run skipped by default
Maximum run timeNoneThree days by default
Output and logsMailed or redirected; syslog or journalRedirect yourself; history log, off by default
cronMeaningschtasks
0 3 * * *Daily at 03:00/sc daily /st 03:00
*/15 * * * *Every 15 minutes/sc minute /mo 15
30 4 * * 0Sundays at 04:30/sc weekly /d SUN /st 04:30
0 0 1 * *First of the month, midnight/sc monthly /d 1 /st 00:00
@rebootAt every boot/sc onstart

If you schedule jobs on Linux servers too, the crontab guide covers the other side, and the cron expression generator spells out what a cron line means before you translate it.

Why a scheduled task does not run

  • It works when you start it, but not as SYSTEM. SYSTEM has no mapped drives (use \\server\share paths), a different %USERPROFILE% and %TEMP%, and none of your per-user tools. winget is one of them: it is a per-user app alias that SYSTEM does not have, and the guide to winget on Windows Server shows the workarounds.
  • It ran at the wrong hour. Triggers follow the server’s own time zone; tzutil /g shows it.
  • It stops after exactly three days. The default time limit. Long-running servers need -ExecutionTimeLimit ([TimeSpan]::Zero), as in the Enshrouded server guide.
  • It never started after a reboot. Check that the trigger is onstart, not onlogon: after updates a server usually restarts with nobody logging on.

Task Scheduler on a Windows VPS

On a server that nobody watches all day, Task Scheduler does the routine work: nightly backups, log cleanup, a weekly restart after updates, and starting game or app servers at boot without anyone logged in. On a Windows VPS from VMHeaven you are Administrator on Windows Server 2022 or 2019, so you can register tasks as SYSTEM, and if a task ever locks you out of Remote Desktop, the VNC console in the panel still reaches the desktop to disable it.

Frequently asked

What is the Windows equivalent of cron?

Task Scheduler. It runs programs on a schedule, at startup, at log on or when an event is logged. Create tasks in taskschd.msc, with schtasks /create in Command Prompt, or with Register-ScheduledTask in PowerShell.

How do I create a scheduled task from the command line?

In an elevated Command Prompt run schtasks /create /tn NAME /tr PATH-TO-SCRIPT /sc daily /st 03:00 /ru SYSTEM /rl highest. That runs the script every day at 03:00 as SYSTEM, whether or not anyone is logged on. Test it right away with schtasks /run /tn NAME.

What does Last Run Result 0x1 mean in Task Scheduler?

The program the task started exited with code 1. For a batch file, its last command failed or it ended with exit /b 1; a wrong path or Start in folder is a common cause. Robocopy returns 1 after a successful copy, so a robocopy job needs its exit codes translated.

How do I make a task run whether the user is logged on or not?

Run it as SYSTEM (/ru SYSTEM, or New-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount), which needs no password, or as a named account with its password stored. In the GUI, select Run whether user is logged on or not on the General tab. Such tasks never show a window.

Related articles

All guides