Cheat Sheet

rsync Commands

rsync copies files locally or over SSH and only sends the parts that changed, which makes it the tool for backups, deployments, and moving a server. Read the trailing slash section first, because that one character decides what every other command here does.

Last updated September 11, 2026

Basic Copy

(archive) is the flag you almost always want: recursive, keeps permissions, times, owner, group, symlinks, and devices. The rsync command builder assembles one of these interactively.

CommandWhat it does
Copy the contents of into , showing each file
Copy the directory itself, so you end up with
Copy one file
With a per-file progress bar
With one overall progress line for the whole transfer
Dry run: show what would be copied, change nothing
Print a summary of bytes sent and files transferred
Skip files that are newer on the destination
Only copy files that do not exist on the destination
Only update files that already exist there
Create missing destination directories (rsync 3.2.3 and newer)

with no prints nothing on success, which is what you want in cron.

Trailing Slash

The one thing about rsync that catches everyone, and the reason behind most "it copied into the wrong folder" and " wiped my directory" reports. A trailing slash on the source path means "the contents of this directory". No slash means "this directory itself". The slash on the destination changes nothing at all.

CommandResult
,
,
Same as the first row
Same as , occasionally seen in scripts
The contents of into
Creates

Read as including hidden files, and as "put a copy of this directory in there". Three consequences worth knowing before you run anything else on this page:

  • With , prunes everything in , while only prunes and leaves the rest of alone. Getting that backwards is how people empty a directory.
  • Tab completion appends the slash for you, so the command you typed and the command you tabbed are not the same command. Check the source path before pressing Enter, and dry run with .
  • is not the same as . The glob is expanded by the shell, which skips dotfiles, so silently leaves out and .

Over SSH

Remote paths are . rsync must be installed at both ends. It runs over SSH by default, so keys, aliases, and agent forwarding all apply; see the SSH cheat sheet.

CommandWhat it does
Local to remote (push)
Remote to local (pull)
Non-standard SSH port
Specific key
Port and key
Use a Host alias from , which holds the port and key
Through a jump host with
Write as root on the remote side (needs passwordless sudo for rsync)
Quote remote paths with spaces
Same, without the shell interpreting the remote path ()
Several sources from the same host
Give up if no data moves for 60 seconds

Copy between two remote servers: rsync cannot take two remote paths in one command. Log into one and push to the other, or forward your agent so the first server can authenticate to the second.

ssh -A user@server1 'rsync -avz /var/www/ user@server2:/var/www/'

A connection that fails with exit code 255 is an SSH problem, not an rsync one. See Connection refused and Permission denied (publickey).

Flags Explained

Paste any command into the rsync flag explainer to decode it.

FlagWhat it does
, Same as : recursive, symlinks as symlinks, permissions, times, group, owner, devices
List each file as it is copied ( for more, for none)
Compress in transit; helps over slow links, wastes CPU on a LAN or for already-compressed files
Human-readable sizes in output
: keep partial files for resuming, and show progress
, Show what would happen without doing it
, Skip files that are newer on the destination
, Compare by checksum instead of size and time (slow, but catches same-size edits)
Recursive; part of
Copy symlinks as symlinks; part of
Copy the file a symlink points to instead of the link
, , , Preserve permissions, times, owner, group; all part of
Preserve hard links (not in )
, Preserve ACLs and extended attributes (not in )
Stay on one filesystem; skip mounted volumes
Itemize changes: a code per file showing what differed
The remote shell command and its options
Remove destination files that no longer exist in the source
Skip matching files
Cap bandwidth, in KiB/s by default ( for 5 MiB/s)
One progress line for the whole run
Transfer summary at the end
Write what happened to a file

is the standard remote copy, the standard local one, and when the link might drop.

Dry Run

Run every new rsync command with first, and always before .

CommandWhat it does
List what would be transferred
Also list what would be deleted, shown as
Itemized dry run: one line per change with a code like
Count and total size without copying

In itemized output, is a file being sent, a directory being created, a deletion, and the dots after the type mark which attributes changed ( size, time, permissions, owner).

Exclude and Include

Patterns match relative to the top of the transfer. A trailing matches directories only, a leading anchors the pattern to the transfer root, and does not cross a ( does).

CommandWhat it does
Skip every at any depth
Skip files by extension
Skip a directory (trailing slash means directories only)
Skip only the top-level , not
Several patterns; repeat the flag
Same, using bash brace expansion (no spaces inside the braces)
Patterns from a file, one per line
Copy only files, keeping the directory tree
Only files in the top level (no include, so no descent)
As above, but prune empty directories
Copy only the paths listed in a file, relative to
Skip files over 100 MiB ( for the other end)
Also delete files on the destination that an exclude now covers

Include and exclude rules are checked in order and the first match wins, which is why comes before . An exclude that "does not work" is almost always anchored wrong: does not match when the transfer root is , but does.

Delete and Mirror

makes the destination match the source exactly, including removing files. Combined with a wrong path or a missing trailing slash it can empty a directory, so dry run first.

CommandWhat it does
Mirror: copy changes and remove anything not in
Preview the deletions first
Refuse to delete more than 100 files (exit code 25 when it trips)
Delete after the transfer, not before; safer for a live site
Delete first, when the destination is short on space
Move deleted and overwritten files into a dated directory instead of losing them
Delete each source file after it is copied (a move); empty source directories remain
Mirror but leave the destination's alone (excludes are protected from deletion by default)

only deletes inside directories that are part of the transfer. (no trailing slash) syncs and never touches the rest of .

Progress and Resume

CommandWhat it does
Keep partial files and show per-file progress
One overall percentage, speed, and time remaining
Keep partials in a hidden directory rather than in place
Run again after an interruption; partial files continue from where they stopped
Append to a partially transferred file and checksum the result; for single large files only
Cap at about 5 MB/s so you do not saturate the link
Cap at 50 MiB/s
Abort if the transfer stalls for two minutes
Run in the background and survive logout

For a transfer that takes hours, start it inside or so a dropped SSH session does not kill it; see the tmux cheat sheet. If the SSH session itself keeps dropping, see Broken pipe.

Speed and Large Transfers

One rsync run is single-threaded and uses one SSH connection, so the way to go faster is usually to do less work or to run several transfers at once.

CommandWhat it does
Copy whole files instead of computing deltas; faster on a LAN or between local disks
Write changes into the existing file rather than a temp copy (VM images, tight disk space)
Keep sparse files sparse instead of writing out the holes
Do not spend CPU compressing files that are already compressed
Cheap compression when the CPU, not the link, is the limit
Use the fast xxHash checksum instead of MD5 (rsync 3.2 and newer)
Build the whole file list up front so progress totals are real ()
Four transfers in parallel, one per top-level entry

On trees with hundreds of thousands of small files, building and comparing the file list costs more than the transfer, and no flag fixes that. Split the tree and run several rsyncs, or the directory and copy one stream.

Compare and Verify

rsync decides what to send from size and modification time, not from file contents. These commands compare without copying, or force a content comparison.

CommandWhat it does
List everything that differs, including files has and does not
The same as an itemized list, one line per difference
Compare by checksum, catching files with identical size and time but different contents
Recopy anything whose contents differ, ignoring timestamps
Treat same-size files as identical, for filesystems with unreliable times
Copy only files that differ from a third reference directory
Same, but take unchanged files from the reference locally instead of over the network
Same, but hard link unchanged files from the reference
Custom output: change code, name, and size per file

rsync already checksums every file it transfers and retries any that fails, so a run that exits wrote correct data. is about deciding what to send, not about verifying what arrived.

Permissions and Ownership

CommandWhat it does
Preserve permissions, times, group; owner too when running as root
Set a fixed owner and group on everything copied (needs root at the destination)
Set directories to 755 and files to 644 as they land
Copy without permission or ownership bits (exFAT, SMB shares)
Same for owner and group only, keeping permissions
Recursive, links, and times, but no permissions or ownership at all
Keep UID and GID numbers instead of mapping names (full system copies)
Also copy ACLs and extended attributes
Compare by size only, for filesystems that cannot store exact times
Treat times within one second as equal (FAT stores 2-second stamps)

Preserving owner means rsync on the receiving end runs as root, either because you are root or via . Otherwise files are owned by the connecting user and rsync prints nothing about it.

Backups and Snapshots

is what turns rsync into a backup tool: each run is a full dated tree, but files that did not change are hard links to the previous run and cost no extra disk. The rsync command builder has a link-dest field if you would rather assemble the command than type it.

CommandWhat it does
A plain mirror backup, ACLs and extended attributes included
Dated snapshot; unchanged files are hard links to the previous one
Keep the previous version of anything overwritten or deleted
Rename replaced files in place instead of moving them aside
Offsite copy over SSH
Keep the exclusion list in a file you can version
Abort rather than mirror a source that has gone missing

A hard-linked snapshot set shares inodes, so every snapshot of an unchanged file is the same blocks on the same disk. It protects against deleting a file, not against the drive dying. Keep one copy on another machine.

Cron and Logging

In cron there is no terminal, no ssh-agent, and a minimal , which is where nearly every "works by hand, fails in cron" report comes from.

CommandWhat it does
Write a full transfer log
Choose what each log line holds (time, operation, file, length)
Fixed key, and fail instead of prompting for a password
Skip this run if the last one is still going
Run at the lowest disk and CPU priority
Itemized change log without full verbosity
# /etc/cron.d/backup
0 3 * * * root /usr/bin/flock -n /var/lock/backup.lock /usr/bin/rsync -a --delete \
  -e "ssh -i /root/.ssh/backup_key -o BatchMode=yes" /srv/ backup@host:/srv/ \
  >> /var/log/backup.log 2>&1

Use absolute paths, a key with no passphrase (or a systemd timer with the agent available), and check rather than the output: prints nothing on success, so a silent cron job and a failed one look the same. turns a password prompt into an immediate exit 255, which is the behaviour you want when nobody is watching.

Daemon Mode

rsync can also run as its own service on TCP 873, with no SSH and no encryption. Remote paths then use a double colon or an URL, and the first component is a module name from , not a filesystem path.

CommandWhat it does
List the modules a daemon offers
The same, in the older syntax
Pull from the module
Push into it
Non-standard daemon port
Authenticate from a file (must be mode 600)
The same without a prompt, from the environment
# /etc/rsyncd.conf
[backup]
path = /srv/backup
read only = false
auth users = deploy
secrets file = /etc/rsyncd.secrets
hosts allow = 10.0.0.0/24

One colon means SSH, two colons mean the daemon. Mixing them up gives or a hang on port 873. Daemon traffic is plaintext and the passwords are not SSH keys, so keep it on a private network and use the default SSH transport over the internet.

Common Recipes

Back up a home directory to an external drive:

rsync -avh --delete --exclude '.cache/' --exclude 'node_modules/' ~/ /Volumes/Backup/home/

Mirror a website from a server, keeping a rotating set of dated snapshots that share unchanged files via hard links:

rsync -avz --delete --link-dest=/backup/www/latest user@host:/var/www/ /backup/www/$(date +%F)/
ln -sfn /backup/www/$(date +%F) /backup/www/latest

Deploy a build, excluding what should not ship, then reload:

rsync -avz --delete --exclude '.git' --exclude '.env' --exclude 'node_modules' \
  ./dist/ deploy@host:/var/www/app/ && ssh deploy@host 'sudo systemctl reload nginx'

Copy only files changed in the last day:

find src -type f -mtime -1 -printf '%P\n' | rsync -av --files-from=- src/ dest/

Sync two directories both ways (run once in each direction; rsync is one-way, keeps the newer copy):

rsync -avu a/ b/ && rsync -avu b/ a/

Move a whole server's data directory to a new host over a slow link, resumable:

rsync -avzP --bwlimit=20m -e "ssh -p 2222" /srv/data/ root@newhost:/srv/data/

Clone a disk or root filesystem to another mounted volume, staying off , , and other mounts:

sudo rsync -aAXHvx --numeric-ids --exclude={'/dev/*','/proc/*','/sys/*','/tmp/*','/run/*','/mnt/*','/media/*','/lost+found'} / /mnt/newdisk/

Windows and macOS

rsync is a Unix program. Windows has no native version, and the one Apple ships is not the rsync the manual describes.

SituationWhat to do
Windows 10 or 11, then ; your drives are under
Git BashNo rsync included; MSYS2 () or Cygwin provides one
Windows to a Linux serverRun it from WSL:
Windows, local copy only mirrors like , but cannot talk to an SSH server
macOS 14 and earlierShips rsync 2.6.9 from 2006; puts 3.x in
macOS 15 and newerThe bundled is openrsync, which rejects and other 3.x flags
Apple metadataApple's rsync takes for resource forks; the Homebrew build uses instead
Copying to exFAT, NTFS, or an SMB shareAdd or everything recopies every run

Check which one you have with . Under WSL, a copy from cannot preserve Unix permissions, so expect to warn and use there too.

rsync vs scp

rsyncscp
Second run of the same copySends only changesSends everything again
Resume after a dropStart over
Delete extra files at the destinationNo
Exclude patternsYesNo
Installed by defaultUsually, not on minimal images or stock WindowsWherever SSH is
One file, once (slightly quicker to type)

OpenSSH's has used the SFTP protocol under the hood since 9.0, which fixed its security problems but did not add any of the above.

Two other comparisons come up often. is the tool when one side is cloud storage (S3, Google Drive, OneDrive, Backblaze), which rsync cannot speak at all. is the Windows built-in that mirrors local and SMB paths with , but has no SSH transport and no delta algorithm.

Exit Codes

rsync reports its result in the exit code ( right after it runs), which is what a cron job or a script should check.

CodeMeaning
Success
Syntax or usage error; a flag is wrong or unsupported by the remote rsync
Protocol incompatibility; very old rsync on one end
Errors selecting input files; usually a source path that does not exist
Error starting the client-server protocol
Socket I/O error; the connection dropped or the remote host closed it
File I/O error; disk full or a read failure
Error in the rsync protocol data stream; remote disk full, rsync missing on the remote, or a login script printing to stdout
Errors with program diagnostics
Killed by a signal (Ctrl+C)
Partial transfer due to error; some files skipped, usually permissions
Partial transfer due to vanished source files; benign for live directories
Stopped by
Timeout in data send or receive ()
Timeout waiting for the daemon connection
SSH failed to connect; not an rsync error at all

Gotchas

  • with the wrong trailing slash or a mistyped destination removes real files. first, every time.
  • rsync must exist on the remote host. from the far end and exit code 12 or 127 mean there, not here.
  • A on the remote that prints something (a banner, a , an unguarded ) corrupts the protocol stream and gives or code 12. Guard it with at the top.
  • macOS ships an old rsync 2.6.9, and macOS 15 replaced it with openrsync, which lacks flags such as . gives you 3.x; on Windows, rsync comes with WSL, Cygwin, or MSYS2, not with Git Bash by default.
  • slows down a LAN copy and does nothing for files that are already compressed (images, video, archives). Use it over the internet, drop it locally.
  • Copying to a drive formatted exFAT, FAT32, or NTFS from Linux recopies everything each run because the filesystem cannot store the same times and permissions. Add or .
  • dies in zsh with when nothing matches, because zsh refuses an unmatched glob instead of passing it through the way bash does. Pick files with rather than a shell glob. On a remote source the glob is expanded by the remote shell, so it has to be quoted: .
  • means the remote path is missing its colon ( instead of ) or you gave two remote paths in one command.
  • Changing only a file's permissions or owner does not change its size or time, so a plain run does not resend it but does update the attributes. Content edits that keep the same size and time (rare, but and some build tools do it) need to be noticed.

rsync FAQ

Does rsync copy or move files?

It copies. The source is untouched after a run, and running the same command again sends only what changed since. To get a move, add --remove-source-files, which deletes each file from the source after it has been transferred and verified. That flag leaves the now-empty directories behind, so follow it with find src -type d -empty -delete if you want them gone. There is no flag that removes source directories in one step.

When should you use a trailing slash with rsync?

On the source, whenever you want the contents of that directory rather than the directory itself. rsync -av src/ dest/ gives you dest/file1. rsync -av src dest/ gives you dest/src/file1. The slash on the destination makes no difference either way. Two things make this worse than it sounds. Shell tab completion appends the slash for you, so a path you tabbed and the same path typed by hand are not the same command. And the slash decides what --delete removes: src/ dest/ prunes the whole of dest, while src dest/ only prunes dest/src. Dry run with -n before any --delete.

Does rsync overwrite existing files?

Yes, silently, whenever the source file differs in size or modification time, and that includes overwriting a destination copy that is newer. It never asks. The write itself is safe: rsync builds a temporary file in the destination directory and renames it into place at the end, so nothing ever reads a half-written file and an interrupted transfer leaves the old version intact. Use --update to skip anything newer on the destination, --ignore-existing to leave files that are already there alone, or --backup --backup-dir=DIR to keep the old copy. --inplace turns off the temporary-file step, which saves disk space on large images but leaves a corrupt file behind if the run dies.

Does rsync delete files in the destination?

Only when you pass --delete. A normal run adds and updates, and removes nothing. Even with --delete, rsync only prunes directories that are part of the transfer, so rsync -av --delete src dest/ (no slash on the source) cannot touch anything outside dest/src. Files matched by an --exclude are protected unless you also pass --delete-excluded. The guard worth putting in any scripted mirror is --max-delete=N, which makes rsync stop and exit 25 rather than remove more than N files. That is what saves you on the morning the source drive fails to mount and looks empty.

Why is rsync faster than scp?

On a repeat copy, because it sends only what changed. rsync compares size and modification time, skips the files that match, and for a file that does differ sends just the blocks that differ, so re-syncing a 50 GB tree after one edit moves a few megabytes. On a first copy of new data there is nothing to skip and rsync can be the slower of the two: it builds a file list before it starts, which hurts on trees with hundreds of thousands of small files, and -z burns CPU on data that is often already compressed. Between two local disks, add -W to skip the delta calculation entirely.

Related cheat sheets