Linux command: rsync
Sooner or later, every Linux user needs to copy a lot of files from one place to another: a website to a server, a home folder to a backup drive, a project between two machines. You can do it with cp or scp, but the moment you run the same copy a second time you feel the waste, because they copy everything again, every time. rsync was built to fix exactly that. It looks at what is already at the destination and transfers only the parts that changed, which makes it the quiet backbone of backups, deployments, and server migrations all over the world.
1. The Basics
The job of rsync is to make a destination look like a source, and to do it with the least possible work. You point it at a source and a destination, and it copies the files across. So far that is just cp. The difference shows up the second time you run it: rsync compares the two sides first and skips everything that is already identical, so a repeated sync of a large folder can finish in seconds even when only one file changed.
That single idea, "copy only the difference", is what makes rsync special. It works on one machine, between two disks, and across the network over SSH, all with the same command. It can preserve permissions, ownership, timestamps, and symbolic links exactly. It can delete files at the destination that no longer exist at the source, so the two sides truly match. And if the connection drops halfway through a huge transfer, it can pick up where it left off instead of starting over.
The command reads left to right, just like cp: the last argument is the destination, and everything before it is a source.
$ rsync -a Documents/ /backup/Documents/
The tool that copies only what changed. Behind that simple promise sits a clever differencing algorithm, a small but sharp set of rules about trailing slashes, and enough options to run anything from a one-line backup to a full server migration.
This article starts with the simplest local copy and builds up to remote transfers over SSH, mirroring with deletes, excluding files, and the delta-transfer trick that lets rsync update a large file by sending only a few kilobytes. By the end you will understand not just how to run rsync, but why it does so little work.
Back to topThe right mental model:
rsyncdoes not "copy files", it synchronises two locations. Every run asks the same question, "what is different?", and moves only that. The danger, as withcp, is always at the destination.
2. Where the Name Comes From
The name rsync is a blend of remote and sync (short for synchronise). It was designed from the start to keep files in sync between a local and a remote machine, and the name says exactly that.
rsync = Remote SYNC
It replaced an older, dumber tool called rcp ("remote copy"), which copied whole files over the network with no cleverness at all. The extra letters in rsync are a small promise: this is not just a remote copy, it is a remote sync that pays attention to what is already there. The name has stuck for nearly thirty years because it still describes the tool perfectly.
3. A Short History
Unlike cp and ls, which reach back to the first days of Unix, rsync is a relatively modern tool. Andrew Tridgell and Paul Mackerras released the first version in 1996. The heart of it is the rsync algorithm, a method for updating a file over a slow network link by sending only the changed pieces. Tridgell described it in a 1996 technical report and later in his PhD thesis. That algorithm is still what makes rsync worth using today.
| Era | Milestone |
|---|---|
| 1996 | Andrew Tridgell and Paul Mackerras release rsync, replacing rcp |
| Late 1990s | The rsync delta-transfer algorithm is published and refined |
| 2008 | rsync 3.0 adds incremental recursion: it starts transferring before scanning the whole tree |
| 2020 | rsync 3.2 adds faster checksums (xxhash) and better compression (zstd) |
| Today | rsync 3.2.x, protocol 31, ships on virtually every Linux distribution |
Because the algorithm was the interesting part, rsync became more than a command: it is also a lightweight protocol. You will meet it as a plain local copy, as a transfer tunnelled over SSH, and as a network service (an "rsync daemon") that public mirrors use to distribute software. This article focuses on the two you will use most: local copies and copies over SSH.
Back to topMost classic Unix tools do one job in one place.
rsyncis unusual because it also carries a network protocol and a differencing algorithm inside a single command, which is why one tool can back up a laptop, deploy a website, and mirror an archive.
4. Simple Use Cases
4.1 The Simplest Possible Sync
Give rsync a source, a destination, and the -a flag, and it copies the source into the destination. In the examples below, the line that starts with $ is what you type; the lines under it are what rsync prints back.
$ rsync -a project/ /backup/project/
Nothing is printed, which is the normal Unix way of saying "it worked". The -a flag (short for archive) is the flag you will use almost every time. It means "copy recursively and keep everything as it is": subfolders, permissions, timestamps, and symbolic links. For now, treat rsync -a as the everyday starting point; section 5 opens it up.
4.2 The Trailing Slash Rule
This is the one rule about rsync that everyone must learn, because it quietly changes where your files land. A trailing slash on the source means "the contents of this directory". No trailing slash means "this directory itself".
$ rsync -a src/ dst/ # copies the CONTENTS of src into dst
$ rsync -a src dst/ # copies the DIRECTORY src, giving dst/src
You can prove it to yourself in a few seconds:
$ rsync -a src/ dst1/
$ ls dst1
a.txt b.txt sub
$ rsync -a src dst2/
$ ls dst2
src
The first command fills dst1 with the files that were inside src. The second one places src as a subfolder inside dst2. When a backup script mysteriously produces backup/project/project, a missing or extra slash is almost always the reason. The slash on the destination does not matter in the same way; it is the source slash that decides the layout.
Here is a trap I ran into myself, backing up my own home folder to an external disk, and it can quietly ruin a backup. It is tempting to reach for a wildcard instead of a trailing slash, which is exactly what I did:
$ rsync -av /home/peter/* /media/peter/writable/ # looks right, is not
The problem is that the * is expanded by the shell, before rsync ever runs, and a plain * does not match names that begin with a dot. So every hidden file and folder in the home directory (.bashrc, .ssh, .config, .gnupg) is silently left out. You can see it happen with echo:
$ echo /home/peter/*
/home/peter/Documents /home/peter/notes.txt # notice: no .bashrc, no .ssh
What makes this dangerous is that it looks like it worked. The visible files and folders copy across fine, and hidden files inside those folders come along too, because rsync recurses into any directory the shell did hand it. Only the top-level dotfiles disappear, which are exactly the ones that hold your settings, keys, and credentials. In my case the external drive ended up with all my documents but none of my configuration, and I only noticed later. The fix is the trailing-slash form, which passes the directory itself to rsync and lets it walk the whole tree, hidden files and all:
$ rsync -av /home/peter/ /media/peter/writable/ # copies EVERYTHING, dotfiles included
Whenever you copy "a whole folder", prefer source/ over source/*. The trailing slash asks rsync to select the files; the wildcard asks the shell, and the shell hides your dotfiles.
4.3 Looking Before You Leap: -v and -n
Because rsync can delete and overwrite, it is worth seeing what it will do before it does it. Two flags make it talkative and cautious.
The -v flag (short for verbose) lists each file as it is transferred:
$ rsync -av src/ dst/
sending incremental file list
./
a.txt
b.txt
sub/
sub/c.txt
sent 308 bytes received 88 bytes 792.00 bytes/sec
total size is 17 speedup is 0.04
The -n flag (short for dry-run) is the safety net. It shows exactly what would happen, but changes nothing. Combine it with -v and you get a preview:
$ rsync -avn src/ dst/
sending incremental file list
a.txt
b.txt
sub/c.txt
sent 163 bytes received 32 bytes 390.00 bytes/sec
total size is 17 speedup is 0.09 (DRY RUN)
Notice the (DRY RUN) marker at the end. Get into the habit of running any new or risky rsync with -avn first, reading the list, and only then removing the n. This single habit prevents most rsync accidents.
4.4 Readable Sizes: -h
Raw byte counts in the summary are hard to read. The -h flag (short for human-readable) turns them into K, M, and G:
$ rsync -ah --stats bigfolder/ /backup/bigfolder/
...
Total file size: 1.42G bytes
Total transferred file size: 12.043M bytes
That second number is the point of rsync in one line: the folder is 1.42 GB, but only 12 MB actually changed, so only 12 MB was sent.
5. Moderate Use Cases
5.1 What -a Really Means
The archive flag is not magic; it is a bundle of smaller flags. Knowing what is inside it helps you understand what rsync keeps and what it drops.
-a = -rlptgoD
| Letter | Short for | Keeps |
|---|---|---|
-r |
recursive | Descends into every subdirectory |
-l |
links | Copies symbolic links as links, not as their targets |
-p |
perms | Preserves permissions (the mode bits) |
-t |
times | Preserves modification times |
-g |
group | Preserves the group |
-o |
owner | Preserves the owner (needs root) |
-D |
devices | Preserves device files and special files (needs root) |
Keeping timestamps matters more than it looks. On the next run, rsync decides whether a file changed by comparing size and modification time. If -t did not preserve the time, every file would look new next time and rsync would copy everything again, throwing away its main advantage. This is why you should almost always include -a (or at least -t) rather than a bare -r.
One thing to notice: -a does not include -A (ACLs), -X (extended attributes), or -H (hard links). Those are separate, and section 6 covers them.
5.2 Copying Over the Network: SSH
The "r" in rsync is for remote, and this is where it shines. To copy to another machine, put user@host: in front of the remote path. By default rsync tunnels the transfer over SSH, so it is as secure as a normal SSH login.
$ rsync -a site/ This email address is being protected from spambots. You need JavaScript enabled to view it. :/var/www/site/ # push to a server
$ rsync -a This email address is being protected from spambots. You need JavaScript enabled to view it. :/var/www/site/ site/ # pull from a server
Whichever side has the host: prefix is the remote one. To pass options to SSH, such as a non-standard port or a specific key, use the -e flag (short for rsh, the remote shell):
$ rsync -a -e "ssh -p 2222" site/ This email address is being protected from spambots. You need JavaScript enabled to view it. :/var/www/site/
5.3 Compressing the Transfer: -z
The -z flag (short for compress) squeezes the data before sending it over the wire. On a slow or metered network this can speed up transfers of text, code, and other compressible files a lot.
$ rsync -az site/ This email address is being protected from spambots. You need JavaScript enabled to view it. :/var/www/site/
Use -z for remote transfers. Skip it for local copies and for folders full of already-compressed files (JPEGs, videos, ZIPs, most media): compressing them again just burns CPU for no gain.
5.4 Resuming and Watching Progress: -P
A big transfer over a shaky connection can fail halfway. The -P flag turns on two helpers at once: --partial (keep the half-transferred file so the next run can finish it) and --progress (show a live progress line per file).
$ rsync -aP bigfile.iso This email address is being protected from spambots. You need JavaScript enabled to view it. :/data/
bigfile.iso
1,048,576 32% 12.4MB/s 0:00:04
Without --partial, an interrupted transfer of a large file starts again from zero next time. With it, rsync keeps what it already sent and resumes. For any transfer that takes more than a few seconds, -P is a friend.
The progress line from -P shows one bar per file. When you are copying thousands of files and want a single bar for the whole job, use --info=progress2 instead:
$ rsync -a --info=progress2 src/ dst/
10,000,025 100% 1.16GB/s 0:00:00 (xfr#6, to-chk=0/8)
The to-chk=0/8 at the end counts how many files are still left to check, so you can watch the run wind down to zero.
5.5 Making the Destination Match Exactly: --delete
By default rsync only adds and updates; it never removes anything from the destination. So if you delete a file at the source, the old copy quietly lingers at the destination. The --delete flag makes the destination a true mirror by removing files that no longer exist at the source.
$ rsync -avn --delete src/ dst/
sending incremental file list
deleting old-report.txt
...
(DRY RUN)
This is powerful and sharp. --delete plus a wrong path or a missing trailing slash can wipe files you meant to keep. Always run it with -n first and read the deleting lines before you trust it. Used carefully, it is exactly how you keep a backup or a deployed website perfectly in step with the source.
6. Advanced Use Cases
6.1 Leaving Things Out: --exclude
Real projects contain things you do not want to copy: caches, logs, dependency folders, temporary files. The --exclude option skips anything matching a pattern, and you can give it more than once.
$ rsync -a --exclude '*.log' --exclude 'cache/' src/ dst/
For a long list, keep the patterns in a file and read them with --exclude-from:
$ rsync -a --exclude-from='ignore.txt' src/ dst/
The patterns work a lot like .gitignore rules: a trailing slash means "a directory", a leading slash anchors the pattern to the top of the transfer, and * matches within one path level. There is a matching --include for the rarer case where you want to exclude almost everything but keep a few things back.
6.2 Extra Metadata: -A, -X, and -H
The archive flag -a keeps the everyday metadata, but three important things are left out on purpose because they cost extra work.
| Flag | Short for | Preserves |
|---|---|---|
-A |
acls | Access control lists (fine-grained permissions) |
-X |
xattrs | Extended attributes (SELinux contexts, custom metadata) |
-H |
hard-links | Hard links, keeping shared files shared instead of duplicated |
When you are backing up a whole system or migrating a server and want the copy to be truly faithful, the common recipe is rsync -aAXH. For an ordinary folder of documents, plain -a is enough.
6.3 Comparing by Content, Not Clock: -c
By default rsync decides a file is unchanged when its size and modification time both match. That is fast, but it can be fooled: a file edited and saved back to the same size within the same second, or a file whose timestamp was reset, can slip through. The -c flag (short for checksum) makes rsync read every file on both sides and compare a strong checksum instead.
$ rsync -avc src/ dst/ # compare by checksum, ignore size and time
This is slower, because it has to read all the data, but it is the honest way to answer "are these two folders really identical?" It is worth knowing about for verification runs, though not for everyday syncs.
6.4 Reading the Itemized Output: -i
The -i flag (short for itemize-changes) prints a compact code beside each file that tells you exactly why it was transferred. It looks cryptic at first, but it repays a minute of study.
$ rsync -ain src/ dst/
>f+++++++++ newfile.txt
>f..t...... changed.txt
.d..t...... sub/
Read the string left to right. The first character is the action: > means a file is being received (sent to the destination), and . means the item itself is not being updated, only an attribute. The second character is the type: f for a file, d for a directory. The rest are the reasons: c checksum, s size, t time, p permissions, o owner, g group. A row of +++++++++ means the file is brand new. So >f..t...... reads as "send this file because its time differs", and >f+++++++++ reads as "this file is new".
6.5 Being a Good Neighbour: --bwlimit and --stats
A full-speed rsync can saturate a network link and slow everything else down. The --bwlimit option caps the transfer rate, which is polite on a shared or production connection.
$ rsync -az --bwlimit=5m site/ This email address is being protected from spambots. You need JavaScript enabled to view it. :/var/www/site/ # cap at 5 MB/s
The --stats option prints a detailed report at the end, which is useful in scripts and logs to see how much really moved:
$ rsync -a --stats src/ dst/
Number of files: 5 (reg: 3, dir: 2)
Number of regular files transferred: 3
Total file size: 25 bytes
Total transferred file size: 25 bytes
...
6.6 Writing in Place and Sparse Files: --inplace and -S
By default rsync writes each updated file to a temporary copy and then renames it over the old one. That is safe, because an interrupted transfer never leaves a half-written file, but it needs room for both versions and it breaks any hard links pointing at the original. The --inplace option writes the changes straight into the existing file instead. It saves space, keeps hard links intact, and works well with the delta algorithm for large files that change only a little, such as database dumps, VM images, and append-only logs.
$ rsync -a --inplace bigdb.sql This email address is being protected from spambots. You need JavaScript enabled to view it. :/backups/
The trade-off is safety: if the transfer is interrupted, the destination file is left partly updated, so use --inplace only where that risk is acceptable.
The -S flag (short for sparse) handles files that contain long runs of zero bytes, such as disk images and some database files. It writes those runs as real "holes" in the filesystem rather than as blocks of zeros, so the copy can take far less space than the file's apparent size. (Older rsync versions, before 3.1.3, refused to combine -S with --inplace; current versions allow it.)
6.7 Beyond SSH: The rsync Daemon
SSH is the usual way to reach another machine, but rsync can also talk to a dedicated rsync daemon running on the far side. This is the mode the big public mirrors use to hand out Linux distributions to thousands of people at once. You recognise it by a double colon (or an rsync:// URL) and a module name instead of a filesystem path:
$ rsync -av rsync://mirror.example/debian/ /srv/mirror/debian/
$ rsync -av mirror.example::debian/ /srv/mirror/debian/ # same thing
A single colon (host:path) means "connect over SSH"; a double colon (host::module) means "connect to the rsync daemon". The daemon listens on port 873 and is configured in /etc/rsyncd.conf, where each module maps a name to a directory and its access rules. The daemon can be faster than SSH because it skips the encryption overhead, but that also means the traffic is unencrypted, so it suits public, read-only mirrors far more than private backups. For anything sensitive, stay with SSH.
7. Something Most Users Do Not Know
7.1 The Delta-Transfer Algorithm: Sending Only the Changes
Here is the idea that makes rsync famous, and that most people use every day without knowing it exists. When rsync updates a file that already exists at the destination, it does not resend the whole file. It sends only the changed pieces, even inside a single large file.
It works like this. The receiving side splits its existing copy into fixed-size blocks and computes two checksums for each block: a fast "rolling" checksum and a strong one. It sends those to the sender. The sender then slides a window byte by byte through its own newer version of the file, using the cheap rolling checksum to spot places that match a block the receiver already has. Where a block matches, rsync sends just a short "you already have this block" reference. Only the genuinely new bytes travel across the network.
This is why appending one line to a 1 GB log file and syncing it can send only a few kilobytes.
rsyncfound that almost every block was unchanged and sent references to them instead of the data.
There is a surprising twist: this clever algorithm is turned off for local copies. When both sides are on the same machine, reading the whole file to compute checksums is usually slower than just copying it, so rsync defaults to --whole-file (-W) locally and simply writes the file out. The delta algorithm earns its keep on slow network links, which is exactly what it was designed for.
7.2 Snapshot Backups with --link-dest
This is the trick behind many "time machine" style backup systems, and it feels like magic the first time you see it. The --link-dest option points rsync at a previous backup. Any file that has not changed since then is not copied at all: instead, rsync creates a hard link to the file in the old backup, which takes essentially no extra space.
$ rsync -a --delete --link-dest=/backups/monday \
src/ /backups/tuesday/
The result is a folder tuesday that looks like a complete, full backup of everything. But on disk, only the files that actually changed since Monday take new space; everything else is a shared hard link back into Monday's backup. You can keep daily snapshots for a month, each one browsable as a full copy, while using little more space than a single copy plus the changes. Delete an old snapshot and only its unique files are freed, because the rest are still linked from newer ones.
7.3 Knowing Where rsync Stops
Part of expertise is knowing when another tool fits better. rsync is superb at syncing file trees, but it is not the answer to everything.
| Need | Use | Why |
|---|---|---|
| A quick one-off local copy | cp |
Less to type, and no checksum overhead for a single copy |
| A single file to a server, once | scp |
Simple when there is nothing to compare or resume |
| Bundle a tree into one archive | tar |
Packs the whole structure into a single file for storage or transfer |
| Version history of a project | git |
Tracks every change over time, not just the latest state |
| Clone a whole disk or partition | dd |
Copies raw blocks, including the filesystem itself |
| Deduplicated, encrypted backups | borg / restic |
Store each unique block once and encrypt it, so a month of snapshots stays small |
| Sync to cloud storage | rclone |
Speaks the APIs of S3, Google Drive, and dozens of other providers, with an rsync-like feel |
| Continuous two-way sync | Syncthing |
Keeps folders on several devices in step automatically in the background, both directions |
The last three are modern tools built for jobs rsync was never meant to do: rsync is one-directional and stateless, so it does not deduplicate across backups, speak cloud APIs, or merge changes from two sides. Learn rsync anyway. Once you are comfortable with it, it quietly replaces a whole shelf of copy scripts, because it is the same command whether you are backing up a laptop, deploying a website, or moving a server to new hardware.
8. Best Practices
- Preview with
-avnfirst. For any new or risky command, especially with--delete, run it as a dry run, read the list, and only then remove then. - Master the trailing slash.
src/means "the contents",srcmeans "the folder itself". This one rule decides where every file lands. - Use
-aas your default, and add-zfor remote transfers,-Pfor large ones, and-AXHwhen you need a truly faithful system copy. - Be careful with
--delete. It makes the destination match the source exactly, including removing files. A wrong path can erase real data, so dry-run it every time. - Keep timestamps. Always preserve modification times (part of
-a), orrsyncwill resend everything on the next run and lose its whole advantage. - Reach for the manual without hesitation. The
rsyncmanual is long because the tool is deep. Typingman rsyncthe moment a question comes up is the habit that separates beginners from professionals.
$ man rsync # the full manual page (long, and worth it)
$ rsync --help # a quick flag summary
Back to top9. Common Mistakes
9.1 Three Common Myths
| Myth | Reality |
|---|---|
"rsync src dst and rsync src/ dst are the same." |
The trailing slash on the source changes the layout: contents versus the folder itself. |
"rsync removes deleted files automatically." |
It never deletes unless you add --delete. By default it only adds and updates. |
"rsync always sends only the changed bytes." |
The delta algorithm is off for local copies and for brand-new files, which are sent whole. |
"rsync dir/* dst/ copies everything in dir." |
The shell expands * and skips top-level dotfiles. Use dir/ so rsync selects the files itself, hidden ones included. |
9.2 Other Traps to Avoid
- The double-nesting surprise. A missing trailing slash on the source produces
dst/srcinstead of fillingdst. Preview with-nif you are unsure. - Backing up a home folder with
*.rsync -a /home/peter/* dst/silently drops every top-level dotfile (.bashrc,.ssh,.config), because the shell expands the*and never matches hidden names. Use the trailing-slash form/home/peter/instead. - Running
--deletewith a typo. If the source path is wrong or empty,--deletecan strip the destination bare. Dry-run first, always. - Forgetting
-aand using bare-r. Without preserved times and permissions, the next sync copies everything again and the files arrive with the wrong ownership. - Compressing already-compressed data. Adding
-zto a transfer of videos or ZIP files wastes CPU for almost no saving. Save-zfor text and code over the network. - Trusting a same-disk sync as a backup. If the disk fails, source and destination go together. A real backup lives on separate storage, ideally off-site.
10. Summary
The rsync command looks like a fancier cp, but it is really a small synchronisation engine that happens to fit on one command line.
rsyncmeans "remote sync". It makes a destination match a source and transfers only what changed, which is why repeated syncs are fast.- It dates from 1996 and carries a differencing algorithm and a network protocol inside a single tool.
- The last argument is the destination. A trailing slash on the source (
src/) copies the contents; no slash (src) copies the folder itself. Prefersrc/oversrc/*, or the shell drops your hidden dotfiles. -a(archive) is the everyday flag: it means-rlptgoD, so it copies recursively and keeps links, permissions, times, and ownership.- Add
-zto compress remote transfers,-Pto resume and show progress, and-e "ssh ..."to control the SSH connection. --deleteturns a copy into a true mirror by removing files that no longer exist at the source. Preview it with-avnevery time.--excludeskips unwanted files;-A,-X, and-Hadd ACLs, extended attributes, and hard links for a faithful system copy.- The delta-transfer algorithm sends only the changed pieces of a file over the network, and
--link-destbuilds space-efficient snapshot backups with hard links. - Beyond SSH,
rsynccan talk to a daemon (host::module, port 873) for public mirrors, and--inplacewith-Supdates large or sparse files efficiently. - Know where
rsyncstops:cpandscpfor one-off copies,tarfor archives,gitfor history,ddfor whole disks, andborg,rclone, orSyncthingfor deduplicated, cloud, or two-way sync. - When in doubt, type
man rsync.
This is the quick reference worth keeping:
rsync -a src/ dst/ sync contents of src into dst
rsync -a src dst/ copy the folder src into dst (dst/src)
rsync -avn src/ dst/ dry run: show what would change
rsync -aP big.iso host:/data/ resume-friendly copy with progress
rsync -a --info=progress2 s/ d/ one overall progress bar for the whole job
rsync -az src/ user@host:/dst/ compressed copy over SSH
rsync -a --delete src/ dst/ mirror: also remove extra files in dst
rsync -a --exclude '*.log' s/ d/ skip files matching a pattern
rsync -aAXH src/ dst/ faithful copy with ACLs, xattrs, hard links
rsync -a --link-dest=/prev src/ /new/ space-saving snapshot backup
Small commands like rsync quietly hold up serious infrastructure: the backups that run every night, the deployments that push a website live, the migration that moves a business to a new server. If your servers matter and you want them backed up, deployed, and maintained by someone who understands what is really happening underneath, that is exactly the kind of work I enjoy helping with.


Peter is a Joomla specialist and a Linux admin for fast, secure and scalable websites.


