robocopy /MIR is the fastest way to keep two folders identical on Windows. It is also the fastest way to wipe a backup drive, because a mirror does more than copy. It deletes everything in the destination that isn't in the source.
This post covers the version I'd actually put in a scheduled task: preview it first, exclude what you don't want touched, stop the default retry loop from hanging for days, log every run, and read the exit code properly so 1 doesn't get flagged as a failure.
Copy vs mirror: /E vs /MIR
These two lines look nearly identical. They behave very differently.
robocopy C:\Projects D:\Backup\Projects /E
robocopy C:\Projects D:\Backup\Projects /MIR
-
/Ecopies all subfolders, including empty ones. Files that exist only in the destination are left alone. -
/MIRis/Eplus/PURGE. Files and folders that exist only in the destination are deleted.
So if you deleted old-notes.txt from C:\Projects last week, /E keeps the copy on D: and /MIR removes it. That's exactly what you want for a true mirror. It's exactly what you don't want if D:\Backup\Projects also holds files that were never in the source.
Rule of thumb: use /E when the destination is an archive and /MIR when it's a disposable replica.
Step 1: dry run with /L
/L lists what robocopy would do without copying, deleting or touching timestamps. Run it every time you build a new mirror command:
robocopy C:\Projects D:\Backup\Projects /MIR /L /NJH /FP /NDL
-
/Llists only. Nothing changes on disk. -
/FPshows full paths so you can tell which side a file is on. -
/NDLhides directory lines so the file actions stand out. -
/NJHdrops the job header.
Look for lines marked *EXTRA File. Those are the files /MIR will delete from the destination. If that list contains anything you care about, stop and fix the command before you remove /L.
You can try the flag-reading habit without risking a real disk in CMD Master, a free browser terminal for practicing Windows CMD, PowerShell and Bash. Nothing gets installed and nothing you break is real.
Step 2: exclude what the mirror must not touch
robocopy C:\Projects D:\Backup\Projects /MIR /XD node_modules .git bin obj /XF *.tmp ~$* Thumbs.db /L
-
/XDexcludes directories by name or path. -
/XFexcludes files by name or wildcard.
Excluded items are skipped in both directions. They aren't copied from the source, and a matching folder that already exists in the destination isn't purged either. That makes /XD useful for protecting a folder in the destination that shouldn't be mirrored, like a local _archive folder.
If someone edits files directly on the destination, add /XO. It stops robocopy from overwriting a destination file with an older source copy:
robocopy C:\Projects D:\Backup\Projects /MIR /XO /L
This doesn't make two-way sync safe, because robocopy is still one-directional. It just stops the worst case of an old source copy overwriting newer work.
Step 3: fix the retry defaults
Robocopy's default retry settings are 1,000,000 retries with a 30 second wait between each one. A single locked file (an open Outlook PST, a database, someone's open Excel workbook) can stall a scheduled job for what is effectively forever.
Set retries explicitly:
robocopy C:\Projects D:\Backup\Projects /MIR /R:1 /W:1
One retry with a one-second wait is enough for a nightly job. The locked file gets reported as failed, and the next run picks it up.
Step 4: speed it up with /MT
robocopy C:\Projects \\nas01\backup\Projects /MIR /R:1 /W:1 /MT:16
/MT copies with multiple threads (default 8, range 1–128). It helps most with lots of small files over a network share. On a single spinning disk, high thread counts can make things slower, so test with 8 and 16 before going higher. Microsoft also recommends sending output to a log file when you use /MT, which brings us to logging.
Step 5: log every run
robocopy C:\Projects \\nas01\backup\Projects /MIR /R:1 /W:1 /MT:16 /LOG+:C:\Logs\projects-mirror.log /TEE /NP
-
/LOG+:appends to the log./LOG:without the plus overwrites it every run, and your history goes with it. -
/TEEwrites to the console and the log at the same time, so interactive runs still show output. -
/NPturns off the per-file percentage counter. Without it the log fills up with10% 20% 30%...noise.
The summary block at the end of each run (Copied, Skipped, Mismatch, FAILED, Extras) is what you check when someone asks whether last night's backup worked.
Step 6: read the exit code correctly
This catches a lot of people. Robocopy's exit code is a bit field, not "0 = OK, anything else = error":
| Code | Meaning |
|---|---|
| 0 | Nothing to copy. Source and destination already match. |
| 1 | Files were copied successfully. |
| 2 | Extra files exist in the destination. Nothing was copied. |
| 3 | Files copied and extras were found. |
| 5, 6, 7 | Combinations of copied, mismatched and extra files, with no failures. |
| 8 and above | At least one file or folder failed to copy. |
So %ERRORLEVEL% of 1 means success with work done. A script that does if errorlevel 1 goto :fail will report a failure every time the backup actually copies something. Task Scheduler does the same thing: "Last Run Result: 0x1" looks like an error but means files were copied.
Here's a batch wrapper that maps robocopy's codes to a normal 0 or 1 for whatever is calling it:
@echo off
setlocal
set "SRC=C:\Projects"
set "DST=\\nas01\backup\Projects"
set "LOG=C:\Logs\projects-mirror.log"
robocopy "%SRC%" "%DST%" /MIR /R:1 /W:1 /MT:16 /XD node_modules .git /XF *.tmp /LOG+:"%LOG%" /TEE /NP
set "RC=%ERRORLEVEL%"
if %RC% GEQ 8 (
echo [%date% %time%] robocopy FAILED with code %RC% >> "%LOG%"
exit /b 1
)
echo [%date% %time%] robocopy OK with code %RC% >> "%LOG%"
exit /b 0
Capture %ERRORLEVEL% into a variable right away. Any command after robocopy, including echo redirection, can change it before you check it.
Gotchas
Swapping source and destination. robocopy D:\Backup\Projects C:\Projects /MIR mirrors the backup onto your working folder and deletes everything you've created since the last backup. Worse, if the "source" path is an empty or freshly created folder, /MIR empties the destination. Put the paths in named variables like the script above, and always do a /L run when you change them.
A trailing backslash inside quotes. robocopy "C:\My Projects\" "D:\Backup\" breaks because \" is read as an escaped quote, so the arguments merge in strange ways. Drop the trailing backslash ("C:\My Projects"). If you need the root of a drive, leave it unquoted (D:\), since a drive root has no spaces.
Mirroring from a drive root. Copying from the root of a volume (for example E:\) can leave the destination folder with the Hidden and System attributes, so it seems to "disappear" in Explorer. Mirror a subfolder instead, or clear the attributes afterwards with attrib -h -s D:\Backup\E.
/MIR resets destination folder permissions. According to the docs, /MIR overwrites the destination directory's security settings, while /E /PURGE keeps them. If the destination share has carefully set ACLs, use /E /PURGE.
/MOV and /MOVE are not mirror options. They delete from the source after copying. Mixing them up with /MIR while "cleaning up" a command is a fast way to lose the original.
FAT/exFAT destinations recopy everything. USB drives formatted as FAT store timestamps with 2-second precision, so every file looks changed on every run. Add /FFT so robocopy allows for this.
A mirror command I'd actually schedule
robocopy "C:\Projects" "\\nas01\backup\Projects" /MIR /XO /R:1 /W:1 /MT:16 /XD node_modules .git bin obj /XF *.tmp ~$* /LOG+:"C:\Logs\projects-mirror.log" /TEE /NP
Run it with /L first, read the *EXTRA File lines, then remove /L, and wrap it in the exit-code script before it goes anywhere near Task Scheduler.
Official docs
- robocopy command reference (Microsoft Learn)
- Return codes used by the Robocopy utility (Microsoft Learn)
- if command and errorlevel checks (Microsoft Learn)
Want to build flag-reading muscle memory before you run /MIR against a real share? Practice Windows CMD commands in CMD Master's interactive browser terminal. It's free, covers CMD, PowerShell and Bash, and has nothing to install.
Top comments (0)