Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

rsync: synchronise and back up files

By updated

Improve this page

When you back up servers, deploy applications or synchronise directories between machines, rsync is the reference tool. Unlike cp or scp, rsync only transfers the differences: if 1% of a file changed, only that 1% crosses the network. The result: fast backups, resumable after a cut, and fine control over what gets copied.

What you will learn

  • Synchronise directories locally with -av, and remotely with -avz
  • Transfer to and from a remote server over SSH
  • Exclude files with --exclude and --exclude-from
  • Use --delete for an exact mirror (with precautions)
  • Create incremental backups with --link-dest

Where rsync fits among Linux tools

rsync belongs to the transfer and backup tools of Linux. Each tool has its speciality, and the first table places them side by side. The second one answers the practical question: for this situation, which tool should you pick? rsync wins as soon as the transfer repeats, because each pass only sends what changed.

CommandSpecialityWhen to use it
rsyncSmart synchronisation (delta)Backups, deployments, large files
cpSimple local copyOne-off files, simple scripts
scpRemote copy over SSHQuick one-shot transfer
sftpInteractive transfer session over SSHExplore a server, several files in one session
tarArchiving with compressionCreate archives, full backups
ddBit-for-bit copyDisk images, low-level cloning

When to use rsync rather than the others:

SituationBest toolWhy
Daily backuprsyncOnly transfers the changes
Copy one file to a serverscp or rsyncscp simpler, rsync resumes if cut
Two-way synchronisationunisonrsync is one-way
Clone a diskddExact bit-for-bit copy
Compressed archive to sendtar + sshA single file to transfer

Frequent combinations:

rsync combines with three everyday tools: cron to schedule, find to choose the files, ssh for the transport. The second form passes the find list to --files-from, which keeps the tree: an -exec rsync {} file by file would drop everything flat at the root of the destination.

Terminal window
# rsync + cron: automatic daily backup
0 2 * * * rsync -avz --delete /data/ backup-srv:/backups/
# rsync + find: back up only the files changed in the last 24 h
find /data -mtime -1 -type f -printf '%P\n' | rsync -av --files-from=- /data/ backup:/data/
# rsync + custom ssh: non-standard port
rsync -avz -e "ssh -p 2222" source/ user@server:/dest/

Understand rsync in 2 minutes

The model below sums up the command: a source, filters that decide what goes through, a comparison with the destination, then sending only the differences. Keep the order in mind, it is the same for a local copy and for a transfer over SSH.

🧠 Mental model: How rsync works

rsync = Source → [Filters] → Destination (only transfers the differences)

rsync reads the source, applies the filters in order, then compares each file with the destination: same size and date, the file is skipped; otherwise only the changed blocks are sent to the destination.yesnorsyncSOURCEdata/ · admin@web-01:/var/www/FILTERS--exclude · --include, in ordersame sizeand date?file skippedonly the changedblocks are sentDESTINATION/backup/ · backup-srv:/backups/
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

Key points

  • rsync compares files by size and date (or checksum with -c)
  • Only the changed blocks are transferred (delta algorithm)
  • The trailing slash on the source changes everything: /source/ ≠ /source
  • Archive mode (-a) preserves permissions, timestamps, symbolic links
  • Always test with --dry-run before --delete
  • SSH is the default transport for remote transfers

Golden rules

1
Always test with --dry-run before running Especially with --delete, which removes files on the destination.
2
Watch the trailing slash on the source /source/ copies the content, /source copies the directory itself.

Essential vocabulary

-a (archive)
Preserves permissions, timestamps, links, recursive
-v (verbose)
Shows the files processed
-z (compress)
Compresses the data during the transfer
-n (dry-run)
Simulates without changing anything
--delete
Deletes the files missing from the source
--progress
Shows the transfer progress
📚 Going further: 12 advanced options
--exclude
Exclude files/directories by pattern
--exclude-from
Read the exclusions from a file
--include
Include explicitly: the first matching rule wins, put it before --exclude
--partial
Keep partially transferred files
--append
Append to the end of a file that only grows (logs), without checking the existing part
--bwlimit
Limit the bandwidth (KiB/s)
--link-dest
Incremental backups with hard links
-e ssh
Set the SSH command (port, key...)
--inplace
Update files in place (risky)
-c (checksum)
Compare by checksum instead of size/date
--max-delete
Stop deletions beyond N files (exit code 25)
-P
Equivalent to --partial --progress

The delta algorithm: rsync splits files into blocks, computes checksums, and only transfers the changed blocks. On a 1 GB file changed by 1%, only ~10 MB of data travel, plus the checksums. This algorithm only helps across the network: between two local paths, the manual states that --whole-file is the default, because reading both files would cost more than copying again.

Minimal syntax

Two rules govern this syntax. The source is always on the left, the destination on the right: swapping them overwrites your working data with a stale backup. And only one of the two ends can be remote: a server-to-server transfer is refused with The source and destination cannot both be remote. and exit code 1. Also remember that the trailing slash on the source changes the result, detailed below.

Terminal window
rsync [OPTIONS] SOURCE DESTINATION

The 3 usage scenarios

Going from local to remote changes nothing in the syntax: you simply add user@server: in front of the path. rsync detects the colon and automatically switches to SSH as the transport, with no extra option. It also explains a common error: a local path containing a colon is read as a remote destination. rsync -av a:b/ /tmp/y/ thus tries to connect to a machine called a and answers ssh: Could not resolve hostname a. Prefix the path with ./ to remove the ambiguity.

ScenarioSyntaxExample
Local → Localrsync source/ dest/rsync -av ~/docs/ /backup/docs/
Local → Remotersync source/ user@srv:dest/rsync -avz ~/data/ admin@srv:/backup/
Remote → Localrsync user@srv:source/ dest/rsync -avz srv:/logs/ ~/local-logs/

The trailing slash trap

This is the number one source of errors with rsync, because it produces no message: the command succeeds, but the tree arrives shifted by one level. The table and the block below show both results side by side.

CommandResult
rsync -av source dest/Creates dest/source/... (copies the directory)
rsync -av source/ dest/Copies the content into dest/...

Simple rule: slash on the source = copies the content. No slash = copies the directory.

/backup/project/src/...
rsync -av ~/project /backup/
# With slash, result: /backup/src/... (content directly)
rsync -av ~/project/ /backup/

The essential options

These six options cover almost every use, and the order of the table reflects how often they are really used. Two of them deserve particular attention. -a is not a mere abbreviation of "recursive": it preserves permissions, owners, timestamps and symbolic links, which is essential for a restorable backup. -n is the safeguard: run once before each destructive command, it costs a few seconds and prevents irreversible losses.

OptionMeaningUsage
-aArchiveStandard mode: recursive + preserves permissions, timestamps, links
-vVerboseShows the files processed
-zCompressCompresses during the transfer (useful over a network)
-nDry-runSimulates without changing (essential before --delete)
-PProgress + PartialShows progress + keeps partial files
--deleteDeleteRemoves from dest what no longer exists in source

The standard combo:

Terminal window
# Basic local backup
rsync -av ~/Documents/ /media/backup/Documents/
# Remote transfer with compression
rsync -avz ~/project/ deployer@prod:/var/www/app/
# Test before deletion
rsync -avn --delete ~/master/ /backup/mirror/

Delta transfer in action

The advantage of rsync over a plain cp or scp: it only transfers the differences. The first synchronisation copies everything, the following ones only touch what changed. Demonstration on three files, output recorded on Debian 13 (rsync 3.5.0):

Terminal window
# 1. First sync: everything is copied
$ rsync -av src/ dst/
sending incremental file list
created directory dst
./
app.conf
data.txt
logs/
logs/app.log
sent 320 bytes received 114 bytes 868.00 bytes/sec
total size is 24 speedup is 0.06
# 2. Nothing changed: no file is transferred
$ rsync -av src/ dst/
sending incremental file list
sent 149 bytes received 13 bytes 324.00 bytes/sec
total size is 24 speedup is 0.15
# 3. A single file changed: only that one goes again
$ echo "listen 8443 ssl" > src/app.conf
$ rsync -av src/ dst/
sending incremental file list
app.conf
sent 220 bytes received 36 bytes 512.00 bytes/sec
total size is 28 speedup is 0.11

That is what makes rsync fast and thrifty on large volumes: on a multi-GB backup across the network, only the changed blocks travel, not whole files. The absence of any file name at step 2 proves that nothing was sent again.

To decide that a file changed, rsync does not read its content: it compares the size and the modification date. A file rewritten with the same number of bytes and the same date, to the second, is therefore silently skipped, which the measurement reproduces by copying the date with touch -r. The -c option forces the comparison by checksum and catches this case, at the cost of a full read on both sides.

Exclude files and directories

Two methods let you skip some files: --exclude options on the command line for two or three patterns, an exclusion file as soon as the list grows or is shared between several scripts. The patterns follow the glob syntax, and rsync interprets them, not the shell.

Method 1: --exclude options

Terminal window
rsync -av --exclude '*.log' --exclude 'cache/' source/ dest/

Method 2: exclusion file

Terminal window
# Create a .rsyncignore file
cat > .rsyncignore << 'EOF'
*.log
*.tmp
cache/
.git/
node_modules/
EOF
# Use the file
rsync -av --exclude-from=.rsyncignore source/ dest/

Rule order: --include must come before --exclude, because rsync applies the first rule that matches. Measured on the three distributions: the wrong form copies no file and reports nothing.

Terminal window
# Wrong: exclude * matches everything before include
rsync -av --exclude '*' --include '*.txt' source/ dest/
# Correct: include first
rsync -av --include '*.txt' --exclude '*' source/ dest/

Exact mirror with --delete

The --delete option makes the destination identical to the source by removing the files that no longer exist on the source side. The danger is real: a path mistake turns the synchronisation into an erasure, without confirmation. Hence the two-step rule, always in this order: preview with -n, then run.

Terminal window
# Step 1: ALWAYS preview
rsync -avn --delete ~/source/ /backup/mirror/
# Check the list of files that would be deleted
# Step 2: run only if the list is right
rsync -av --delete ~/source/ /backup/mirror/

Variants of --delete:

Since rsync 3.0.0, --delete alone is equivalent to --delete-during: deletions happen during the transfer, directory by directory, not before. --delete-before still exists but forces the old non-incremental scan, which uses more memory. The real safety net, by the way, is none of these variants but --max-delete=N, which stops deletions beyond N files: rsync prints Deletions stopped due to --max-delete limit (2 skipped) and exits with code 25.

OptionBehaviour
--deleteDeletes during the transfer, directory by directory (equivalent to --delete-during since rsync 3.0.0)
--delete-beforeDeletes before the transfer starts, at the price of the old non-incremental scan
--delete-afterDeletes once the transfer is finished
--delete-excludedAlso deletes the files excluded from the transfer
--max-delete=NStops deletions beyond N files, exit code 25

Remote transfers over SSH

SSH is the default transport for remote transfers: rsync starts ssh to the designated machine, then launches a second rsync at the other end. rsync must therefore be installed on both sides, and everything your SSH client can do (keys, ~/.ssh/config, jump host) applies with no extra option.

Terminal window
# Basic syntax
rsync -avz source/ user@server:/path/dest/
# Fetch from a server
rsync -avz user@server:/var/log/ ~/server-logs/

Custom SSH (different port, specific key): the -e option replaces the transport command, which lets you pass it any ssh option.

Terminal window
# Non-standard SSH port
rsync -avz -e "ssh -p 2222" source/ user@srv:/dest/
# Specific SSH key
rsync -avz -e "ssh -i ~/.ssh/backup_key" source/ backup@srv:/data/
# Combined
rsync -avz -e "ssh -p 2222 -i ~/.ssh/backup_key" source/ user@srv:/dest/

Transfer resume and bandwidth

For large files or unstable connections, three options make the difference. -P keeps the partial file and shows the progress, --bwlimit caps the rate in KiB/s (units of 1,024 bytes according to the manual), and --append has a much narrower use than it seems.

Terminal window
# -P = --partial + --progress
# --partial keeps partially transferred files
rsync -avP bigfile.iso user@server:/data/
# Limit the bandwidth (in KiB/s)
rsync -avz --bwlimit=1000 source/ user@server:/dest/
# --append: only for files that just grow (logs)
rsync -av --append /var/log/app/ user@server:/archives/logs/

-P is enough to resume a broken transfer: the partial file kept serves as the basis for the delta algorithm at the next pass. --append is not a generic resume mode: it adds bytes at the end without ever checking the content already there, and therefore silently corrupts any file that was changed anywhere other than at its end. Keep it for strictly growing files, or use --append-verify, which checks the existing part by checksum.

--link-dest creates backups that look complete but only use space for the changed files: each file identical to the one in the reference directory becomes a hard link to it. A relative path is resolved from the destination, and the parent directory of the destination must exist, otherwise rsync answers mkdir "/backup/today" failed: No such file or directory (2) with code 11.

Terminal window
# Backup structure
/backup/
├── 2024-01-01/ # Full backup
├── 2024-01-02/ # Hard links to 2024-01-01 + new files
└── 2024-01-03/ # Hard links to 2024-01-02 + new files
# Command
rsync -av --link-dest=/backup/2024-01-01 ~/data/ /backup/2024-01-02/

Incremental backup script:

At the first run, the previous day's backup does not exist yet: rsync prints --link-dest arg does not exist: /backup/2026-09-29, makes a full copy and exits with code 0. The script therefore does not need to handle that case separately.

#!/bin/bash
BACKUP_DIR="/backup"
SOURCE="/home/user/data"
TODAY=$(date +%Y-%m-%d)
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
mkdir -p "$BACKUP_DIR"
# Create the backup using the previous one as reference
rsync -av --link-dest="$BACKUP_DIR/$YESTERDAY" \
"$SOURCE/" "$BACKUP_DIR/$TODAY/"

Space savings: with 100 GB of data of which 1% changes per day, seven daily backups take ~106 GB (one full copy, then six times 1 GB) instead of 700 GB. The calculation assumes that this 1% fits in few files: a hard link covers a whole file, and a file changed by a single byte is copied in full.

Automation with cron

cron runs the command at a fixed time, without a terminal: everything rsync prints must therefore go to a log, or a failure goes unnoticed. In a crontab, the % character has a special meaning and must be escaped as \%, hence the form of the date below.

Terminal window
# Edit the crontab
crontab -e
# Add (backup at 2 a.m.)
0 2 * * * rsync -avz --delete /data/ backup-server:/backups/$(date +\%Y-\%m-\%d)/ >> /var/log/rsync-backup.log 2>&1

Complete script with rotation:

The rotation shifts each snapshot by one day, then freezes the current state with cp -al, which creates hard links instead of copying. Each mv is conditioned on the directory existing: without that precaution, the first six nights would fail for lack of day.6. Replayed over nine consecutive nights on Debian 13: seven snapshots, the most recent in day.1, and the unchanged files shared by hard links.

#!/bin/bash
# backup.sh - Backup with a 7-day rotation
REMOTE="backup@srv:/backups"
SOURCE="/home/user/data"
LOG="/var/log/rsync-backup.log"
echo "=== Backup started: $(date) ===" >> "$LOG"
# Backup
rsync -avz --delete "$SOURCE/" "$REMOTE/current/" >> "$LOG" 2>&1
# Rotation (keep 7 days)
ssh backup@srv 'cd /backups && rm -rf day.7 &&
for i in 6 5 4 3 2 1; do
if [ -d "day.$i" ]; then mv "day.$i" "day.$((i + 1))"; fi
done &&
cp -al current day.1'
echo "=== Backup completed: $(date) ===" >> "$LOG"

Pitfalls to avoid

These mistakes come back constantly with rsync. Knowing them in advance saves you data losses and hours of diagnosis. Each one is presented the same way: the command that goes wrong, the symptom, the cause, then the correct form.

The trailing slash trap

Terminal window
rsync -av /source /destination

Symptom: an unexpected source/ directory appears inside destination/.

Cause: without a trailing slash, rsync copies the directory itself; with a slash, it copies its content.

Terminal window
rsync -av /source/ /destination/

--delete without a prior test

Terminal window
rsync -av --delete source/ dest/

Symptom: important files disappear from the destination.

Cause: --delete removes everything that is not present in the source, so a path mistake becomes destructive.

Terminal window
rsync -av --delete --dry-run source/ dest/ # check, then run again without --dry-run

Swapping source and destination

Terminal window
rsync -av --delete backup/ source/

Symptom: the working files are overwritten by an old backup.

Cause: confusion between source and destination; rsync always copies what is on the left to what is on the right.

Terminal window
rsync -av source/ backup/

Order of --include / --exclude

Terminal window
rsync -av --exclude "*" --include "*.txt" source/ dest/

Symptom: no file is copied despite the --include.

Cause: rsync applies the rules in order; --exclude "*" catches everything before --include is evaluated.

Terminal window
rsync -av --include "*.txt" --exclude "*" source/ dest/

Permission errors on the destination

Terminal window
rsync -av ~/files/ /opt/

Symptom: one line rsync: [receiver] mkstemp "/opt/.app.conf.ms5nIm" failed: Permission denied (13) per file, then exit with code 23.

Cause: the current user is not allowed to write in the destination. rsync creates each file under a temporary name before renaming it, hence the .app.conf.XXXXXX name. Not to be confused with the owner: a non-root account cannot keep it, and -a then assigns the files to the current account without any error.

Terminal window
rsync -av ~/files/ ~/backup/ # accessible destination
rsync -av --rsync-path="sudo rsync" ~/files/ admin@srv:/opt/ # remote side, if sudo is set up for rsync

Spaces in remote paths

Terminal window
rsync -av user@srv:/path/with spaces/ local/

Symptom: unexpected files, or a path not found on the remote side.

Cause: the local shell splits the argument on the space before rsync even sees it.

Terminal window
rsync -av "user@srv:/path/with spaces/" local/

Plain quotes are enough: since version 3.2.4, rsync escapes the active characters itself, spaces included, in the arguments passed to the remote shell. Adding a backslash by hand leads to a double escape: measured on rsync 3.2.7 and 3.5.0, the command answers change_dir "/srv/with\ spaces" failed: No such file or directory (2) and exits with code 23. On a server running an older rsync, the --old-args option restores the old behaviour.

Keeping rsync up to date: the 2026 security reflex

rsync is a mature tool, but 2026 has been a busy year for its security. Version 3.4.3 (20 May 2026) fixes six CVEs, including CVE-2026-43618 (CVSS v3.1 8.1, integer overflow in the compressed-token decoder, reachable from a plain pull) and CVE-2026-29518 (CVSS v4.0 7.3, TOCTOU race condition in daemon mode without chroot). Version 3.5.0 (13 August 2026), presented by its maintainers as a "major security release", then fixes 33 issues from an audit of path handling and of the daemon protocol: most exploit a symbolic link planted by a local user that an rsync run as root ends up following. Version 3.5.1 (21 September 2026) fixes the 3.5.0 regressions (source: the project's NEWS file).

The version number is not enough to judge a machine: distributions backport fixes without changing the upstream version. Measured on 2026-09-30:

Distributionrsync --version
Ubuntu 24.043.2.7, protocol 31
Debian 133.5.0, protocol 32
AlmaLinux 103.5.0, protocol 32
Terminal window
rsync --version | head -1
# rsync version 3.5.0 protocol version 32

On Ubuntu, the distribution's security tracking is what counts (apt changelog rsync), not the 3.2.7 displayed. Two reflexes follow. First, update rsync on the machines that expose it (backup servers, transfer relays). Second, never expose a bare rsync daemon: most uses go through SSH (rsync -e ssh), which encrypts and authenticates the transfer. Daemon mode (rsync://) remains reserved for trusted networks, ideally behind a firewall and with chroot.

Hands-on practice

This lab has you handle rsync on a real tree, from basic synchronisation to incremental backup. Allow 15 minutes.

Prepare the ground

The script below creates a test tree with files of various sizes and dates, plus two trap cases: files to exclude and a name containing a space.

Terminal window
# Create the lab
mkdir -p ~/rsync-lab/{source,backup,remote-sim}
cd ~/rsync-lab/source
# Create various files
echo "Main configuration" > config.conf
echo "Application logs" > app.log
dd if=/dev/zero of=data.bin bs=1M count=5 2>/dev/null
mkdir -p docs
echo "Documentation" > docs/readme.md
echo "Notes" > docs/notes.txt
# Files to exclude
echo "Temporary" > temp.tmp
mkdir -p cache
echo "Cache data" > cache/data.cache
# File with a space in its name
echo "Important report" > "report 2024.pdf"
# Old file
touch -d "30 days ago" docs/old-doc.md
echo "Lab created in ~/rsync-lab"
ls -laR ~/rsync-lab/source

To clean everything up at the end: rm -rf ~/rsync-lab.

The 8 lab steps

The steps chain on the same tree and the state obtained at each step feeds the next one: do not take them out of order. After each one, compare the two directories with diff -r ~/rsync-lab/source ~/rsync-lab/backup to see the real effect of the option used rather than trusting the message displayed.

  1. Basic synchronisation, copy the content of source/ to backup/ in archive mode: rsync -av ~/rsync-lab/source/ ~/rsync-lab/backup/. All files are copied with their attributes preserved.

  2. Test with --dry-run, simulate a synchronisation with --delete without changing anything: rsync -av --delete --dry-run ~/rsync-lab/source/ ~/rsync-lab/backup/. The output shows what would be done, without touching the files.

  3. Exclude files, synchronise while skipping the .tmp files and the cache/ directory: rsync -av --exclude "*.tmp" --exclude "cache/" ~/rsync-lab/source/ ~/rsync-lab/backup/. temp.tmp and cache/ are not copied.

  4. Exact mirror with --delete, create an orphan file then synchronise: touch ~/rsync-lab/backup/orphan-file.txt followed by rsync -av --delete ~/rsync-lab/source/ ~/rsync-lab/backup/. The orphan file is deleted because it is missing from the source.

  5. Remote transfer (simulated locally), use compression: rsync -avz ~/rsync-lab/source/ ~/rsync-lab/remote-sim/. For a real server, the destination would be user@server:/path/, and with a custom SSH port: rsync -avz -e "ssh -p 2222" source/ user@server:/backup/.

  6. Show the progress, synchronise data.bin with the progress bar: rsync -av --progress ~/rsync-lab/source/data.bin ~/rsync-lab/backup/. Size, speed and percentage are displayed during the transfer.

  7. Limit the bandwidth, transfer data.bin at 500 KiB/s maximum: rsync -avz --bwlimit=500 ~/rsync-lab/source/data.bin ~/rsync-lab/backup/. The transfer is capped, useful not to saturate the network.

  8. Incremental backup, create a backup with hard links to the previous one: rsync -av --link-dest=../backup ~/rsync-lab/source/ ~/rsync-lab/backup-new/. Unchanged files become hard links, which saves disk space.

Progressive exercises

These exercises go from the basic reflex to a professional backup. Read the statement, look for the command, then unfold the solution to check.

Foundations level

The first two exercises set the two reflexes that prevent almost every loss: archive mode with the trailing slash, and simulation before any deletion.

Exercise 1, First synchronisation. Synchronise the ~/Documents directory to /media/backup/Documents while preserving the attributes.

Hint: -a for archive, -v to see what happens.

See the solution
Terminal window
rsync -av ~/Documents/ /media/backup/Documents/

-a preserves everything (permissions, timestamps, links). Do not forget the trailing slash on the source.

Exercise 2, Test before running. Simulate a synchronisation with --delete without changing anything.

Hint: --dry-run (or -n) simulates without changing.

See the solution
Terminal window
rsync -av --delete --dry-run ~/source/ ~/backup/

--dry-run is essential before any --delete command: it shows the real impact without risk.

Composition level

These three exercises combine the transfer with a filter, compression or an exact mirror.

Exercise 3, Excluding files. Synchronise while excluding all .log files and the cache/ directory.

Hint: --exclude can be used several times.

See the solution
Terminal window
rsync -av --exclude "*.log" --exclude "cache/" ~/source/ ~/backup/

--exclude accepts glob patterns and can be repeated as many times as needed.

Exercise 4, Transfer to a remote server. Send the ~/project directory to user@server:/var/www/app with compression.

Hint: -z enables compression, useful over the network.

See the solution
Terminal window
rsync -avz ~/project/ user@server:/var/www/app/

-z compresses the data during the transfer. SSH is the default transport.

Exercise 5, Exact mirror. Create an exact mirror of ~/master in /backup/mirror while deleting obsolete files.

Hint: --delete removes what no longer exists in the source.

See the solution
Terminal window
rsync -av --delete ~/master/ /backup/mirror/

--delete makes the destination identical to the source. Always test with --dry-run first.

Industrialisation level

Here the transfer must hold in a real context: a non-standard SSH port and an unstable connection.

Exercise 6, SSH with a custom port. Synchronise to a server that uses SSH port 2222.

Hint: -e lets you specify the SSH command.

See the solution
Terminal window
rsync -avz -e "ssh -p 2222" ~/data/ user@server:/backup/

-e "ssh -p 2222" changes the port. The same syntax accepts -i for a dedicated private key.

Exercise 7, Transfer with resume. Transfer a large file with the ability to resume if the connection is cut.

Hint: -P combines --partial and --progress.

See the solution
Terminal window
rsync -avP bigfile.iso user@server:/data/

-P keeps partial files and shows the progress: the transfer resumes where it stopped.

Security level

The last exercise builds an incremental backup, the one that lets you go back to an earlier state without multiplying the disk space.

Exercise 8, Incremental backup. Create a dated backup using hard links to the previous one.

Hint: --link-dest points to the previous backup.

See the solution
Terminal window
rsync -av --link-dest=/backup/2024-01-01 ~/data/ /backup/2024-01-02/

--link-dest saves a huge amount of space: only the changed files are really duplicated.

Troubleshooting

When a transfer fails or behaves badly, these diagnosis methods and these frequent errors cover the vast majority of cases. Start with the method, which tells you where to look, then match the message and the exit code against the table.

Diagnosis methods

These four commands each answer a different question. --dry-run answers "what is rsync going to do?", -vvv "why is this file sent again?", the separate SSH test "does the problem come from rsync or from the transport?", and --stats "how much actually travelled?". Always start by isolating the transport: an ssh user@server "ls" that fails makes any rsync-side diagnosis pointless.

Terminal window
# Simulate without changing anything
rsync -av --dry-run source/ dest/
# Very verbose mode: all the details
rsync -avvv source/ dest/
# Test the SSH connection before rsync
ssh -v user@server "ls /path/"
# Show the transfer statistics
rsync -av --stats source/ dest/

Frequent errors

Two of these five errors concern the transport, not the copy itself: it is the reflex to keep when a remote transfer fails. rsync returns a distinct exit code for each family of problems, visible with echo $? right after the command and documented in the EXIT VALUES section of the manual: 1 for a syntax error, 10 for a socket error, 11 for a file I/O error, 12 for a protocol error, 23 for a partial transfer due to errors, 24 when source files vanished along the way, 25 when the --max-delete limit stopped the deletions. If the SSH connection fails, 255 comes back. Every message in the table was recorded on rsync 3.2.7 and 3.5.0.

ErrorProbable causeSolution
mkstemp "..." failed: Permission denied (13), code 23No write rights on the destination, or SELinux/AppArmor blocksAim at an accessible directory, or --rsync-path="sudo rsync" on the remote side
ssh: connect to host srv port 22: Connection refused, code 255sshd stopped, firewall, or wrong SSH portStart sshd, open the firewall, or give the right port with -e "ssh -p ..."
failed to connect to srv (...): Connection refused (111), code 10rsync:// URL while no rsync daemon listensGo through SSH (user@srv:/path) or start the daemon
Host key verification failedFirst connection, or reinstalled server (key changed)ssh-keygen -R server then ssh user@server to accept the key
write failed on "...": No space left on device (28), code 11Destination disk full or quota reachedCheck with df -h, free some space or raise the quota

rsync cheat sheet

Keep it at hand: the general syntax, the basic options and the filters used most every day.

Syntax and trailing slash

The last two rows are the ones to read again before each backup command. The difference between source/ and source triggers no error, it simply produces a tree shifted by one level, often discovered several backups later. The slash on the destination, on the other hand, has no effect.

SyntaxMeaningExample
rsync [OPTIONS] SOURCE DESTGeneral form of the commandrsync -av source/ dest/
rsync ... user@host:pathRemote transfer over SSHrsync -avz data/ user@srv:/backup/
source/Copies the content of sourcersync -av source/ dest/
sourceCopies the source directoryrsync -av source dest/

Common options

These options combine freely, with one exception: -c forces the comparison by checksum of every file and cancels the size-and-date optimisation, which considerably lengthens the processing on large volumes. Only enable it to check a suspicious backup, never in a daily scheduled job.

SyntaxMeaningExample
-a (archive)Recursive + preserves everythingrsync -a source/ dest/
-v (verbose)Shows the files processedrsync -av source/ dest/
-z (compress)Compresses during the transferrsync -avz source/ dest/
-n (dry-run)Simulates without changingrsync -avn --delete source/ dest/
-P--partial + --progressrsync -avP big.iso dest/
--bwlimit=RATELimits the bandwidth (KiB/s by default)--bwlimit=1000
-c (checksum)Compares by checksumrsync -avc source/ dest/
--statsShows the statisticsrsync -av --stats source/ dest/
-e "ssh ..."Customises SSH (port, key)-e "ssh -p 2222"

Synchronisation and filters

Filters are evaluated in the order they appear on the command line, and the first rule that matches wins. It is the cause of every surprise with --include and --exclude. The pattern follows the shell glob syntax, but rsync interprets it, not the shell: always wrap your patterns in quotes so they reach it intact.

SyntaxMeaningExample
--deleteDeletes the files missing from the sourcersync -av --delete source/ dest/
--delete-afterDeletes after the transferrsync -av --delete-after source/ dest/
--updateSkips files that are newer on the destinationrsync -av --update source/ dest/
--exclude "pattern"Excludes files--exclude "*.log"
--exclude-from=FILEExclusions from a file--exclude-from=.rsyncignore
--include "pattern"Includes (put before --exclude)--include "*.txt" --exclude "*"
--link-dest=DIRHard links for incremental backups--link-dest=/backup/prev

Mastery checklist

You master rsync when you can tick each of these points without hesitating. A point you cannot tick sends you back to the matching section above.

  • I can use -a, -v, -z for a standard synchronisation.
  • I can test with --dry-run before running.
  • I can exclude files with --exclude.
  • I can transfer to and from a remote server.
  • I can use --delete for an exact mirror.
  • I can customise SSH (port, key).
  • I can resume an interrupted transfer with -P.
  • I can make incremental backups with --link-dest.
  • I understand why the trailing slash on the source matters.
  • I can diagnose an SSH connection error.

Key points

  • -avz: the standard combination (archive + verbose + compression)
  • Trailing slash: /source/ copies the content, /source copies the directory
  • --dry-run: ALWAYS test before --delete
  • --link-dest: space-saving incremental backups
  • -P: resume possible for interrupted transfers
  • Size and date: a file changed without altering either is skipped, unless you use -c
  • Exit codes: 23 partial transfer, 25 --max-delete limit, 255 SSH failure

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

FAQ: frequently asked questions

The answers below go back over the points that come up most often, in question form: the syntax, the trailing slash, the filters, deletion with --delete and bandwidth. Each one stands on its own and refers to the measurements made above.

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn