How-to

rsync Examples: File Synchronization and Backups in Linux

Updated: · Skillaria.Top

rsync copies and synchronizes files locally and over SSH, transferring only the changes. The basic command is rsync -avz source/ user@host:/path/: -a preserves permissions and times, -v lists the files, -z compresses the traffic. --delete makes the destination an exact copy of the source, and -n lets you check the result first without changing anything.

rsync is the standard Linux tool for copying and synchronizing files: locally, between disks and between servers over SSH. Its main difference from cp and scp is that it transfers only what has changed, so re-syncing a large directory takes seconds. Below are rsync examples for typical tasks: copying a project to a server, mirroring a directory, exclusions, backups with snapshots and running on a schedule.

Syntax and the most useful flags

The general form of the command:

rsync [options] SOURCE... DESTINATION

The source and destination can be local paths or remote ones in the form user@host:/path. One side must always be local: rsync does not copy directly between two remote machines.

FlagWhat it does
-aarchive mode: recursive, preserving permissions, owner, group, modification time and symbolic links (this is -rlptgoD)
-vlist the files being transferred
-zcompress data during network transfer
-hhuman-readable sizes (K, M, G)
-Psame as --partial --progress: progress display and resuming interrupted files
-n, --dry-rundry run: show what would be done without changing anything
--deletedelete files in the destination that are not in the source
--excludedo not copy files matching a pattern
-eset the remote shell, for example -e "ssh -p 2222"
-H, -A, -Xpreserve hard links, ACLs and extended attributes (not included in -a)

In practice, nine times out of ten rsync -avh locally and rsync -avzh over the network are enough.

The trailing slash: the main trap

The most common beginner mistake involves the trailing slash on the source. It determines whether the directory itself or only its contents gets copied.

# Copy the project DIRECTORY into /backup → you get /backup/project/...
rsync -av project /backup/

# Copy the CONTENTS of project into /backup → the files end up directly in /backup/
rsync -av project/ /backup/

Easy to remember: a slash at the end of the source means "the contents of this directory". The slash does not matter on the destination. If you are unsure, add -n and look at the paths rsync plans to create.

Local copying

Copy a directory to an external drive with progress shown:

rsync -avhP ~/photos/ /mnt/usb/photos/

Running the same command again does not copy everything from scratch: rsync compares file size and modification time and transfers only new and changed files. The summary at the end shows how much data was actually sent:

sending incremental file list
2026/10/IMG_2041.jpg
          4.12M 100%   38.40MB/s    0:00:00 (xfr#1, to-chk=0/1534)

sent 4.13M bytes  received 9.25K bytes  2.76M bytes/sec
total size is 6.82G  speedup is 1,648.21

The speedup value shows how many times less data was transferred compared with a full copy.

rsync over SSH: copying to a server and back

For remote paths rsync uses SSH by default, so there is nothing extra to configure: it is enough that a regular ssh login works. Key-based login is the most convenient: no password prompt, and the command can be placed in cron.

# Upload a site to the server
rsync -avz ./site/ deploy@203.0.113.10:/var/www/site/

# Download logs from the server to your machine
rsync -avz deploy@203.0.113.10:/var/log/nginx/ ./nginx-logs/

# SSH on a non-standard port and with a specific key
rsync -avz -e "ssh -p 2222 -i ~/.ssh/deploy_key" ./site/ deploy@203.0.113.10:/var/www/site/

rsync must be installed on both machines: on the remote side it is started through SSH. If it is missing there, you will see rsync: command not found — install the package (sudo apt install rsync or sudo dnf install rsync).

If the host is defined in ~/.ssh/config under an alias, rsync understands it: rsync -avz ./site/ prod:/var/www/site/.

Root privileges needed on the server

When the files in the destination belong to root and you log in as a regular user, you can run the remote rsync through sudo:

rsync -avz --rsync-path="sudo rsync" ./conf/ admin@server:/etc/myapp/

For this to work, sudo must run rsync on the server without a password (a rule in sudoers), otherwise the command will hang waiting for input.

Directory mirror: --delete

By default rsync only adds and updates files, but never deletes anything. To make the destination an exact copy of the source, you need --delete:

rsync -avh --delete ~/projects/ /mnt/backup/projects/

This is a powerful and dangerous flag. A mistake in the path (for example, an empty source or a forgotten slash) will delete data in the destination. So the rule is simple: always run any command with --delete first with -n.

$ rsync -avhn --delete ~/projects/ /mnt/backup/projects/
sending incremental file list
deleting old-app/README.md
deleting old-app/
app/main.py

sent 1.20K bytes  received 64 bytes  2.53K bytes/sec
total size is 18.4M  speedup is 14,554.39 (DRY RUN)

The deleting lines show what would be removed. Once you are sure everything is correct, run the command without -n.

Useful variations: --delete-after deletes files after the transfer (the destination stays complete for longer), --backup --backup-dir=/mnt/backup/deleted-$(date +%F) does not delete but moves the deleted and replaced files into a separate directory.

Exclusions: --exclude and --include

Excluding temporary files, dependencies and secrets is standard practice in deployments and backups.

# Several patterns
rsync -avz --exclude='.git/' --exclude='node_modules/' --exclude='*.log' ./app/ server:/srv/app/

# Patterns from a file
rsync -avz --exclude-from='.rsyncignore' ./app/ server:/srv/app/

The .rsyncignore file is a simple list of patterns, one per line:

.git/
node_modules/
*.log
.env
tmp/

How the patterns work:

  • A pattern without a leading slash (*.log) matches at any depth.
  • A pattern with a leading slash (/tmp/) is anchored to the root of the transfer — it excludes only the top-level tmp.
  • A trailing slash (cache/) means "directories only".
  • Rules are checked in order, and the first match applies.

The last point leads to a technique for "copy only what is needed": allow first, then exclude the rest.

# Copy only .conf files, keeping the directory structure
rsync -av --include='*/' --include='*.conf' --exclude='*' /etc/ ./etc-confs/

Here --include='*/' is needed so that rsync descends into all subdirectories. Add --prune-empty-dirs (-m) to avoid creating empty folders.

Backups with rsync

A simple daily copy

A minimal backup of a home directory to a second disk:

rsync -aH --delete --exclude='.cache/' /home/alice/ /mnt/backup/alice/

The drawback of this approach is that only one version is kept. If a file was damaged and the backup has already synchronized, the old version is lost.

Incremental snapshots with --link-dest

The --link-dest flag solves this problem almost for free in terms of space. Unchanged files are not copied; instead they are created as hard links to the files of the previous snapshot. Each snapshot looks like a complete copy, but only the changes take up space.

#!/bin/bash
set -euo pipefail

SRC="/home/alice/"
DEST="/mnt/backup/snapshots"
TODAY="$(date +%F)"

mkdir -p "$DEST"
rsync -aH --delete \
  --exclude='.cache/' \
  --link-dest="$DEST/latest" \
  "$SRC" "$DEST/$TODAY"

ln -sfn "$DEST/$TODAY" "$DEST/latest"

On the first run the latest directory does not exist yet — rsync will print a warning and make a full copy, which is normal. Old snapshots are removed with a plain rm -rf: files that other snapshots still link to will not disappear.

To delete snapshots older than 30 days:

find /mnt/backup/snapshots -maxdepth 1 -type d -name '20*' -mtime +30 -exec rm -rf {} +

Backing up a server to another machine

It is convenient to choose the direction "from the backup server to the working one": the backup machine pulls the data itself, and the working server has no access to the copies. If it is compromised, the backups stay intact.

rsync -azH --delete -e "ssh -i /root/.ssh/backup_key" \
  backup@web01:/var/www/ /srv/backups/web01/www/

Keep in mind that databases cannot be copied this way: the files of a running DBMS are inconsistent while being copied. First make a dump (mysqldump, pg_dump), and let rsync transfer the finished dump file.

Scheduled runs with cron

The backup script is added to cron with crontab -e:

# Every day at 03:15, output goes to a log
15 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

What to check so that a backup run from cron really works:

  • The script uses absolute paths: cron has a minimal PATH and a different current directory.
  • The SSH key has no passphrase, and the host is already in known_hosts — otherwise rsync will hang on the host authenticity question.
  • The script checks the exit code: rsync returns 0 on success, 23 if some files were not transferred, 24 if files vanished during the copy.
  • Protect against two copies running at once with flock: flock -n /tmp/backup.lock /usr/local/bin/backup.sh.

Instead of cron you can use a systemd timer — it writes its output to the journal, and results are easy to check with journalctl.

Useful options for large transfers

TaskOption
Limit the speed so the link is not saturated--bwlimit=5M
Resume a large file after an interruption-P (or --partial)
Overall progress instead of the file list--info=progress2
Compare by content, not by time-c (--checksum), slower
Do not touch files that are newer in the destination-u (--update)
Do not overwrite existing files--ignore-existing
Delete source files after transfer (move)--remove-source-files
Detailed statistics at the end--stats

Common errors and how to fix them

  • Permission denied when writing — the user has no rights on the destination directory. Check the directory owner or use --rsync-path="sudo rsync".
  • Files are copied again every time — the modification time is not preserved. Make sure -a or -t is present; on FAT/exFAT add --modify-window=2, because these file systems store time with two-second precision.
  • chown or chgrp errors when copying to a USB stick or a network share — the file system does not support owners. Replace -a with -rlt.
  • An extra nesting appears: /backup/project/project — the trailing slash on the source was mixed up.
  • connection unexpectedly closed — an SSH problem, or rsync is not installed on the remote side. Check a regular ssh login and that rsync exists on the server.

Summary

For everyday work it is enough to remember a few commands: rsync -avz src/ host:/dst/ for copying over SSH, --delete together with a dry run -n for a mirror, --exclude for unwanted files and --link-dest for snapshot backups. Watch the trailing slash on the source, and rsync becomes the most reliable way to move files between machines. Synchronization and backups are covered in practice in our Linux course.

FAQ

How is rsync better than scp?

scp copies files in full every time, while rsync transfers only the changes, can resume, delete extra files in the destination and exclude files by pattern. For a one-off copy of a single file there is almost no difference; for regular synchronization rsync is much faster.

Can I use rsync on Windows?

Yes, through WSL, where rsync installs as on any Linux system, or through Cygwin. Syncing files from a Windows machine with a Linux server is easiest from WSL.

Does rsync encrypt the data it transfers?

rsync does not encrypt on its own, but when copying over SSH all traffic goes through the encrypted SSH connection. Without encryption, data is sent only when working with an rsync daemon at an address of the form rsync://.

How do I check that a copy matches the original?

Run rsync -avnc source/ destination/ — with the -c flag files are compared by checksums, and -n changes nothing. If the list of files is empty, the copies are identical.

What happens if I interrupt rsync in the middle of a transfer?

Files already transferred remain, and the unfinished file is deleted by default. Running the same command again continues from where it stopped, and with the -P flag the partially transferred file is kept too.

Learn this topic on video

This topic is covered in depth with practice in the course “Linux: с нуля до уверенного администрирования”.

Go to course