| | |

LFCA 69 🐧 Backing Up with rsync

The previous chapter covered tar — the archive tool that bundles files into a single stream. This chapter covers rsync — the synchronization tool that copies only what changed. They solve different problems. tar creates a portable archive that can be stored, moved, and restored as a single file. rsync keeps two directories in sync, transferring only the differences and preserving everything else in place. For backups, rsync is the tool you use when you want a live, browsable copy of your data that updates efficiently .

The LFCA exam places backup and recovery under System Administration Fundamentals, which carries 20% of the total weight. The competency list includes “Implement backup strategies” and “Disaster Recovery” . rsync is one of the two primary tools — alongside tar — that implement those strategies. Knowing the flags, the delta-transfer algorithm, the --delete behavior, and the hard-link incremental pattern is the practical skill the exam tests.

Key point: rsync copies only the changed portions of files. It does this by comparing modification times and sizes, then computing checksums on the parts that differ. The result is a synchronization that transfers a fraction of the data that a full copy would. The trailing slash on the source path is the single most important syntax detail: source/ copies the contents into the destination, while source copies the directory itself into the destination .


Why rsync exists

cp copies files. scp copies files over the network. Neither is suitable for backups. Both copy everything, every time.

The redundant-transfer problem. A backup of a 100 GB directory that changes by 50 MB per day should not transfer 100 GB per day. cp and scp do exactly that. rsync compares the source and destination, identifies the files that have changed, and transfers only those files. For files that have changed partially — a log file that has grown, a database file with new pages — rsync can transfer only the changed blocks using its delta-transfer algorithm . The savings in bandwidth and time are substantial.

The metadata problem. A cp of a directory does not preserve ownership, permissions, or timestamps unless you use specific flags. rsync -a preserves all of these by default. The -a flag is shorthand for -rlptgoD: recursive, links, permissions, times, group, owner, and devices . This is the archive mode, and it is what you want for backups.

The deletion problem. A backup that only adds files is not a mirror. If you delete a file from the source, the backup still has it. Over time, the backup accumulates stale data. The --delete flag tells rsync to remove files from the destination that no longer exist in the source . This keeps the backup a true mirror. Without --delete, the backup grows forever.

The network problem. rsync works over SSH. The command rsync -avz source/ user@remote:/backup/ transfers the directory to a remote server over an encrypted connection . This is how off-site backups are made. The -z flag compresses the data during transfer, reducing bandwidth usage further.

The trade-off. rsync is not an archive tool. It does not create a single file. The backup is a directory tree, not a .tar.gz file. This is an advantage for browsing and restoring individual files, but a disadvantage for long-term archival storage or for transferring a complete backup as a unit. rsync is for live, current mirrors. tar is for archives. Most backup strategies use both .


a. Basic rsync: The -avz Flags and the Trailing Slash

The canonical rsync command for backups is rsync -avz source/ destination/. Each flag serves a purpose.

The -a flag enables archive mode. It is shorthand for a combination of flags: -r (recursive), -l (copy symlinks as symlinks), -p (preserve permissions), -t (preserve modification times), -g (preserve group), -o (preserve owner), and -D (preserve device files and special files) . For backups, archive mode is the baseline. Without it, rsync copies files but strips their metadata.

The -v flag enables verbose output. It prints each file as it is transferred. This is useful for interactive use and for logging. In scripts, the output goes to a log file.

The -z flag enables compression during transfer. It reduces the amount of data sent over the network, at the cost of CPU time to compress and decompress. For local backups, -z is unnecessary. For remote backups, it is almost always worth enabling .

# Local backup with archive mode
rsync -av /home/user/ /backup/user/

# Remote backup over SSH with compression
rsync -avz /home/user/ user@remote:/backup/user/

# Dry run to preview what would happen
rsync -avz --dry-run /home/user/ /backup/user/

The --dry-run flag (-n) is the safety net. It shows what rsync would do without actually doing it. Before running a backup with --delete, always run with --dry-run first. This is how you avoid accidentally deleting files from the destination that should not be deleted .

The trailing slash on the source path determines what gets copied. rsync -av /home/user/ /backup/user/ copies the contents of /home/user/ into /backup/user/. The result is /backup/user/file.txt, /backup/user/subdir/, and so on. rsync -av /home/user /backup/user/ copies the directory itself into /backup/user/, creating /backup/user/user/ . This distinction is the most common source of confusion with rsync. For backups, you almost always want the trailing slash — you want the contents of the source directory to appear in the destination directory.


b. The –delete Flag and Exclusion Patterns

The --delete flag makes the destination an exact mirror of the source. Files that exist in the destination but not in the source are removed. This is essential for a true backup — without it, the backup accumulates deleted files forever .

# Mirror the source, deleting anything in the destination that is not in the source
rsync -av --delete /home/user/ /backup/user/

The --delete flag has variants. --delete-before deletes files before transferring new ones (useful when space is tight). --delete-after deletes after transfer (the default). --delete-during deletes during the transfer. --delete-excluded also deletes files that match the exclude patterns, which is useful when you add a new exclusion and want it removed from the destination .

The --exclude flag skips files and directories. It can be repeated for multiple patterns, or a file of patterns can be used with --exclude-from.

# Exclude temporary files and caches
rsync -av --delete \
  --exclude='*.tmp' \
  --exclude='*.cache' \
  --exclude='.git' \
  /home/user/ /backup/user/

# Read exclusions from a file
rsync -av --delete --exclude-from=/etc/backup-exclude.txt /home/user/ /backup/user/

The exclusion patterns are matched against the paths as they appear in the transfer. --exclude='*.tmp' excludes any file ending in .tmp. --exclude='/tmp' excludes a directory named tmp at the root of the source. The patterns are case-sensitive and follow glob syntax .

For system backups, the standard exclusions are /proc, /sys, /dev, /tmp, /run, /mnt, /media, and /lost+found. These directories either contain virtual filesystems that should not be backed up, or temporary data that is regenerated on boot.


c. Incremental Backups with –link-dest

The most powerful feature of rsync for backups is the --link-dest option. It creates hard links to files that are unchanged, so that each backup looks like a complete copy but consumes almost no additional space .

# First backup (full copy)
rsync -av /home/user/ /backup/2025-05-07/

# Next day: create a new directory with hard links to unchanged files
rsync -av --link-dest=/backup/2025-05-07/ /home/user/ /backup/2025-05-08/

When rsync transfers to /backup/2025-05-08/, it compares each file against the corresponding file in /backup/2025-05-07/. If the file is unchanged, it creates a hard link instead of copying the data. The result is a directory that appears to be a full copy — every file is present — but the unchanged files share the same disk blocks with the previous backup. Only the changed files consume new space .

The hard-link approach gives you the best of both worlds. Each backup is a complete, browsable, restorable directory tree. There is no chain of incrementals to replay. But the storage consumption is only the size of the changes, not the size of the full data.

A typical rotation script uses --link-dest with a latest symlink:

#!/bin/bash
# /usr/local/bin/backup-rsync.sh

BACKUP_BASE="/backup"
SOURCE="/home/user"
DATE=$(date +%Y-%m-%d)
DEST="$BACKUP_BASE/$DATE"
LATEST="$BACKUP_BASE/latest"

mkdir -p "$DEST"

# Use the latest backup as the link destination
if [ -d "$LATEST" ]; then
    rsync -av --delete --link-dest="$LATEST" "$SOURCE/" "$DEST/"
else
    rsync -av --delete "$SOURCE/" "$DEST/"
fi

# Update the latest symlink
rm -f "$LATEST"
ln -s "$DEST" "$LATEST"

The latest symlink points to the most recent backup. Each new backup uses --link-dest=$LATEST to hard-link unchanged files. The result is a series of dated directories, each one a complete copy, all sharing the unchanged data .


Complete Example Session

This session demonstrates a local backup, a remote backup over SSH, an incremental backup with hard links, and a cron-scheduled rotation script.

# ============================================
# PART 1: THE BASIC LOCAL BACKUP
# ============================================

# Back up /home/user to /backup/user
rsync -av /home/user/ /backup/user/

# The first run copies everything.
# Subsequent runs copy only changed files.

# ============================================
# PART 2: THE DRY RUN
# ============================================

# Preview what would change
rsync -av --dry-run --delete /home/user/ /backup/user/

# Output shows files that would be transferred or deleted.

# ============================================
# PART 3: THE MIRROR WITH DELETE
# ============================================

# Make the backup an exact mirror
rsync -av --delete /home/user/ /backup/user/

# Files deleted from /home/user are also deleted from /backup/user.

# ============================================
# PART 4: THE EXCLUSION PATTERNS
# ============================================

# Exclude caches and temporary files
rsync -av --delete \
  --exclude='.cache' \
  --exclude='.local/share/Trash' \
  --exclude='*.tmp' \
  /home/user/ /backup/user/

# ============================================
# PART 5: THE REMOTE BACKUP
# ============================================

# Back up to a remote server over SSH
rsync -avz --delete /home/user/ user@backup-server:/backup/user/

# The -z flag compresses data during transfer.

# ============================================
# PART 6: THE REMOTE PULL
# ============================================

# Pull a backup from a remote server
rsync -avz user@remote-server:/home/user/ /backup/user/

# The direction is reversed: remote is the source.

# ============================================
# PART 7: THE INCREMENTAL BACKUP WITH HARD LINKS
# ============================================

# First backup
rsync -av /home/user/ /backup/2025-05-07/

# Next day
rsync -av --link-dest=/backup/2025-05-07/ /home/user/ /backup/2025-05-08/

# Unchanged files are hard links.
# du -sh shows the total size is only slightly larger.

# ============================================
# PART 8: THE ROTATION SCRIPT
# ============================================

#!/bin/bash
# /usr/local/bin/backup-rotate.sh

BACKUP_BASE="/backup"
SOURCE="/home/user"
DATE=$(date +%Y-%m-%d)
DEST="$BACKUP_BASE/$DATE"
LATEST="$BACKUP_BASE/latest"

mkdir -p "$DEST"

if [ -d "$LATEST" ]; then
    rsync -av --delete --link-dest="$LATEST" "$SOURCE/" "$DEST/"
else
    rsync -av --delete "$SOURCE/" "$DEST/"
fi

rm -f "$LATEST"
ln -s "$DEST" "$LATEST"

# ============================================
# PART 9: THE CRON JOB
# ============================================

# Edit root's crontab
sudo crontab -e

# Add:
# Run backup daily at 2:00 AM
0 2 * * * /usr/local/bin/backup-rotate.sh

# ============================================
# PART 10: THE RESTORE
# ============================================

# Restore a single file
rsync -av /backup/2025-05-08/home/user/important.txt /home/user/

# Restore the entire home directory
rsync -av /backup/2025-05-08/home/user/ /home/user/

# The backup is a plain directory tree.
# Any file can be restored individually.

The ten parts cover the basic local backup, the dry run, the mirror with --delete, exclusion patterns, the remote backup, the remote pull, the incremental backup with --link-dest, the rotation script, the cron job, and the restore.


Quick Reference

The Core Flags

FlagPurpose
-aArchive mode (-rlptgoD)
-vVerbose output
-zCompress during transfer
-n / --dry-runPreview without doing
--deleteRemove files from destination not in source
--exclude=patternSkip matching files
--exclude-from=fileRead exclusions from file
--link-dest=dirHard-link unchanged files to dir

The Archive Mode Components

FlagMeaning
-rRecursive
-lCopy symlinks as symlinks
-pPreserve permissions
-tPreserve modification times
-gPreserve group
-oPreserve owner
-DPreserve devices and special files

The Trailing Slash Rule

Source PathDestination Result
source/Contents of source in destination
sourceSource directory itself in destination

The Common Backup Patterns

TaskCommand
Local mirrorrsync -av --delete /src/ /dest/
Remote pushrsync -avz /src/ user@host:/dest/
Remote pullrsync -avz user@host:/src/ /dest/
Incrementalrsync -av --link-dest=prev /src/ /new/
Dry runrsync -avn --delete /src/ /dest/

The Standard Exclusions

PatternReason
/procVirtual filesystem
/sysVirtual filesystem
/devDevice files
/tmpTemporary data
/runRuntime data
*.tmpTemporary files
*.cacheCache directories

Best Practices

✅ Do This:

# Use archive mode for backups
rsync -av /home/user/ /backup/user/                          # ✅
# Always dry-run before --delete
rsync -avn --delete /home/user/ /backup/user/                # ✅
# Use trailing slash on source to copy contents
rsync -av /home/user/ /backup/user/                          # ✅
# Use --link-dest for space-efficient incrementals
rsync -av --link-dest=/backup/prev/ /home/user/ /backup/new/ # ✅
# Compress remote transfers
rsync -avz /home/user/ user@host:/backup/user/               # ✅
# Exclude regenerable directories from system backups
rsync -av --delete --exclude='/proc' --exclude='/sys' / /backup/ # ✅

❌ Don’t Do This:

# Don't forget the trailing slash
rsync -av /home/user /backup/user/  # creates /backup/user/user/  # ❌
# Don't use --delete without --dry-run first
rsync -av --delete /home/user/ /backup/user/                 # ❌ risky
# Don't back up /proc, /sys, /dev
rsync -av / /backup/                                         # ❌ includes virtual filesystems
# Don't rely on rsync for archival storage
# rsync mirrors; tar archives.                               # ❌

Common Pitfalls

PitfallWhy It HappensFix
Backup has extra directory levelMissing trailing slash on sourceAdd / to source path
Files deleted unexpectedly--delete without --dry-runRun with -n first
Permission errorsNot running as root for system filesUse sudo
Backup larger than expected--delete not usedAdd --delete to mirror
Hard links not working--link-dest path wrongVerify the previous backup path
SSH key promptNo key-based authUse ssh-copy-id

Real-World Examples

1. Basic Local Backup

rsync -av /home/user/ /backup/user/

2. Mirror with Delete

rsync -av --delete /home/user/ /backup/user/

3. Remote Push

rsync -avz /home/user/ user@backup-server:/backup/user/

4. Remote Pull

rsync -avz user@remote-server:/home/user/ /backup/user/

5. Incremental with Hard Links

rsync -av --link-dest=/backup/2025-05-07/ /home/user/ /backup/2025-05-08/

6. Exclude Caches

rsync -av --exclude='.cache' --exclude='*.tmp' /home/user/ /backup/user/

7. System Backup with Exclusions

rsync -av --delete --exclude='/proc' --exclude='/sys' --exclude='/dev' / /backup/system/

8. Dry Run

rsync -avn --delete /home/user/ /backup/user/

9. Restore One File

rsync -av /backup/2025-05-08/home/user/important.txt /home/user/

10. Cron-Scheduled Backup

0 2 * * * /usr/local/bin/backup-rotate.sh

Visual

The Delta-Transfer Algorithm

┌──────────────────────────────────────────────┐
│  RSYNC DELTA TRANSFER                        │
│                                              │
│  Source file:                                │
│  [AAAAAAAA][BBBBBBBB][CCCCCCCC]              │
│                                              │
│  Destination file:                           │
│  [AAAAAAAA][XXXXXX][CCCCCCCC]                │
│                                              │
│  rsync compares checksums per block.         │
│  Only the changed block is transferred.      │
│                                              │
│  Result: [BBBBBBBB] sent over the wire.      │
│  The unchanged blocks stay.                  │
│                                              │
└──────────────────────────────────────────────┘

The Trailing Slash Rule

┌──────────────────────────────────────────────┐
│  TRAILING SLASH                              │
│                                              │
│  rsync -av /home/user/ /backup/              │
│    └─ Copies CONTENTS of /home/user/         │
│    └─ Result: /backup/file.txt               │
│                                              │
│  rsync -av /home/user /backup/               │
│    └─ Copies the DIRECTORY itself            │
│    └─ Result: /backup/user/file.txt          │
│                                              │
│  For backups, use the trailing slash.        │
│                                              │
└──────────────────────────────────────────────┘

The –link-dest Incremental Pattern

┌──────────────────────────────────────────────┐
│  --link-dest INCREMENTAL                     │
│                                              │
│  /backup/2025-05-07/  ← full backup          │
│    file1.txt (100M)                          │
│    file2.txt (50M)                           │
│                                              │
│  /backup/2025-05-08/  ← new backup           │
│    file1.txt (100M) ← hard link, 0 new space │
│    file2.txt (50M)  ← changed, 50M new       │
│                                              │
│  Each backup looks complete.                 │
│  Only changes consume space.                 │
│                                              │
└──────────────────────────────────────────────┘

The Backup Rotation Flow

┌──────────────────────────────────────────────┐
│  ROTATION WITH latest SYMLINK                │
│                                              │
│  /backup/2025-05-07/                         │
│  /backup/2025-05-08/                         │
│  /backup/2025-05-09/                         │
│  /backup/latest → /backup/2025-05-09/        │
│                                              │
│  Each new backup:                            │
│    1. rsync --link-dest=latest               │
│    2. Update latest symlink                  │
│                                              │
│  Result: dated snapshots, minimal space.     │
│                                              │
└──────────────────────────────────────────────┘

Summary

ItemValue
Archive mode-a (-rlptgoD)
Verbose-v
Compress-z
Dry run-n / --dry-run
Mirror (delete)--delete
Exclude--exclude, --exclude-from
Incremental--link-dest
Trailing slashsource/ = contents, source = directory
Remoteuser@host:/path/
Schedulecron

Key takeaways:

  • rsync copies only what changed. It compares source and destination, identifies differences, and transfers only the changed data. For partially changed files, it transfers only the changed blocks using the delta-transfer algorithm .
  • Archive mode (-a) preserves metadata. The -a flag is shorthand for recursive, links, permissions, times, group, owner, and devices. Without it, rsync copies files but strips their metadata .
  • The trailing slash on the source path determines what gets copied. source/ copies the contents into the destination. source copies the directory itself. For backups, use the trailing slash .
  • --delete makes the destination a true mirror. Without it, files deleted from the source remain in the backup forever. Always run with --dry-run first to preview what would be deleted .
  • --link-dest creates space-efficient incremental backups. Each backup is a complete directory tree, but unchanged files are hard-linked to the previous backup. The storage consumption is only the size of the changes .
  • rsync works over SSH. The command rsync -avz source/ user@remote:/backup/ transfers over an encrypted connection. The -z flag compresses the data during transfer .
  • rsync and tar serve different purposes. rsync keeps a live, browsable mirror that updates efficiently. tar creates a portable archive. Most backup strategies use both .

Remember: rsync is the workhorse of Linux backups. It copies only what changed, preserves metadata, works over SSH, and can create space-efficient incremental backups with hard links. The trailing slash is the most important syntax detail. The --delete flag makes a true mirror. The --link-dest flag gives you dated snapshots with minimal storage. Use --dry-run before every --delete. Schedule it with cron. And remember that rsync is for mirrors, not archives — for long-term archival storage, use tar.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!