
Automate a Nightly Robocopy Mirror to a NAS with Verification
Before you run this
This sets up a nightly one-way mirror of a local folder to a NAS SMB share using robocopy /MIR, followed by a second pass that lists any remaining differences as a verification step, all logged to a timestamped file and driven by a Scheduled Task.
Read this part twice: /MIR is destructive to the destination. A mirror makes the destination look exactly like the source, which means it deletes files and folders on the NAS that are not present in the source. If you point it at the wrong destination, or the source is empty because a drive failed to mount, /MIR will happily empty the NAS folder to match. Those deletions are not reversible unless the NAS itself has snapshots or recycle-bin retention. Mirror is not a backup with history — it is a copy of "right now."
Privileges:
- Running
robocopyitself only needs read on the source and write on the destination — no admin required. - Registering the Scheduled Task with
-RunLevel Highestand under a service account needs an elevated (Administrator) PowerShell session. - The task must run under an account that can reach the UNC path. SYSTEM and S4U logon types cannot access a network share, so you either run the task as a domain/service account with a stored password, or use a Group Managed Service Account (gMSA).
Test first, on a scratch folder and a scratch destination — never straight onto production data:
- Do a dry run with
/L(list only, changes nothing) and read the log. - Run the real mirror once by hand and check the result before you schedule it.
- Only then register the task.
I'm assuming Windows Server 2019/2022 or Windows 10/11, PowerShell 5.1, a local source folder, and a NAS reachable over SMB at a UNC path. robocopy and the ScheduledTasks module ship in-box on all of these.
The dry run — look before you leap
Open PowerShell and preview exactly what a mirror would do, without touching anything. Replace the two paths with yours:
# /L = list only, no changes. This shows what /MIR would copy AND delete.
robocopy 'D:\Data' '\\nas.example.com\backup\Data' /MIR /FFT /L /NP
Scroll to the summary and confirm the "Extras" figures are what you expect. "Extras" are the files on the destination that a real run would delete. If that number is large and surprising, stop and figure out why before going further.
The mirror script
Save this as C:\Scripts\nas-mirror.ps1 (create the folder yourself). Replace the placeholder paths.
# nas-mirror.ps1 — nightly one-way mirror to NAS, with a verification pass.
$Source = 'D:\Data' # replace: your source folder
$Dest = '\\nas.example.com\backup\Data' # replace: your NAS UNC path
$LogDir = 'C:\Logs\nas-mirror' # replace: where logs should live
$Stamp = Get-Date -Format 'yyyy-MM-dd_HHmmss'
$Log = Join-Path $LogDir "mirror_$Stamp.log"
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
# --- Mirror pass ---
# /MIR mirror: copy new/changed AND delete dest files missing from source
# /FFT 2-second file-time tolerance, avoids needless recopy over SMB/NAS
# /R:2 /W:5 2 retries, 5s wait, so a locked file doesn't stall for hours
# /DCOPY:DAT copy directory data, attributes, timestamps
# /NP no per-file percentage clutter in the log
robocopy $Source $Dest /MIR /FFT /R:2 /W:5 /DCOPY:DAT /NP /LOG:$Log
$mirrorCode = $LASTEXITCODE
# Robocopy exit codes are a bitmask: anything < 8 is success, >= 8 is failure.
if ($mirrorCode -ge 8) {
Write-Error "Mirror FAILED (robocopy exit code $mirrorCode). See $Log"
exit 1
}
# --- Verification pass ---
# /L lists what a mirror would STILL do. After a clean mirror, that should be
# nothing, so this pass should return exit code 0.
robocopy $Source $Dest /MIR /FFT /L /NP /LOG+:$Log
$verifyCode = $LASTEXITCODE
if ($verifyCode -eq 0) {
Write-Host "Verification OK: source and destination are in sync."
exit 0
} else {
Write-Warning "Verification found differences (robocopy /L code $verifyCode). Review $Log"
exit 2
}
A note on what "verification" means here. Robocopy does not do content checksums — there is no built-in MD5/hash verify flag, so do not expect one. What this script verifies is that a second mirror pass finds nothing left to copy and nothing left to delete, which confirms the two trees agree on name, size, and timestamp. That is the standard, honest way to sanity-check a robocopy mirror. If you need byte-level hash verification, that is a separate tool and a separate job.
Run it once by hand
Before scheduling, run the script interactively and read the log:
& 'C:\Scripts\nas-mirror.ps1'
Get-Content 'C:\Logs\nas-mirror\mirror_*.log' | Select-Object -Last 20
Confirm the summary shows the copy you expected and the verification line says "in sync."
Schedule it
Run this in an elevated PowerShell. Replace the account and password. The -User/-Password pair makes the task run with a stored password, which is what gives it network access to the NAS share.
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
-Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\nas-mirror.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At 1:30am
Register-ScheduledTask -TaskName 'NAS Nightly Mirror' `
-Action $action -Trigger $trigger `
-User 'EXAMPLE\svc-backup' -Password 'REPLACE_WITH_PASSWORD' `
-RunLevel Highest `
-Description 'Nightly robocopy mirror of D:\Data to NAS with verification'
Storing a plaintext password in a command is the trade-off of the -Password parameter. If your environment has one, a gMSA avoids the stored password entirely — set it up per Microsoft's guidance and register the task against it. I'm not walking through gMSA here; see Microsoft Learn for "Group Managed Service Accounts" and for the Register-ScheduledTask cmdlet.
Verify the task works
Force a run and inspect the result rather than waiting until 1:30 a.m.:
Start-ScheduledTask -TaskName 'NAS Nightly Mirror'
# LastTaskResult 0 = success. Non-zero warrants a look at the log.
Get-ScheduledTaskInfo -TaskName 'NAS Nightly Mirror' |
Select-Object TaskName, LastRunTime, LastTaskResult, NextRunTime
Then open the newest file in C:\Logs\nas-mirror and confirm the mirror summary and the "in sync" verification line. Spot-check the NAS itself — browse \\nas.example.com\backup\Data and confirm a recently changed file made it across with the right timestamp and size.
Undo and rollback
To stop the automation, remove the task:
Unregister-ScheduledTask -TaskName 'NAS Nightly Mirror' -Confirm:$false
That stops future runs and deletes the task definition; it does not touch the data already on the NAS.
Be clear-eyed about the one thing you cannot undo from here: /MIR deletions on the destination are gone. Removing the scheduled task will not bring them back. If you need protection against a bad mirror wiping good data, that protection lives on the NAS side — enable share snapshots or a recycle-bin/retention feature so a mistaken run is recoverable. Set that up before you trust this job with anything you care about.
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.
