Linux rsync Command
last modified October 23, 2026
The rsync command synchronizes files and directories, locally
or between machines over SSH. On repeated runs, it transfers only what has
changed: by default, it skips files whose size and modification time match
the destination, and over the network, it sends only the changed parts of
modified files (the delta-transfer algorithm). This makes it the standard
tool for backups, mirroring, and deployments.
The syntax is rsync [options] source... destination, where a
remote location is written as host:path. The examples use
rsync 3.2.7 on Debian 12, with a sample project directory and
a second machine reachable as web, an alias defined in
~/.ssh/config.
Copying a directory
The -a (archive) option copies recursively and preserves
permissions, times, symbolic links, and, when run as root, owners. The
-v option lists the transferred files.
rsync -av project/ backup/
The destination directory is created if needed. Hidden files such as
.editorconfig are included. The summary shows how many bytes
were sent and received; the speedup is the ratio of the total size to the
transferred amount, which is about 1 for a first copy.
$ rsync -av project/ backup/ sending incremental file list created directory backup ./ .editorconfig readme.txt cache/ cache/page.tmp docs/ docs/manual.txt images/ images/logo.png logs/ logs/app.log sent 116,085 bytes received 190 bytes 232,550.00 bytes/sec total size is 115,530 speedup is 0.99
The trailing slash
A trailing slash on the source changes what is copied.
rsync -a project backup2/ rsync -a project/ backup3/ ls backup2 backup3 rsync -a project/ deploy/2026/site/ rsync -a --mkpath project/ deploy/2026/site/ ls deploy/2026/site
Without the slash, the directory itself is copied into the destination
(backup2/project). With the slash, its contents are copied
(think of it as project/*, including hidden files). A slash
on the destination makes no difference. rsync creates only the last
component of a destination path; the --mkpath option, added
in rsync 3.2.3, creates the missing parents too.
$ rsync -a project backup2/ $ rsync -a project/ backup3/ $ ls backup2 backup3 backup2: project backup3: cache docs images logs readme.txt $ rsync -a project/ deploy/2026/site/ rsync: [Receiver] mkdir "/home/jano/work/deploy/2026/site" failed: No such file or directory (2) rsync error: error in file IO (code 11) at main.c(800) [Receiver=3.2.7] $ rsync -a --mkpath project/ deploy/2026/site/ $ ls deploy/2026/site cache docs images logs readme.txt
Transferring only changes
Running the same command again transfers nothing, unless files have changed.
rsync -av project/ backup/ echo "Version 2" >> project/readme.txt rsync -av project/ backup/
The second run finds nothing to do and sends only the file list. After a
change, only readme.txt is transferred. This is why a daily
backup with rsync is fast even for large directories.
$ rsync -av project/ backup/ sending incremental file list sent 246 bytes received 16 bytes 524.00 bytes/sec total size is 115,530 speedup is 440.95 $ echo "Version 2" >> project/readme.txt $ rsync -av project/ backup/ sending incremental file list readme.txt sent 330 bytes received 39 bytes 738.00 bytes/sec total size is 115,540 speedup is 313.12
Itemizing changes
The -i (--itemize-changes) option explains why
each file is transferred.
echo "error: disk full" >> project/logs/app.log touch project/docs/manual.txt rsync -ai project/ backup/
In >f.st......, > means the file is sent,
f that it is a regular file, s that the size
differs, and t that the modification time differs. The
manual has only a new time, but the quick check cannot tell whether its
content changed, so the file is transferred; over the network, the
delta-transfer algorithm would find that almost nothing needs to be sent.
Other letters include
c (checksum), p (permissions), and
o/g (owner/group).
$ echo "error: disk full" >> project/logs/app.log $ touch project/docs/manual.txt $ rsync -ai project/ backup/ >f..t...... docs/manual.txt >f.st...... logs/app.log
Dry run
The -n (--dry-run) option shows what would be done
without changing anything.
rm project/cache/page.tmp echo "new" > project/notes.txt rsync -av --delete --dry-run project/ backup/ ls backup/cache
The output says that notes.txt would be copied and
cache/page.tmp deleted, and ends with (DRY RUN).
The file is still in the backup. Always do a dry run before commands with
--delete.
$ rm project/cache/page.tmp $ echo "new" > project/notes.txt $ rsync -av --delete --dry-run project/ backup/ sending incremental file list ./ notes.txt deleting cache/page.tmp cache/ sent 312 bytes received 47 bytes 718.00 bytes/sec total size is 115,557 speedup is 321.89 (DRY RUN) $ ls backup/cache page.tmp
Mirroring with --delete
By default, rsync never deletes files in the destination. The
--delete option removes files that no longer exist in the
source, so the destination becomes a mirror.
rsync -av --delete project/ backup/
Be careful with the source path: a typo or an empty source directory makes
rsync delete the whole destination. Excluded files are protected from
deletion unless --delete-excluded is used.
$ rsync -av --delete project/ backup/ sending incremental file list ./ notes.txt deleting cache/page.tmp cache/ sent 356 bytes received 63 bytes 838.00 bytes/sec total size is 115,557 speedup is 275.79
Excluding and including files
The --exclude option skips files that match a pattern.
--include rules, given before a broader exclude, keep specific
files.
rsync -av --exclude='*.log' --exclude='images/' project/ site/ rsync -avm --include='*/' --include='*.txt' --exclude='*' project/ texts/
A pattern ending with a slash matches only directories. The rules are
checked in order, and the first match wins. The second command copies only
.txt files: '*/' lets rsync descend into every
directory, '*.txt' includes the text files, and
'*' excludes everything else. The -m
(--prune-empty-dirs) option drops the directories that would
remain empty. Longer lists of patterns can be kept in a file and read with
--exclude-from.
$ rsync -av --exclude='*.log' --exclude='images/' project/ site/ sending incremental file list created directory site ./ .editorconfig notes.txt readme.txt cache/ docs/ docs/manual.txt logs/ sent 13,559 bytes received 142 bytes 27,402.00 bytes/sec total size is 13,132 speedup is 0.96 $ rsync -avm --include='*/' --include='*.txt' --exclude='*' project/ texts/ building file list ... done created directory texts ./ notes.txt readme.txt docs/ docs/manual.txt sent 13,468 bytes received 107 bytes 27,150.00 bytes/sec total size is 13,121 speedup is 0.97
Showing progress
--info=progress2 shows the progress of the whole transfer, and
--progress the progress of each file.
rsync -a --info=progress2 video.bin media/ rsync -a --progress --bwlimit=5000 video.bin web:/tmp/
In a terminal, the progress line is updated in place; the output shows its
final state. The local copy of the 50 MB file is limited only by the disk.
The upload to web is limited to 5000 KiB/s by
--bwlimit, so it takes about ten seconds.
$ rsync -a --info=progress2 video.bin media/
50,000,000 100% 1.45GB/s 0:00:00 (xfr#1, to-chk=0/1)
$ rsync -a --progress --bwlimit=5000 video.bin web:/tmp/
sending incremental file list
video.bin
50,000,000 100% 4.90MB/s 0:00:09 (xfr#1, to-chk=0/1)
Comparing contents with checksums
The quick check compares only size and modification time. The
-c (--checksum) option compares the contents.
printf 'AAAA\n' > a/config.ini printf 'BBBB\n' > b/config.ini touch -r a/config.ini b/config.ini rsync -ai a/ b/ cat b/config.ini rsync -aic a/ b/ cat b/config.ini
The two files have the same size and, thanks to touch -r, the
same modification time, so the quick check considers them equal and the
outdated b/config.ini is kept. With -c, rsync
reads and hashes both files, finds the difference (c in the
itemized output), and copies the file. Checksums are slower, because every
file must be read on both sides.
$ printf 'AAAA\n' > a/config.ini $ printf 'BBBB\n' > b/config.ini $ touch -r a/config.ini b/config.ini $ rsync -ai a/ b/ $ cat b/config.ini BBBB $ rsync -aic a/ b/ >fc........ config.ini $ cat b/config.ini AAAA
Keeping backups of replaced files
The --backup option keeps files that would be overwritten or
deleted. --backup-dir collects them in a separate
directory.
echo "Version 3" >> project/readme.txt rsync -a --backup --backup-dir=../backup-old project/ backup/ ls backup-old cat backup-old/readme.txt
A relative --backup-dir is relative to the destination, so
the old version of readme.txt ends up in
backup-old next to backup. Without
--backup-dir, the old files stay next to the new ones with a
~ suffix.
$ echo "Version 3" >> project/readme.txt $ rsync -a --backup --backup-dir=../backup-old project/ backup/ $ ls backup-old readme.txt $ cat backup-old/readme.txt Project readme Version 2
Synchronizing over SSH
When the source or the destination contains a host name, rsync connects through SSH and starts rsync on the other side, which must therefore be installed there.
rsync -avz project/ web:/home/jano/project-copy/ ssh web 'ls -A project-copy' rsync -av web:/home/jano/project-copy/docs/ restored-docs/
The first command uploads the project; -z compresses the data
in transit, which helps on slow links but not on a fast LAN. The third
command downloads a directory. The host alias, user, port, and key come
from ~/.ssh/config; other SSH options can be passed with
-e, for example -e 'ssh -p 2222'.
$ rsync -avz project/ web:/home/jano/project-copy/ sending incremental file list ./ notes.txt readme.txt cache/ docs/manual.txt logs/app.log sent 467 bytes received 238 bytes 1,410.00 bytes/sec total size is 115,567 speedup is 163.92 $ ssh web 'ls -A project-copy' cache docs .editorconfig images logs notes.txt readme.txt $ rsync -av web:/home/jano/project-copy/docs/ restored-docs/ receiving incremental file list created directory restored-docs ./ manual.txt sent 46 bytes received 13,219 bytes 26,530.00 bytes/sec total size is 13,092 speedup is 0.99
Limiting bandwidth
The --bwlimit option sets the maximum transfer rate, in KiB
per second unless a suffix is given.
head -c 1000000 /dev/urandom > upload.bin time rsync -a --bwlimit=250 upload.bin web:/tmp/
At 250 KiB/s, the 1 MB file takes about four seconds. Limiting the rate keeps a large transfer from saturating a shared connection.
$ head -c 1000000 /dev/urandom > upload.bin $ time rsync -a --bwlimit=250 upload.bin web:/tmp/ real 0m4.136s user 0m0.113s sys 0m0.026s
Incremental snapshots with hard links
The --link-dest option compares the source with an earlier
backup and hard-links unchanged files to it instead of copying them.
mkdir snapshots rsync -a project/ snapshots/day1/ echo "Version 4" >> project/readme.txt rsync -a --link-dest=../day1 project/ snapshots/day2/ ls -li snapshots/day1/images/logo.png snapshots/day2/images/logo.png ls -li snapshots/day1/readme.txt snapshots/day2/readme.txt du -sh snapshots/day1 snapshots/day2
The unchanged logo.png has the same inode number in both
snapshots and a link count of 2, so it is stored once. The changed
readme.txt is a separate file. Each snapshot looks like a full
copy, but du shows that the second one uses only 24K of new
space. This is the technique behind tools such as rsnapshot and Time
Machine-style backups. Like --backup-dir, a relative
--link-dest path is relative to the destination.
$ mkdir snapshots $ rsync -a project/ snapshots/day1/ $ echo "Version 4" >> project/readme.txt $ rsync -a --link-dest=../day1 project/ snapshots/day2/ $ ls -li snapshots/day1/images/logo.png snapshots/day2/images/logo.png 2628596 -rw-r--r-- 2 jano jano 102400 Oct 20 09:00 snapshots/day1/images/logo.png 2628596 -rw-r--r-- 2 jano jano 102400 Oct 20 09:00 snapshots/day2/images/logo.png $ ls -li snapshots/day1/readme.txt snapshots/day2/readme.txt 2628594 -rw-r--r-- 1 jano jano 35 Oct 23 13:39 snapshots/day1/readme.txt 2628603 -rw-r--r-- 1 jano jano 45 Oct 23 13:39 snapshots/day2/readme.txt $ du -sh snapshots/day1 snapshots/day2 152K snapshots/day1 24K snapshots/day2
Moving files
The --remove-source-files option deletes source files after
they have been transferred successfully.
mkdir outbox; touch outbox/report1.csv outbox/report2.csv rsync -av --remove-source-files outbox/ archive/ ls outbox
The files are moved, but the source directories are left in place. This is useful for queues, for example to upload finished reports to a server and remove them locally only when the upload succeeded.
$ mkdir outbox; touch outbox/report1.csv outbox/report2.csv $ rsync -av --remove-source-files outbox/ archive/ sending incremental file list created directory archive ./ report1.csv report2.csv sent 181 bytes received 135 bytes 632.00 bytes/sec total size is 0 speedup is 0.00 $ ls outbox
Transfer statistics
The --stats option prints a detailed summary.
echo "Version 5" >> project/readme.txt rsync -a --stats project/ backup/
Of the 11 entries, only one regular file was transferred. Literal
data is data sent as is, and Matched data is data the
receiver could reuse from its old copy. For local copies, rsync sends whole
files, so all data is literal.
$ echo "Version 5" >> project/readme.txt $ rsync -a --stats project/ backup/ Number of files: 11 (reg: 6, dir: 5) Number of created files: 0 Number of deleted files: 0 Number of regular files transferred: 1 Total file size: 115,587 bytes Total transferred file size: 55 bytes Literal data: 55 bytes Matched data: 0 bytes File list size: 0 File list generation time: 0.001 seconds File list transfer time: 0.000 seconds Total bytes sent: 405 Total bytes received: 39 sent 405 bytes received 39 bytes 888.00 bytes/sec total size is 115,587 speedup is 260.33
Best practices for rsync
- Watch the slash:
src/copies the contents,srcthe directory itself. - Dry run first: Use
-nwith-ior-vbefore every new command, especially with--delete. - Start with -a: Add
-H,-A, and-Xto preserve hard links, ACLs, and extended attributes when needed. - Use --link-dest for snapshots: It gives full-looking daily backups at the cost of the changes only.
- Test restores: A backup is only useful if you have restored from it at least once.
Source
In this article, we have used the rsync command to copy and
mirror directories, filter files, compare checksums, keep backups and
hard-linked snapshots, and synchronize with a remote machine over SSH.
Author
List all Linux tutorials.