Home › Topics › Bulk Moves › Robocopy

Robocopy for Server Migrations, Properly

Ask any Windows administrator how they moved their last file server and the answer is almost always robocopy. That is "robust file copy," the command-line tool that ships with Windows and Windows Server. Everybody uses it; surprisingly few drive it deliberately. The gap shows at migration time, because robocopy's defaults were tuned for casual copying. Several of them — the retry count above all — are actively dangerous on a big move.

This article turns robocopy into a rehearsed migration engine. We will walk the flags that matter in plain words and give the mirror switch the respect its delete behavior demands. We will fix the retry trap and tell the truth about multithreading. We will set up logging you can hand to an auditor, and read exit codes the way scripts must. It all ends in a worked example: a first-attempt command evolving, one justified flag at a time, into the hardened version you would actually run. This is the Windows half of our Bulk Internal Moves with Robocopy and Rsync series; the overall phase plan lives in planning the big copy.

Why Robocopy Owns Windows Migrations

Robocopy earned its place with four properties migrations need. It is already there — no installation, no license, present on every Windows box you will touch. It is restartable — kill it mid-run, run the same command again, and it skips what already arrived. It understands NTFS metadata — permissions, ownership, attributes, timestamps — which generic copy tools mangle. And it logs — every file, every skip, every failure, into a file you keep.

The restartability comes from how robocopy decides what to copy. For every file it compares source and destination by size and timestamp. A file that exists on both sides with matching size and time is classed same and skipped. A newer source file is copied. A file present only at the destination is classed extra and, normally, left alone. Rerunning a copy therefore costs only a tree walk plus the changed files — which is exactly the seed-and-delta behavior a migration is built on.

One consequence deserves early notice: because the comparison looks at size and timestamp, a file whose only change is its permissions looks "same" and gets skipped on later passes. We will fix that with /SECFIX below.

Choosing What to Copy: /E, /S, and the /COPY Letters

Two flag families define the scope of the copy: which directories come along, and how much of each file comes along.

Subdirectories: /S copies subdirectories but skips empty ones; /E copies subdirectories including empty ones. For a migration, always /E. Empty folders are not clutter — they are structure users expect, drop targets for scanners and applications, and carriers of permissions. A migration that silently drops them fails verification on directory counts.

File fidelity: the /COPY: flag takes letters, each enabling one slice of the file:

  • D — Data: the contents.
  • A — Attributes: read-only, hidden, archive, and friends.
  • T — Timestamps: created and modified times.
  • S — Security: the NTFS access control list (ACL) — who may read, write, and modify.
  • O — Owner: which account owns the file (quotas and auditing care).
  • U — aUditing: the auditing entries (the SACL), if your organization audits file access.

The default is /COPY:DAT — data, attributes, timestamps, no security. That default has quietly flattened permissions in a thousand migrations. Everything arrives owned by the copying account, inheriting whatever ACL the destination folder happened to have. For a server migration you almost always want /COPYALL, shorthand for /COPY:DATSOU. Copying security, ownership, and auditing needs privilege: run elevated, with an account holding backup and restore rights (Administrators or Backup Operators). /SEC exists too — it is shorthand for /COPY:DATS, security without owner or auditing info.

Directories have their own flag: /DCOPY:T preserves folder timestamps instead of stamping every directory with the moment of the copy. Folder dates matter more than they look — users sort by them, and cleanup scripts trust them. Losing them is the most common cosmetic complaint after a move. If ACLs change on the source after your seed pass, remember the skip problem from above. In that case, add /SECFIX to a pass to reapply security information to every file, including skipped ones. How NTFS permissions actually work — inheritance, explicit entries, SIDs — is covered in permission models explained.

The Retry Trap: /R and /W

Here is the flag pair that ruins unattended migration runs. When a file fails to copy — locked by an application, access denied — robocopy retries. How many times? /R:n sets the count, and the default is one million. How long between tries? /W:n sets the wait in seconds, and the default is thirty.

Multiply those defaults: a single file that will never copy — a mailbox archive locked every minute of every day — stalls the job for thirty million seconds. That is the better part of a year. Your carefully scheduled overnight seed pass is still sitting on the same file at breakfast, and the schedule you promised is gone.

For migration work, set both low: /R:2 /W:5 is plenty. A file that fails twice, five seconds apart, is not suffering a network blip — it is locked or forbidden, and thirty more retries will not change that. Let the pass finish, let the failure land in the log with its reason, and let the migration process handle it properly. Locked files get caught by a later delta pass after the user goes home. The stragglers get swept up during the write freeze, when nothing is locked at all. Failures chased at the end, with the log in hand, are the subject of verifying nothing was left behind. The broader philosophy — when retrying helps and when it just hides problems — is its own topic in our retry and error handling series.

Remember: never launch a migration pass with default retries. /R:2 /W:5 turns "one locked file stalls the night" into "one locked file adds ten seconds and a log line."

/MIR: The Mirror Switch and Its Teeth

/MIR makes the destination mirror the source exactly. It is equivalent to /E plus /PURGE — copy everything, including empty directories, and delete anything at the destination that no longer exists at the source. That second half is the teeth.

A migration genuinely needs purging. Users delete and rename files between your passes; without purging, the destination accumulates ghosts. Those are files deleted weeks ago on the source, resurrected forever on the new server, failing your verification counts in the confusing direction. So /MIR is the right tool for delta passes. It simply demands respect, because it deletes without asking:

  • Swapped arguments are a catastrophe. Robocopy's syntax is robocopy SOURCE DESTINATION. Reverse them with /MIR and robocopy faithfully mirrors your empty new tree onto the production share — which means deleting the production share. This exact accident has ended careers. Script the command once, review it, and never retype it ad hoc.
  • The wrong folder level is a quieter catastrophe. Mirroring \\new\share from \\old\share\projects purges every sibling folder at the destination.
  • Anything saved directly to the destination dies. If a helpful colleague pre-loads files onto the new server, the next /MIR pass removes them, because they do not exist at the source. During a migration, the rule is: nobody writes to the destination until cutover.

The discipline that makes /MIR safe is the preview run. /L tells robocopy to list what it would do — including every deletion — without doing any of it. Before the first mirror pass, run the exact command with /L, read the "EXTRA File" lines, and only then run it for real. Suppose you find yourself running /MIR on a schedule indefinitely because two servers must stay aligned from now on. You have drifted out of migration territory into standing synchronization — a job class with its own scheduling, retry, and alerting needs. A tool like Sysax FTP Automation fits that class better. Its wizard generates mirror and synchronization tasks over SFTP, FTPS, or FTP, runs them on a schedule, and emails you when a run fails.

/MT: Multithreading, Honestly

By default robocopy copies one file at a time. /MT:n copies with n parallel threads (8 if you write just /MT; up to 128). For migration workloads it is often the single biggest speed lever — and it is routinely oversold, so here is the honest version.

Multithreading shines on many small files. Small-file copying is dominated by per-file overhead — open, metadata, ACL, close. Parallel threads overlap those waits, easily tripling or quintupling throughput on a tree of a million small documents. On large sequential files it helps far less. A single thread can already saturate a disk or a gigabit link with a big file, and more threads just make the disk seek. It also has sharp edges. /MT is incompatible with /IPG (the inter-packet-gap throttle, covered with the other bandwidth tactics in bulk move pitfalls). Multithreading interleaves console output into nonsense, and a high thread count can saturate a shared link or a struggling NAS. Sensible practice: start at /MT:8, measure a real pass, try 16 or 32 for small-file trees, and always pair it with /NP /LOG: so the log stays readable.

Locked Files: /B, /Z, and /ZB

Two different problems both look like "file would not copy," and robocopy has a different answer for each.

Access denied means the copying account lacks permission to read the file — common on user home folders with tight ACLs. /B enables backup mode: robocopy invokes the backup privilege, which lets a suitably privileged account read files regardless of their ACL, exactly as backup software does. The catch is the privilege itself. The account needs the "back up files and directories" right (Administrators or Backup Operators membership) and an elevated session. That is one more reason migration passes run under a proper service account rather than whoever is logged in. Our scheduled jobs series covers setting that up.

Sharing violation means an application holds the file open exclusively. No privilege unlocks that — not /B, nothing. Those files wait for a later pass when the application has closed them. The last of them are collected during the write freeze, when every handle is closed by decree.

/Z is a different tool that gets confused with these: restartable mode, which checkpoints each file so an interrupted copy resumes mid-file instead of starting over. That insurance costs meaningful throughput, and on a LAN, where re-copying one interrupted file is cheap, it is usually not worth paying. Save /Z for huge files over unreliable WAN links. /ZB combines the pair: try restartable, and on access denied fall back to backup mode.

Logs and Exit Codes: Knowing What Happened

A migration pass you cannot audit did not happen. Three flags make robocopy's logging trustworthy. /LOG:file writes the log (overwriting), and /LOG+:file appends. /TEE shows output on the console as well. And /NP suppresses the per-file percentage counter that otherwise fills the log with garbage. Give every pass its own log file, named so passes sort in order. That is one of the many places a naming convention pays off, as our file naming and datestamping series argues. Keep every log until the migration is signed off: the FAILED lines are the input to your residue chase, and the summary tables are your evidence.

When a pass finishes, robocopy reports an exit code — and unlike most tools, it is a bitmap, not a simple pass/fail. Individual bits mean:

Code Meaning Migration reading
0 Nothing copied, no failures — already synchronized Your delta has reached zero. Good news.
1 Files copied successfully Normal successful pass.
2 Extra files or directories at the destination Expected with mirroring; check what was (or would be) purged.
4 Mismatched files or directories detected Investigate — often a file on one side is a folder on the other.
8 Some items could not be copied (retries exhausted) Failure. Read the log, chase every FAILED line.
16 Serious error — nothing copied (bad syntax, no access) Failure. Fix the command or permissions and rerun.

Codes add together: an exit code of 3 means "files copied (1) and extras seen (2)," which on a mirror pass is a perfectly healthy result. The rule for scripts is simple — 0 through 7 are the success family; 8 and above mean at least one thing failed. In a batch wrapper:

robocopy \\oldserver\eng E:\shares\eng /MIR /COPYALL /DCOPY:T /R:2 /W:5 /MT:16 /B /NP /LOG+:C:\miglogs\eng_delta.log
if %ERRORLEVEL% GEQ 8 (
    echo PASS FAILED with code %ERRORLEVEL% - see log
    exit /b 1
)
echo Pass OK, robocopy code %ERRORLEVEL%
exit /b 0

The same test in PowerShell is if ($LASTEXITCODE -ge 8). This matters beyond tidiness: anything that launches robocopy — Task Scheduler included — sees a nonzero code. It needs your wrapper to translate "3" as success and "8" as a page-someone failure.

The Worked Evolution: First Attempt to Hardened Command

Watch a realistic command grow. The job: move the engineering share from the old server to a new one's E: volume.

Attempt one is what everyone types first:

robocopy \\oldserver\eng E:\shares\eng /E

It copies data, attributes, and timestamps, including empty folders. It also copies no permissions and no ownership, retries locked files a million times, writes no log, and crawls through three million small files one at a time. Every following step fixes one of those.

Add fidelity. /COPYALL brings ACLs, owners, and auditing entries; /DCOPY:T keeps folder timestamps. The command must now run elevated, and errors about security are meaningful, not noise.

Defuse the retries. /R:2 /W:5: locked files become ten-second log entries for later passes instead of overnight stalls.

Read what ACLs forbid. /B lets the (privileged) migration account copy files whose permissions exclude it — user home folders being the classic case.

Go parallel, log properly. /MT:16 for the small-file speedup; /NP /TEE /LOG+: so the pass is watchable live and auditable afterward.

Exclude the non-cargo. /XD skips directories, /XF skips files: recycle bins, the System Volume Information folder, temp litter. Every exclusion is documented in the migration plan, because verification must treat "excluded" differently from "missing."

The hardened seed command:

robocopy \\oldserver\eng E:\shares\eng /E /COPYALL /DCOPY:T ^
    /R:2 /W:5 /MT:16 /B /NP /TEE /LOG+:C:\miglogs\eng_seed.log ^
    /XD "System Volume Information" "$RECYCLE.BIN" ^
    /XF Thumbs.db ~$*.*

Later, for delta passes, two changes: /E becomes /MIR so deletions propagate — but only after a preview pass with /L has shown you exactly what would be purged:

rem Preview first - lists copies AND deletions, changes nothing
robocopy \\oldserver\eng E:\shares\eng /MIR /COPYALL /DCOPY:T /R:2 /W:5 /B /NP /L /LOG:C:\miglogs\eng_preview.log

rem Then the real delta pass
robocopy \\oldserver\eng E:\shares\eng /MIR /COPYALL /DCOPY:T /R:2 /W:5 /MT:16 /B /NP /TEE /LOG+:C:\miglogs\eng_delta.log

Every flag now has a reason you can recite. That is the standard to hold: if you cannot justify a flag, it does not belong in a command that will touch four terabytes of other people's work. One boundary note: robocopy speaks SMB, which belongs inside trusted networks. If a leg of your move crosses the internet, carry it over an encrypted channel instead. One example is an SFTP/FTPS server like Sysax Multi Server on the receiving side, whose activity log doubles as a transfer record. The reasons are in securing SMB.

Where This Fits in the Migration

Robocopy is the engine; the vehicle is the phased plan. The seed command runs for days while users work, and the delta variant runs nightly and shrinks. The final pass runs under the write freeze of cutover night. After that, the logs, counts, and a difference report become the proof demanded by verification. Before your first real pass, skim the pitfalls article — long paths, ACL translation, and open files are cheaper to meet in a test tree than in a log at midnight.

Frequently Asked Questions

What is the difference between /E and /MIR?
/E copies all subdirectories, including empty ones, and never deletes anything. /MIR is /E plus /PURGE: it also deletes destination files and folders that no longer exist at the source. Use /E for the seed, and /MIR for delta passes — after a /L preview has shown you what it would purge.
Why does robocopy skip files without copying their new permissions?
Robocopy classifies files by size and timestamp. A file whose data is unchanged but whose ACL changed looks "same" and is skipped, so the permission change never travels. Run a pass with /SECFIX (together with /COPYALL or /SEC) to reapply security information to all files, including skipped ones.
What robocopy exit code means the copy failed?
Codes are a bitmap that adds together: 1 means files copied, 2 means extras at the destination, 4 means mismatches. Anything from 0 through 7 is the success family. A code of 8 or higher means at least one item could not be copied — treat it as a failure in scripts: if %ERRORLEVEL% GEQ 8.
Does /B backup mode copy files that are open in an application?
No. /B uses the backup privilege to read files whose permissions would otherwise deny you — it defeats ACLs, not locks. A file held open exclusively by an application still fails with a sharing violation; those files are caught on a later pass or during the write freeze.
Is a bigger /MT number always faster?
No. Threads help most on trees of many small files, where per-file overhead dominates. Large files are limited by disk and network speed, and too many threads can saturate a shared link or overwhelm a modest NAS. Start at /MT:8, measure a real pass, and raise it only if the numbers improve.

From the Sysax team: we build secure file transfer software for Windows. Sysax Multi Server is an FTP, FTPS, SFTP, and HTTPS server. Sysax FTP Automation handles scheduled, scripted transfers. Free trials are on the download page.