rsync: The Complete Linux Backup and Sync Tool Guide

Tested on: Ubuntu 26.04 LTS · Debian 12 · Arch Linux · Fedora 44 — Last updated: June 2026
rsync is the standard Linux tool for copying and synchronizing files locally and over SSH. It transfers only what changed between source and destination — so a second run of the same backup is nearly instant. Unlike cp or scp, rsync compares file metadata (size and modification time by default) and builds a delta, making it indispensable for backups, deployments, and ongoing directory synchronization.
- Prerequisites
- Installing rsync
- Basic Syntax and the Trailing Slash Rule
- Core Flags Reference
- Local File and Directory Copying
- Remote Transfers Over SSH
- Excluding Files and Directories
- Dry Run and Inspecting What rsync Will Do
- Incremental Snapshot Backups with --link-dest
- Understanding --delete Variants
- Automating Backups with Cron
- Bandwidth and Resource Limiting
Prerequisites
- Basic terminal usage and familiarity with file paths
- For remote transfers: SSH access to the target server and an existing user account
- For automated cron jobs: a passwordless SSH key pair set up between source and destination
- rsync installed on both machines for remote transfers (the remote side needs rsync in
$PATH)
Installing rsync
rsync ships pre-installed on most distributions. Verify your version before starting — some flags covered here require rsync 3.1+.
# Ubuntu/Debian:
sudo apt install rsync -y
# Fedora/RHEL:
sudo dnf install rsync -y
# Arch Linux:
sudo pacman -S rsync
# Check version:
rsync --versionExpected output:
rsync version 3.2.7 protocol version 31
Copyright (C) 1996-2022 by Andrew Tridgell, Wayne Davison, and others.
Web site: https://rsync.samba.org/Basic Syntax and the Trailing Slash Rule
The general form is rsync [OPTIONS] SOURCE DESTINATION. The single most common rsync mistake is misunderstanding the trailing slash on the source path — it changes behavior fundamentally.
# WITH trailing slash — sync the CONTENTS of src/ into dest/:
rsync -av /home/user/src/ /backup/dest/
# Result: /backup/dest/file1.txt, /backup/dest/file2.txt
# WITHOUT trailing slash — sync the DIRECTORY ITSELF into dest/:
rsync -av /home/user/src /backup/dest/
# Result: /backup/dest/src/file1.txt, /backup/dest/src/file2.txtThe destination trailing slash is optional — rsync creates the destination directory if it doesn't exist. The rule only applies to the source.
Core Flags Reference
These are the flags you'll use in nearly every rsync command:
-a/--archive— Archive mode. Equivalent to-rlptgoD: recursive, preserves symlinks, permissions, timestamps, owner, group, and device files. Use this for all backups.-v/--verbose— Print each file as it's transferred.-z/--compress— Compress data in transit. Useful over slow WAN links; adds CPU overhead on fast LAN — omit it for local or gigabit transfers.-P— Combines--progress(show transfer progress) and--partial(keep partial files for resume). Essential for large transfers.-n/--dry-run— Simulate the transfer without touching any files. Always use this first on destructive operations.-u/--update— Skip files that are newer at the destination. Useful for two-way sync scenarios.-h/--human-readable— Show file sizes in KB/MB/GB.--delete— Remove files from destination that no longer exist in source. Makes destination a true mirror.-e/--rsh— Specify the remote shell, e.g.,-e "ssh -p 2222".-A/--acls— Preserve ACLs (extends-a).-X/--xattrs— Preserve extended attributes (extends-a).
Local File and Directory Copying
# Copy a single file:
rsync -av file.txt /backup/file.txt
# Copy directory contents with progress:
rsync -avP /home/user/Documents/ /backup/Documents/
# Mirror — make destination identical to source (deletes extras in dest):
rsync -av --delete /home/user/ /backup/home/
# Preserve ACLs and extended attributes — required for system-level backups:
sudo rsync -aAXvh /etc/ /backup/etc/
# Backup to external drive, excluding cache and trash:
sudo rsync -aAXvh --delete
--exclude='/home/user/.cache'
--exclude='/home/user/.local/share/Trash'
--exclude='/home/user/.thumbnails'
/home/user/ /mnt/external/backup/home/Remote Transfers Over SSH
rsync tunnels over SSH by default when the source or destination contains a colon. No extra daemon or port is needed — just working SSH access.
# Push local directory to remote server:
rsync -avz /home/user/projects/ user@192.168.1.100:/home/user/projects/
# Pull from remote to local:
rsync -avz user@192.168.1.100:/home/user/backups/ /local/backups/
# Use a non-standard SSH port:
rsync -avz -e "ssh -p 2222" /source/ user@server:/destination/
# Use a specific SSH key:
rsync -avz -e "ssh -i ~/.ssh/backup_key" /source/ user@server:/dest/
# Combine port and key:
rsync -avz -e "ssh -p 2222 -i ~/.ssh/backup_key" /source/ user@server:/dest/
# Deploy a built static site to a VPS:
rsync -avz --delete
--exclude='.git'
--exclude='node_modules'
./dist/ user@yourserver.com:/var/www/mysite/Setting Up Passwordless SSH for Automated Backups
Cron jobs and scripts cannot enter a passphrase interactively. Generate a dedicated key with no passphrase:
# Generate dedicated backup key (no passphrase):
ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N "" -C "rsync-backup-$(hostname)"
# Copy public key to backup server:
ssh-copy-id -i ~/.ssh/backup_key.pub user@backup-server
# Test the connection:
ssh -i ~/.ssh/backup_key user@backup-server "echo connection ok"
# Now rsync runs without prompts:
rsync -avz -e "ssh -i ~/.ssh/backup_key" /source/ user@backup-server:/dest/Excluding Files and Directories
Exclude patterns match against the relative path of each file from the source root. A pattern ending with / matches directories only.
# Single exclude:
rsync -av --exclude='*.log' /source/ /dest/
# Multiple inline excludes:
rsync -av
--exclude='*.log'
--exclude='*.tmp'
--exclude='.git/'
--exclude='__pycache__/'
--exclude='node_modules/'
/source/ /dest/
# Exclude file — more maintainable for complex rules:
cat > ~/.rsync-exclude << 'EOF'
*.log
*.tmp
*.swp
.git/
.DS_Store
node_modules/
__pycache__/
.cache/
.thumbnails/
Thumbs.db
EOF
rsync -av --exclude-from="$HOME/.rsync-exclude" /source/ /dest/Exclude rules are processed in order — first match wins. Use --include before --exclude to whitelist specific patterns inside an otherwise excluded tree:
# Sync only .conf files from /etc, skip everything else:
rsync -av
--include='*.conf'
--include='*/'
--exclude='*'
/etc/ /backup/etc-conf/Dry Run and Inspecting What rsync Will Do
Before running any destructive rsync command — especially those using --delete — always simulate with -n.
# Show what would be transferred without doing it:
rsync -avn /source/ /dest/
# Show what would be deleted:
rsync -av --delete --dry-run /source/ /dest/
# Show transfer statistics after a run:
rsync -av --stats /source/ /dest/
# Output includes: total files, transferred count, bytes sent/received, transfer speed
# Itemize changes — shows exactly WHY each file was transferred:
rsync -av --itemize-changes /source/ /dest/The --itemize-changes output uses an 11-character code per file. The most useful characters: f = file, d = directory, > = transferred to destination, c = checksum differs, s = size differs, t = timestamp differs. For example, >f.st...... means a file was sent because size and timestamp changed.
Incremental Snapshot Backups with --link-dest
--link-dest is rsync's most powerful backup feature. Each snapshot directory appears to contain a full backup, but unchanged files are hard-linked to the previous snapshot rather than copied — so each incremental backup costs only the disk space of what actually changed.
#!/bin/bash
# snapshot-backup.sh — run daily via cron
SOURCE="/home/user/"
BACKUP_DIR="/backup/snapshots"
TODAY=$(date +%Y-%m-%d)
LATEST="$BACKUP_DIR/latest"
# Create today's snapshot, hard-linking unchanged files from the last run:
rsync -aAXvh --delete
--exclude='/home/user/.cache'
--link-dest="$LATEST"
"$SOURCE"
"$BACKUP_DIR/$TODAY/"
# Update the 'latest' symlink:
ln -snf "$BACKUP_DIR/$TODAY" "$LATEST"
echo "Snapshot complete: $BACKUP_DIR/$TODAY"# After several runs:
ls -lh /backup/snapshots/
# drwxr-xr-x 2026-06-01/
# drwxr-xr-x 2026-06-02/
# drwxr-xr-x 2026-06-03/
# lrwxrwxrwx latest -> 2026-06-03
# Restore from a specific date (non-destructive — goes to /tmp first):
rsync -aAXvh /backup/snapshots/2026-06-01/ /tmp/restore-test/
# Check actual disk usage — hard-links mean all snapshots together
# use far less space than their apparent size:
du -sh /backup/snapshots/*/Understanding --delete Variants
Without --delete, rsync only ever adds files. Files removed from source persist in the destination indefinitely. Add --delete to create a true mirror — but understand the variants before choosing one.
# Standard delete — removes destination files not in source:
rsync -av --delete /source/ /dest/
# Safe delete — move deleted files to a separate directory instead of removing them:
rsync -av --delete --backup --backup-dir=/backup/deleted-$(date +%Y%m%d)
/source/ /dest/
# Delete excluded files too (normally excluded files are left in place even with --delete):
rsync -av --delete --delete-excluded --exclude='*.tmp' /source/ /dest/
# Only delete after transfer is complete (safer, prevents partial-sync data loss):
rsync -av --delete-after /source/ /dest/Automating Backups with Cron
crontab -e# Daily backup at 2:00 AM — local to external drive:
0 2 * * * rsync -aAXh --delete
--exclude='/home/user/.cache'
/home/user/ /mnt/external/backup/ >> /var/log/rsync-daily.log 2>&1
# Hourly sync to remote backup server:
0 * * * * rsync -az -e "ssh -i /home/user/.ssh/backup_key"
/home/user/projects/ backup@nas.local:/backups/projects/ >> /var/log/rsync-remote.log 2>&1
# Weekly snapshot to remote (uses the snapshot script above):
0 3 * * 0 /usr/local/bin/snapshot-backup.sh >> /var/log/rsync-snapshot.log 2>&1Log rotation prevents these log files from growing unbounded. Add a logrotate config:
# /etc/logrotate.d/rsync-backup
/var/log/rsync-*.log {
weekly
rotate 8
compress
missingok
notifempty
}Bandwidth and Resource Limiting
# Limit to 10 MB/s (value is in KB/s):
rsync -avz --bwlimit=10240 /source/ user@server:/dest/
# 1 MB/s limit for background backup over metered connection:
rsync -avz --bwlimit=1024 /source/ user@server:/dest/
# Reduce CPU priority so backup doesn't impact foreground work:
nice -n 19 rsync -avz /source/ /dest/
# Combine bandwidth and priority limiting:
nice -n 19 ionice -c 3 rsync -avz --bwlimit=5120 /source/ user@server:/dest/
# ionice -
