Shore Up
A wind-up clock wired to a mechanical arm that copies a stack of documents from one tray into a second labelled storage drawer, at night.
WindowsSecurity

Automate a Scheduled Folder Backup with Robocopy and Task Scheduler

Ketan Aagja9 min read
No ratings yet

Before you run this

This guide sets up a nightly copy of one folder to a backup location using robocopy driven by a script, then schedules that script to run unattended with Windows Task Scheduler. The point is a low-fuss, repeatable file copy — not a full backup product with versioning or bare-metal restore.

A few things to be clear about before you touch anything:

  • The copy script itself needs no elevationrobocopy runs fine as a normal user, provided that user can read the source and write to the destination. Creating the scheduled task does require an elevated (Administrator) PowerShell session, because registering a task that runs as SYSTEM or as another account is an administrative action.
  • The script I give you is additive, not a mirror. It uses /E, which copies new and changed files and never deletes anything at the destination. That is deliberate. robocopy also has a /MIR (mirror) switch that makes the destination match the source exactly — which means it deletes files at the destination that no longer exist at the source, and that deletion is irreversible. I mention /MIR so you know it exists; I do not put it in the default script. If you switch to it, understand you are handing robocopy permission to delete.
  • Read the script, then test it on a throwaway pair of folders first. Point SOURCE and DEST at two test directories on a non-production machine, run it by hand, and confirm the log looks right before you schedule it against real data. Never schedule a copy you haven't watched run once.

I assume Windows Server 2022 or Windows 10/11, Windows PowerShell 5.1 (built in), and robocopy (built into Windows for years). The PowerShell scheduling cmdlets used below come from the ScheduledTasks module, which ships with the OS.

The backup script

Save this as C:\Scripts\backup.cmd. Edit the three lines in the config block and nothing else to start with.

@echo off
setlocal

REM ---- Configuration: edit these three lines ----
set "SOURCE=C:\path\to\source"
set "DEST=D:\Backups\source"
set "LOGFILE=C:\Backup-Logs\backup.log"
REM ------------------------------------------------

REM Make sure the log folder exists
for %%I in ("%LOGFILE%") do if not exist "%%~dpI" mkdir "%%~dpI"

REM /E    copy subdirectories, including empty ones (additive, never deletes)
REM /Z    restartable mode, survives a brief network blip
REM /COPY:DAT copy Data, Attributes, Timestamps
REM /DCOPY:T  copy directory timestamps
REM /R:3 /W:5 retry 3 times, wait 5s (defaults are far too high)
REM /NP   no per-file percentage (keeps the log clean)
REM /LOG: write a fresh log each run   /TEE also echo to console
robocopy "%SOURCE%" "%DEST%" /E /Z /COPY:DAT /DCOPY:T /R:3 /W:5 /NP /LOG:"%LOGFILE%" /TEE

set RC=%ERRORLEVEL%
echo. >> "%LOGFILE%"
echo Robocopy exit code: %RC% >> "%LOGFILE%"

REM Robocopy exit codes 0-7 mean success (files copied, extras seen, etc).
REM 8 and above mean at least one file could not be copied.
if %RC% GEQ 8 (
  echo BACKUP FAILED with exit code %RC%
  exit /b %RC%
)

echo Backup completed OK
exit /b 0

Two things worth knowing about that script:

  • robocopy's exit codes are a bitmask. Anything from 0 to 7 is a success (0 means nothing needed copying, 1 means files were copied, and so on). 8 or higher means a real failure. That is why the check is GEQ 8 rather than NEQ 0 — treating a normal "files copied" result as an error is a classic mistake.
  • The /LOG: switch overwrites the log each run, so it never grows without bound. If you want a running history instead, /LOG+: appends — but then you own trimming the file. Pick one; don't guess.

Run it once by hand from an ordinary command prompt to confirm it behaves, and open C:\Backup-Logs\backup.log to read what it did.

Schedule it with Task Scheduler

I'll register the task from PowerShell, which is scriptable and repeatable. Open an elevated PowerShell prompt (Run as administrator).

First decide which account the task runs as. For a copy between two local disks, running as the built-in SYSTEM account is simplest and needs no stored password. For a copy to a network share, SYSTEM won't have credentials on the remote host — you must run the task as a specific domain or local user who can write to that share, and Task Scheduler will prompt to store that account's password.

Here is the local-disk case, running as SYSTEM:

$action = New-ScheduledTaskAction -Execute "C:\Scripts\backup.cmd"

# Run every day at 02:00. Adjust the time to suit.
$trigger = New-ScheduledTaskTrigger -Daily -At "2:00AM"

# Run as SYSTEM, highest privileges. Good for a local disk-to-disk copy.
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" `
    -LogonType ServiceAccount -RunLevel Highest

Register-ScheduledTask -TaskName "Nightly Folder Backup" `
    -Action $action -Trigger $trigger -Principal $principal `
    -Description "Robocopy additive backup of C:\path\to\source"

For a network destination, drop the -Principal line and register the task against a named account instead. The straightforward way is:

Register-ScheduledTask -TaskName "Nightly Folder Backup" `
    -Action $action -Trigger $trigger `
    -User "EXAMPLE\backupuser" -Password (Read-Host -AsSecureString) `
    -RunLevel Highest `
    -Description "Robocopy backup to a network share"

Replace EXAMPLE\backupuser with a real account that can read the source and write the share. Read-Host -AsSecureString prompts so the password isn't sitting in your script or history.

If you'd rather not script the scheduling at all, the graphical Task Scheduler (taskschd.msc) creates the same task through the "Create Task" wizard — point the action at C:\Scripts\backup.cmd, set a daily trigger, and choose the run-as account. schtasks.exe is the command-line equivalent if you prefer it.

Verify it worked

Confirm the task exists and check its last result:

Get-ScheduledTask -TaskName "Nightly Folder Backup"
Get-ScheduledTaskInfo -TaskName "Nightly Folder Backup"

Get-ScheduledTaskInfo shows LastRunTime and LastTaskResult. A LastTaskResult of 0 means the script exited cleanly. Don't wait until 2 a.m. — force a run now:

Start-ScheduledTask -TaskName "Nightly Folder Backup"

Then check three things, in order:

  1. The log: open C:\Backup-Logs\backup.log. The summary table at the bottom lists files copied and any failures, and the script appends the robocopy exit code.
  2. The destination: browse D:\Backups\source and confirm the files and folder structure are there.
  3. A spot check: open one copied file and confirm it opens. A file that copied but won't open tells you more than any exit code.

Undo and roll back

To remove the schedule (this deletes the task definition, not any backed-up files):

Unregister-ScheduledTask -TaskName "Nightly Folder Backup" -Confirm:$false

Because the default script is additive and never deletes at the source, there is nothing to reverse on your live data — the worst case is extra files sitting in the destination, which you can simply delete. That safety is the whole reason I kept /MIR out of the default. If you later switch to /MIR and it prunes something you wanted, robocopy gives you no built-in way to get it back; that's why a mirror job belongs only against a destination you're content to have overwritten wholesale.

For the exact meaning of every switch and exit code, see Microsoft's own robocopy reference on Microsoft Learn, and the Task Scheduler cmdlet pages (Register-ScheduledTask, New-ScheduledTaskTrigger) on the same site — worth a look before you deviate from what's above.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.