rsync Across Platforms: Windows, Permissions, and Other Gotchas
rsync grew up on Unix, and it shows. Its worldview assumes files have an owner and a group identified by number, a nine-bit permission mode, a symlink is a thing a filesystem can hold, names are case-sensitive byte strings, and timestamps are precise. Every one of those assumptions is false somewhere in a real network — on a Windows workstation, a NAS appliance, a FAT-formatted USB drive, a macOS laptop — and each broken assumption produces its own distinctive misery: error spam, jobs that re-copy everything every run, files that silently overwrite each other.
The good news is that the failures are systematic, not random. Learn the half-dozen mismatch categories and you can predict, from the two endpoints alone, exactly what a cross-platform rsync job will complain about and which flags calm it down. This article is that field guide: getting rsync onto Windows in the first place, the permissions problem, the timestamp trap, filename hazards, and the handful of setups that genuinely hold up. It is part of our rsync & Delta Transfer series.
Getting rsync Onto Windows at All
rsync is not a native Windows program, so every "rsync on Windows" setup is one of two shapes, and it pays to know which you are running:
- WSL — a Linux environment inside Windows. The Windows Subsystem for Linux runs an actual Linux userland, and you install rsync in it exactly as on any Linux box. Windows drives appear under paths like
/mnt/c/…, so a job readsrsync -av /mnt/c/Data/ user@server:/srv/data/. Because it is real Linux, behavior matches the documentation, SSH keys live in the Linux home directory, and quirks are mostly confined to the boundary where/mnt/ctranslates NTFS metadata. - Cygwin-based builds — rsync compiled against a POSIX compatibility layer. Several packaged distributions of rsync for Windows take this shape, bundling the compatibility DLLs alongside the binary. Drives appear as
/cygdrive/c/…. These builds integrate a little more directly with Windows (no Linux subsystem needed, easy to ship on servers) at the price of an extra translation layer with opinions of its own about permissions and users.
Both approaches work in production. The practical advice is to pick one shape per fleet and standardize — path spelling, line-ending expectations of your scripts, where SSH keys live, and how scheduled tasks launch the job all differ between the two, and a mixed estate doubles your documentation for no benefit.
Scheduling is the detail that trips people after the first manual success. A WSL rsync job runs inside the Linux environment, so a Windows Task Scheduler entry must launch it through WSL (running the command via the wsl launcher) rather than pointing at a binary directly, and the job runs with the Linux side's SSH keys and environment — test the scheduled invocation itself, not just the interactive command, because "works in my terminal, fails at 2 a.m." is almost always an environment difference between the two.
Robocopy: The Native-Windows Cousin
Before forcing rsync into a purely Windows corner, know its native cousin. Robocopy ships with Windows and does robust mirroring — robocopy C:\Data \\server\share\Data /MIR — with retries, logging, and NTFS attribute and ACL awareness that rsync can only envy on that terrain. What it lacks is rsync's essence: it has no delta-transfer algorithm (changed files are re-sent whole), and it speaks no SSH — it works over local paths and SMB shares.
The decision rule is simple. Windows to Windows over a LAN or SMB share: robocopy is the path of least resistance. The moment a Linux/Unix endpoint, an SSH transport requirement, or WAN bandwidth efficiency enters the picture, rsync earns its setup cost. (For the SMB side of that boundary — and when shares beat transfers at all — see our network shares vs file transfer series.)
The Permissions Problem: -a Meets a Foreign Filesystem
Archive mode's -o, -g, and -p assume the destination speaks Unix: numeric user and group IDs and a permission mode per file. Point that at a destination that doesn't — NTFS with its ACL model, a FAT or exFAT USB drive with no ownership concept at all, an SMB-mounted share where the mount itself fakes uniform ownership — and you get the classic symptom: a wall of chown/chgrp "operation not permitted" errors, one per file, on an otherwise successful transfer. Worse, because the destination can never hold the attributes rsync keeps trying to set, some jobs re-attempt the same attribute changes run after run, forever.
The cure is to stop asking for what the destination cannot store. The standard recipe for pushing to a foreign or dumbed-down filesystem:
# "Foreign filesystem" recipe: keep structure, times, and data;
# stop pretending the destination has Unix owners and modes.
rsync -rltDv --no-perms --no-owner --no-group \
--chmod=ugo=rwX --modify-window=2 \
/data/projects/ /mnt/usbdrive/projects/
Reading it: -rltD is archive mode minus the three attribute letters — recursion, symlinks, times, specials — and the three --no-* flags make the subtraction explicit and self-documenting. --chmod=ugo=rwX hands every file a sane, uniform mode where modes exist at all (capital X means "execute bit only on directories and files already executable"). --modify-window we will meet in a moment. Between genuinely Unix-like machines, by contrast, keep -a — and for server-to-server clones add --numeric-ids, so UID 1004 stays UID 1004 instead of being remapped through name lookups that may not agree between hosts. ACLs (-A) and extended attributes (-X) are worth carrying only between filesystems of the same family; across families they map badly or not at all.
Two refinements complete the permissions picture. First, who runs the receiving side matters: setting arbitrary ownership requires root there, so a backup pulled by an unprivileged account silently stores every file as that account even with -o -g requested. Second, for exactly that situation rsync offers --fake-super: the receiver records the true owners, groups, and modes in extended attributes alongside each file instead of applying them. The backup data is complete — attributes preserved as metadata, restorable later with the same flag — while the backup process runs without root. It is the respectable middle path for backup servers that shouldn't hold root credentials to anything.
Timestamps: The Two-Second Trap
rsync's quick check — skip files whose size and modification time match — silently assumes both sides store timestamps at similar precision. They often don't. FAT-family filesystems round modification times to two-second boundaries; some network filesystems and older NAS firmwares truncate sub-second precision; Unix filesystems commonly keep nanoseconds.
The failure looks like this: you sync to the USB drive, everything copies, done. Tomorrow you run the same job and rsync transfers everything again — because every source mtime of, say, 10:31:07.4 was stored on FAT as 10:31:08 or 10:31:06, no destination time exactly matches its source, and the quick check flags the whole tree as changed. The fix is one flag: --modify-window=2, which tells the quick check to treat times within two seconds of each other as equal. Cheap, harmless on same-platform jobs, and the difference between a thirty-second nightly run and a four-hour one.
One more timestamp oddity deserves a sentence of honesty: FAT filesystems store times in local time rather than universal time, so a daylight-saving shift or a machine's timezone change can make every file on the drive appear one hour "newer" or "older" overnight — provoking exactly one puzzling full re-copy. If a job that was quiet suddenly re-copies the world twice a year, this is the culprit, and --modify-window=3601 is the pragmatic, slightly rueful workaround for drives that must stay FAT.
Remember: when a cross-platform rsync job re-copies everything every run, it is almost never "rsync being slow" — it is the quick check failing honestly over metadata the destination cannot store. Diagnose with a dry run and itemized output (-ni): the change codes show exactly which attribute rsync thinks differs, and the flag that silences it follows directly. Decoding those codes is covered in dry runs and verification.
Filenames and Encodings
Byte-string filenames meet three different filesystem opinions, and each collision has a signature:
- Characters and names Windows refuses. NTFS forbids
< > : " / \ | ? *in names, plus names ending in a dot or space, plus a set of reserved device words (CON,PRN,AUX,NUL,COM1…). A Linux tree containingreport: final.txtor a directory calledauxwill fail file-by-file when pushed to Windows storage. The error is per-file and the rest of the job continues — check exit codes and logs, or the gaps go unnoticed. - Case-insensitivity collisions. Linux happily holds
Makefileandmakefilein one directory; Windows and default macOS treat those as the same name, so one arrives and the other overwrites it — silently. The symptom is a destination with slightly fewer files than the source and no error anywhere. A pre-flight duplicate check on the source (case-folded name comparison) is the only real defense. - Unicode normalization. An accented character can be encoded as one composed codepoint or as base-plus-combining-mark, and macOS filesystems historically favored the decomposed form while Linux favors composed. The same visible name —
café.txt— can therefore be two different byte strings, and a sync between the platforms duplicates such files or re-copies them forever. rsync's--iconvoption exists for exactly this, translating names in transit (the conventional macOS-to-Linux incantation is--iconv=utf-8-mac,utf-8). - Path length. Very deep trees that are legal on Linux can exceed Windows path-length limits, failing on arrival. Modern Windows can lift the limit system-wide, but the durable fix is not building 300-character paths into your data layout.
The meta-lesson: most filename pain is cheapest to fix at the source, as naming policy, rather than in transit as heroics.
Symlinks, Special Files, and the Line-Ending Question
Two smaller mismatch families round out the set. Symbolic links (-l in archive mode) can't be represented faithfully on FAT at all and translate awkwardly onto Windows destinations; your options are -L (follow links and copy what they point at — the tree arrives complete but "fatter," and link loops become traps), or excluding them and documenting the fact. Hard links (-H) and device/special files (the D in -a) only make sense between like systems; across platforms, drop them. If your job leans on hard-link snapshot patterns, keep the snapshot storage on a Unix filesystem — see mirroring patterns.
And one reassurance, because juniors reasonably ask: rsync never converts line endings. It copies bytes; a file with Unix LF endings arrives with Unix endings on Windows, and vice versa. If a config file "breaks" after syncing to Windows, the file is intact — the editor opening it is the variable. (Text-mode conversion is an FTP-era concept — that protocol's ASCII transfer mode really did rewrite line endings in transit, a behavior with its own long history of mangled files, covered in our FTP protocol series.)
Setups That Hold Up
Before blessing any cross-platform pairing, put it through the run-twice test on a small canary tree: a few hundred representative files, synced once, then immediately synced again with -i. The second run should print nothing. Every line it does print is an attribute the destination cannot hold or a timestamp it cannot keep — the exact list of flags you still need to adjust, delivered in thirty seconds instead of discovered across a month of slow nightly jobs. Only when the second run is silent does the pairing graduate to real data. With that ritual in place, these are the arrangements that survive contact with production:
| Scenario | Setup that works | Key flags / caveats |
|---|---|---|
| Windows machine ↔ Linux server | rsync under WSL (or a Cygwin-based build) over SSH | Expect NTFS metadata translation at /mnt/c; test attribute flags with a small tree first |
| Linux server → NAS appliance | Use the NAS's own SSH or rsync service, not an SMB mount | Syncing into an SMB mount loses ownership and trips timestamp quirks; talk to the NAS natively and it is just Unix-to-Unix |
| Anything → FAT/exFAT drive | Foreign-filesystem recipe | -rltDv --no-perms --no-owner --no-group --modify-window=2; no symlinks |
| macOS ↔ Linux | rsync over SSH, names translated | --iconv=utf-8-mac,utf-8 for accented names; case-collision check on the source |
| Windows ↔ Windows (LAN/SMB) | robocopy /MIR, scheduled natively |
No delta transfer, no SSH — fine on a LAN, wrong tool over a WAN |
One boundary case deserves its own paragraph because it comes up constantly in mixed shops: the Windows end shouldn't be a shell at all. When the Windows side's real job is receiving files from partners or distributing them to internal consumers — with named users, quotas on trust, and logs someone can audit — bolting sshd-plus-rsync onto it is more Unix than the situation wants. A managed transfer server speaking SFTP, such as Sysax Multi Server, is the Windows-native shape for that role, and a scheduler like Sysax FTP Automation covers the recurring push/pull jobs on the Windows side without a compatibility layer in sight. rsync then handles the Unix-to-Unix legs, and each platform runs tooling that treats it as a first-class citizen.
The Version to Tell a Colleague
rsync assumes Unix; every cross-platform gotcha is one of its assumptions failing politely. On Windows, rsync means WSL or a Cygwin-based build — or means robocopy, if both ends are Windows anyway. Against filesystems without Unix attributes, subtract the attribute flags (-rltD plus --no-perms --no-owner --no-group) instead of collecting ten thousand errors. When everything re-copies every run, suspect timestamp precision and reach for --modify-window=2. Watch for names Windows forbids, case collisions that overwrite silently, and macOS accent encoding (--iconv). Symlinks and hard links travel only between like systems, and rsync never touches line endings. Test every new pairing with a small tree and a dry run before trusting it with the real one.
The flag mechanics behind these recipes live in rsync flags that matter, and the preview-first discipline that makes cross-platform surprises cheap is the whole subject of dry runs and verification. For the transport side of mixed-platform jobs, see rsync over SSH vs the daemon.
Frequently Asked Questions
Can I run rsync natively on Windows?
Why does rsync re-copy every file to my USB drive or NAS on every run?
What causes the wall of "operation not permitted" errors?
Does rsync convert line endings between Unix and Windows?
Is robocopy basically rsync for Windows?
Why did some accented filenames duplicate after syncing from a Mac?
From the Sysax team: we build secure file transfer software for Windows — Sysax Multi Server, an FTP, FTPS, SFTP, and HTTPS server, and Sysax FTP Automation for scheduled, scripted transfers. Free trials are on the download page.
