schtasks is the command line interface to the Windows Task Scheduler. It creates, queries, runs, stops and deletes scheduled tasks, on the local machine or on a remote one, without opening the console. Everything the GUI can do, it can do, and a few things the GUI makes painful.
The reason to learn it is not convenience on a single machine. It is that scheduled tasks accumulate. A server that has been in production for five years carries jobs created by three administrators who have left, plus whatever the monitoring agent installed. Nobody clicks through Task Scheduler on twenty servers to find out what actually runs at 02:00.
This page treats schtasks as an inventory and deployment tool rather than a way to create one task. If your problem is a task that exists and fails, the walk-through on PowerShell scripts failing in Task Scheduler covers the six reasons that happens.
Applies to: Windows 10 / 11 and Windows Server 2016 / 2019 / 2022 / 2025
Quick answer
Three commands cover most of what you will actually type: see what exists, create something, and prove it works without waiting for the schedule.
rem Everything scheduled on this machine, in full detail
schtasks /query /fo LIST /v
rem A daily job at 02:00 running as SYSTEM with full privileges
schtasks /create /tn "Nightly-Backup" /tr "C:\bat\backup.cmd" /sc daily /st 02:00 /ru SYSTEM /rl HIGHEST
rem Run it now and read the result instead of waiting until 02:00
schtasks /run /tn "Nightly-Backup"
schtasks /query /tn "Nightly-Backup" /fo LIST /v
Creating or changing a task needs an elevated prompt. Querying does not, although a non-elevated query will not show every task.
What schtasks does
Six operations, and each one takes /tn to identify the task. Task names are paths: a task in the root folder is "Nightly-Backup", one in a subfolder is "\Contoso\Nightly-Backup".
| Operation | What it does | Needs elevation |
|---|---|---|
/query | Lists tasks, optionally as TABLE, LIST, CSV or XML | No |
/create | Creates a task from parameters or from an XML file | Yes |
/change | Modifies the program, the run-as account or the password | Yes |
/run | Starts the task now, ignoring the schedule | Yes |
/end | Stops the program the task started | Yes |
/delete | Removes the task | Yes |
The schedule is set with /sc, and the accepted values are MINUTE, HOURLY, DAILY, WEEKLY, MONTHLY, ONCE, ONSTART, ONLOGON, ONIDLE and ONEVENT. /mo is the modifier that turns DAILY into “every third day” or MONTHLY into LASTDAY.
/ru SYSTEM needs no password and survives password changes, which is why it is the usual choice for maintenance jobs. It also has no network identity of its own, so a task running as SYSTEM cannot reach a UNC path with the credentials of a user.
Practical examples
1. Find out what actually runs on this server
The problem: SRV-APP01 has a CPU spike every night at 01:30 and nobody knows what causes it. Task Scheduler shows several hundred entries, almost all of them Microsoft’s own maintenance jobs.
The solution: Export everything as CSV and filter out the Microsoft folder. What remains is what your organisation put there.
rem /v adds the run-as account, last result and next run time to each row
rem findstr /v /i removes the Microsoft branded tasks, which are noise here
schtasks /query /fo CSV /v | findstr /v /i "\Microsoft\" > C:\logs\tasks.csv
rem Same thing against a remote server, no session needed
schtasks /query /s SRV-APP01 /fo CSV /v > C:\logs\srv-app01-tasks.csv
Open the CSV and sort by Next Run Time. The column you actually care about is Run As User, because a task running under a named account is a task that will break the day that account’s password changes.
/fo CSV /v writes a header row for every task rather than one for the whole file. Import it into Excel or PowerShell and filter the repeated headers out, or use the PowerShell equivalent shown further down, which returns objects and avoids the problem.
2. Create a maintenance task that survives a reboot
The problem: A cleanup script has to run every night, as a service account, with full privileges, and keep working after the server restarts and after the account’s password changes.
The solution: Run it as SYSTEM with /rl HIGHEST. No password to store, no password to expire.
rem /rl HIGHEST is the "Run with highest privileges" checkbox; without it the task is limited
rem /f overwrites an existing task of the same name instead of prompting
schtasks /create /tn "Contoso\Cleanup-Temp" /tr "C:\bat\cleanup.cmd" /sc daily /st 01:30 /ru SYSTEM /rl HIGHEST /f
If the program needs its own arguments, the whole command goes inside /tr and the executable path needs its own quotes inside the outer ones.
schtasks /create /tn "Contoso\Report" /tr "\"C:\Program Files\Contoso\report.exe\" -mode nightly" /sc daily /st 03:00 /ru SYSTEM /rl HIGHEST /f
C:\Program. This is the single most common reason a task created from the command line never runs.
3. Prove it works without waiting for 01:30
The problem: The task exists, but nobody knows whether it will work until tomorrow morning.
The solution: Start it manually and read the result. /run uses the saved account and program path, so it tests the real configuration rather than your interactive session.
schtasks /run /tn "Contoso\Cleanup-Temp"
rem Last Result is the line that matters; Status tells you if it is still going
schtasks /query /tn "Contoso\Cleanup-Temp" /fo LIST /v
rem If it hangs, stop the program the task started
schtasks /end /tn "Contoso\Cleanup-Temp"
TaskName: \Contoso\Cleanup-Temp
Next Run Time: 05/09/2026 01:30:00
Status: Ready
Last Run Time: 04/09/2026 22:41:07
Last Result: 0
Run As User: SYSTEM
Schedule Type: Daily
Start Time: 01:30:00
4. Copy a task to twenty other servers
The problem: The cleanup task is correct on SRV-APP01 and now has to exist on every application server, identically. Recreating it by hand twenty times guarantees that at least one will differ.
The solution: Export the working task to XML and create the others from that file. The XML carries the triggers, conditions and settings that the command line parameters cannot express.
rem Export the definition from the server where it is known good
schtasks /query /tn "Contoso\Cleanup-Temp" /xml > C:\bat\cleanup-temp.xml
rem Recreate it elsewhere; /ru is still needed because the password is never exported
schtasks /create /tn "Contoso\Cleanup-Temp" /xml C:\bat\cleanup-temp.xml /ru SYSTEM /f
rem Same thing against a remote machine
schtasks /create /s SRV-APP02 /tn "Contoso\Cleanup-Temp" /xml C:\bat\cleanup-temp.xml /ru SYSTEM /f
Keep that XML in the same repository as the script it runs. A scheduled task without its definition under version control is a configuration that exists in exactly one place, on a machine that will eventually be rebuilt.
5. Remove it again
The problem: The job is retired, or the test task from the example above should not stay on a production server.
The solution: /delete removes it. Without /f it asks for confirmation, which is exactly what you want interactively and exactly what breaks a script.
rem Confirm what you are about to remove before removing it
schtasks /query /tn "Contoso\Cleanup-Temp"
rem /f suppresses the confirmation prompt
schtasks /delete /tn "Contoso\Cleanup-Temp" /f
schtasks /delete /tn * deletes every task on the machine, including the Microsoft maintenance tasks the operating system relies on. There is no undo. Always name the task.
Hidden gems
Half the alarming Last Result codes are not errors. Task Scheduler reports status as an HRESULT, and several of them are successes. This table is worth keeping.
| Last Result | Constant | What it actually means |
|---|---|---|
0 | S_OK | The program ran and exited with code 0. |
0x41300 | SCHED_S_TASK_READY | Ready to run at its next scheduled time. |
0x41301 | SCHED_S_TASK_RUNNING | Still running right now. Not a failure. |
0x41302 | SCHED_S_TASK_DISABLED | Disabled, so it will not run at all. |
0x41303 | SCHED_S_TASK_HAS_NOT_RUN | Never run yet. Normal on a task you just created. |
0x41304 | SCHED_S_TASK_NO_MORE_RUNS | The schedule has expired. |
0x41306 | SCHED_S_TASK_TERMINATED | The last run was stopped, by a user or by a time limit. |
0x8004131F | SCHED_E_ALREADY_RUNNING | A previous run never finished, so this one was skipped. |
Any other non-zero result is your program, not the scheduler. Last Result 1 means the program exited with 1. Task Scheduler ran it correctly and reported what it returned, which is why chasing the scheduler at that point wastes the afternoon.
/query /s beats remote desktop. It takes an inventory of a remote machine without a session, without a profile being created, and without anyone noticing. Loop it over a server list and you have an audit of every scheduled job in the estate in a couple of minutes.
Task folders are part of the name. /tn "Cleanup-Temp" and /tn "\Contoso\Cleanup-Temp" are two different tasks. Putting your organisation’s tasks in a named folder is the difference between finding them in three seconds and scrolling past four hundred Microsoft entries.
/sc ONEVENT turns an event log entry into a trigger. Combined with an XPath query it will start a task when a specific event ID appears, which is how you react to a service crash without polling for it.
PowerShell equivalent
The ScheduledTasks module returns objects, which makes it the better choice for anything you intend to filter, sort or report on. schtasks keeps the edge for one-line remote calls and for XML import on machines where remoting is not enabled.
# Everything that is not Microsoft's, with the account it runs as
Get-ScheduledTask | Where-Object TaskPath -notlike '*\Microsoft\*' |
Select-Object TaskPath, TaskName, State, @{n='RunAs';e={$_.Principal.UserId}}
# Last result and last run time, which live on a separate object
Get-ScheduledTask -TaskPath '\Contoso\' | Get-ScheduledTaskInfo |
Select-Object TaskName, LastRunTime, LastTaskResult, NextRunTime
# Start it now and stop it if it hangs
Start-ScheduledTask -TaskPath '\Contoso\' -TaskName 'Cleanup-Temp'
Stop-ScheduledTask -TaskPath '\Contoso\' -TaskName 'Cleanup-Temp'
# Export and remove, the equivalents of /query /xml and /delete /f
Export-ScheduledTask -TaskPath '\Contoso\' -TaskName 'Cleanup-Temp' | Out-File C:\bat\cleanup-temp.xml
Unregister-ScheduledTask -TaskPath '\Contoso\' -TaskName 'Cleanup-Temp' -Confirm:$false
Get-ScheduledTask does not carry the last result. It is on Get-ScheduledTaskInfo, which is why so many inventory scripts pipe one into the other.
Where this matters
- Server handover and audit. A CSV of every task on every server, with its run-as account, is the fastest way to find jobs owned by people who left.
- Account decommissioning. Before disabling a service account, query every server for tasks running as it. Disabling it first turns the discovery into an incident.
- Scheduled backups. A ROBOCOPY backup job only exists as a scheduled task, and its Last Result is what tells you whether last night’s copy ran.
- Build and deployment servers. XML export and import keep the same job identical across a farm instead of subtly different on each node.
- Incident response. An unexpected task, especially one in the root folder running a script from a temporary directory, is a persistence mechanism worth looking at closely.
Tips and limitations
- Creating, changing, running and deleting all need an elevated prompt. A non-elevated
/queryworks but hides tasks your account cannot see. - Never pass a password with
/rpin a script or a shared runbook. It lands in the command history and in any transcript. Use/ru SYSTEM, or a group managed service account. - The date format for
/sdand/edfollows the regional settings of the machine. A script that works on a machine set to English (United States) can fail on one set to Italian. XML import avoids the whole problem. - A task set to run only when a user is logged on, with
/it, will silently not run on a server nobody is logged into. - The Task Scheduler operational log is disabled by default. Enable it in Event Viewer under Applications and Services Logs before you need it, not during the incident.
/sagainst a remote machine needs the Remote Registry service and the firewall rules for remote administration. If it fails with access denied on a machine you are admin on, that is usually the cause.
Official documentation
Related tools
- ROBOCOPY Command Builder: builds the copy command that most nightly scheduled tasks end up running.
- Windows Event Log Analyzer: for reading the Task Scheduler operational log once a job starts failing silently.
Related guides
- net use in Windows: mapped drives, credentials and error 1219: why the drive letter a task depends on does not exist in its logon session.
- PowerShell scripts in Windows Task Scheduler: why they fail and how to fix it: the six reasons a task that looks correct produces nothing.
- sc Command in Windows: Query and Control Services from CMD: the same job for services rather than scheduled tasks.
- tasklist and taskkill in Windows: Find and End Processes from CMD: for the process a task started and left behind.
- How to backup files with ROBOCOPY: the most common thing to put behind a scheduled task in the first place.