A good backup follows the 3-2-1 rule: keep 3 copies of your data, on 2 different kinds of media, with 1 copy stored offsite. The tool you reach for depends on what you are protecting.
| Goal | Reach for |
|---|---|
| Sync files to another disk | [rsync](/man/rsync) |
| Versioned, deduplicated backups | [borg](/man/borg), [restic](/man/restic) |
| Clone a whole disk or partition | [dd](/man/dd), [partclone](/man/partclone) |
| Rescue a failing drive | [ddrescue](/man/ddrescue) |
| Push to cloud storage | [rclone](/man/rclone) |
| Snapshot a live filesystem | [lvm](/man/lvm), [btrfs](/man/btrfs), [zfs](/man/zfs) |
Test your restores regularly - a backup you have never restored is only a hope.
rsync copies only the differences between source and destination, which makes repeat backups fast. The -a (archive) flag preserves permissions, timestamps, symlinks, and ownership.
[rsync](/man/rsync) -a /home/user/ /mnt/backup/user/
[rsync](/man/rsync) -ah --info=progress2 /data/ /mnt/backup/data/
Add --delete to mirror the source exactly, removing files from the destination that no longer exist in the source.
[rsync](/man/rsync) -a --delete /data/ /mnt/backup/data/
A trailing slash on the source matters: src/ copies the contents of src, while src copies the directory src itself.
| Flag | Description |
|---|---|
| -a | Archive mode (preserve metadata, recurse) |
| -v | Verbose output |
| -z | Compress data during transfer |
| -h | Human-readable sizes |
| --delete | Remove extra files from destination |
| -n / --dry-run | Show what would happen, change nothing |
| --exclude | Skip matching paths |
| --progress | Show per-file progress |
Always dry-run first: when using --delete, do a --dry-run so you can confirm nothing important will be wiped.
When either side of an rsync is user@host:path, the transfer runs over SSH automatically, still copying only changes. Add -z to compress on slow links.
[rsync](/man/rsync) -avz /data/ user@host:/backup/data/
[rsync](/man/rsync) -avz user@host:/var/www/ /mnt/backup/www/
Pass SSH options, such as a non-standard port, through -e.
[rsync](/man/rsync) -avz -e "[ssh](/man/ssh) -p 2222" /data/ user@host:/backup/data/
For a quick one-off copy of a single file, scp is simpler.
[scp](/man/scp) backup.tar.gz user@host:/backup/
With --link-dest, rsync hard-links unchanged files to a previous backup, so each snapshot looks complete but only uses extra space for files that changed.
[rsync](/man/rsync) -a --delete --link-dest=/backup/prev /data/ /backup/2026-06-06/
This gives you browseable, dated snapshots at the storage cost of an incremental backup. Rotate by pointing each new run at the previous day's directory. rsnapshot automates exactly this scheme with a config file and retention levels.
A tar archive bundles many files into one, ideal for a point-in-time snapshot. Combine with compression to save space.
[tar](/man/tar) czf backup-$(date +%F).tar.gz /home/user
[tar](/man/tar) xzf backup-2026-06-06.tar.gz
For large datasets, zstd compresses much faster than gzip at similar or better ratios.
[tar](/man/tar) --zstd -cf backup.tar.zst /data
tar supports incremental archives: -g keeps a snapshot file that records what changed between runs. The first run is a full backup; later runs with the same snapshot file only store changes.
[tar](/man/tar) czf full.tar.gz -g backup.snar /data
[tar](/man/tar) czf incr1.tar.gz -g backup.snar /data
To restore, extract the full archive first, then each incremental in order.
Stream an archive straight to a remote host without a temporary file.
[tar](/man/tar) czf - /data | [ssh](/man/ssh) user@host "cat > /backup/data.tar.gz"
See the Compression & Archiving basics page for the full set of tar, gzip, xz, and zstd options.
Dedicated backup programs add deduplication, encryption, compression, and retention policies on top of plain copies. They store each file's blocks once, so re-running a backup is cheap.
| Tool | Strengths |
|---|---|
[borg](/man/borg) |
Dedup, compression, encryption, mountable archives |
[restic](/man/restic) |
Simple, fast, many cloud backends built in |
[duplicity](/man/duplicity) |
Encrypted incremental backups via GPG |
[bup](/man/bup) |
Git-based dedup, handles huge files well |
| rsnapshot | rsync plus hard-link rotation, no extra format |
Initialize a borg repository, then create a compressed, deduplicated archive.
[borg](/man/borg) init --encryption=repokey /mnt/backup/repo
[borg](/man/borg) create --compression zstd /mnt/backup/repo::{now} /home/user
restic works the same way and speaks to local disks, SFTP, S3, and more.
[restic](/man/restic) -r /mnt/backup/repo init
[restic](/man/restic) -r /mnt/backup/repo backup /home/user
Prune old snapshots with a retention policy so backups do not grow forever. With borg, run compact afterwards to actually free the space.
[borg](/man/borg) prune --keep-daily=7 --keep-weekly=4 /mnt/backup/repo
[borg](/man/borg) compact /mnt/backup/repo
[restic](/man/restic) -r /mnt/backup/repo forget --keep-daily 7 --keep-weekly 4 --prune
Test a restore by extracting a snapshot to a scratch directory, or mount the repository and browse it.
[restic](/man/restic) -r /mnt/backup/repo restore latest --target /tmp/restore-test
[borg](/man/borg) mount /mnt/backup/repo /mnt/restore
If the repository is encrypted, the backup is only as safe as the key and passphrase. Export the key and store it somewhere that is not inside the backup.
Copying a running database's files mid-write produces a corrupt backup. Dump to a consistent snapshot first, then back up the dump like any other file.
[mysqldump](/man/mysqldump) --all-databases > mysql-backup.sql
[pg_dumpall](/man/pg_dumpall) > postgres-backup.sql
[sqlite3](/man/sqlite3) app.db ".backup app-backup.db"
A dump you have never restored is only a hope. restoredrill loads a PostgreSQL pg_dump into a throwaway container, runs the checks you define, and writes a timestamped JSON report.
[restoredrill](/man/restoredrill) --config restoredrill.yml --trigger manual
The same applies to anything that writes constantly (virtual machine disks, mail spools): stop the writer, dump it, or back up from a filesystem snapshot (see below).
dd copies data block by block, making an exact image of a disk or partition. Identify the target device first with lsblk and double-check it.
[lsblk](/man/lsblk)
[dd](/man/dd) if=/dev/sda of=/dev/sdb bs=4M status=progress
Save a partition to a compressed image file instead of another disk.
[dd](/man/dd) if=/dev/sda1 bs=4M status=progress | [gzip](/man/gzip) > sda1.img.gz
Restore an image back onto a device.
[gunzip](/man/gunzip) -c sda1.img.gz | [dd](/man/dd) of=/dev/sda1 bs=4M status=progress
Careful: dd writes wherever you point it with no confirmation. A wrong of= value will destroy data instantly. Check the device name twice, then once more. Image only unmounted partitions: an image of a filesystem that is changing underneath you is inconsistent.
| Option | Description |
|---|---|
| if= | Input file or device |
| of= | Output file or device |
| bs= | Block size (4M is a good default) |
| status=progress | Show transfer progress |
| conv=noerror,sync | Keep going past read errors, pad blocks |
| conv=fsync | Flush to the device before exiting |
| count= | Copy only N blocks |
To write an installer ISO to a USB stick, dd it to the whole device (not a partition). conv=fsync makes sure everything is flushed before the command returns.
[dd](/man/dd) if=distro.iso of=/dev/sdc bs=4M status=progress conv=fsync
Plain cp and cat can also stream an image to a device.
[cp](/man/cp) distro.iso /dev/sdc && [sync](/man/sync)
Wipe old filesystem signatures from a device before reusing it.
[wipefs](/man/wipefs) -a /dev/sdc
When a drive is failing, use ddrescue instead of dd: it retries bad sectors, works from the easy areas first, and keeps a map file so it can resume later.
[ddrescue](/man/ddrescue) /dev/sda rescue.img rescue.map
[ddrescue](/man/ddrescue) -r3 /dev/sda rescue.img rescue.map
partclone images only the used blocks of a filesystem, so it is faster and smaller than a full dd of an empty-ish partition. Use -c to create an image and -r to restore it.
[partclone](/man/partclone).ext4 -c -s /dev/sda1 -o sda1.img
[partclone](/man/partclone).ext4 -r -s sda1.img -o /dev/sda1
clonezilla wraps these tools into a guided disk-cloning environment for bare-metal backups.
[clonezilla](/man/clonezilla)
For repairing a system that no longer boots, recovering deleted files with testdisk and photorec, and reading SMART health, see the System Recovery basics page.
Modern filesystems and volume managers can snapshot a live system instantly, giving you a stable, consistent view to back up from.
| System | Create a snapshot |
|---|---|
| LVM | lvcreate -s -n snap -L 5G /dev/vg/lv |
| Btrfs | btrfs subvolume snapshot / /snap |
| ZFS | zfs snapshot pool/data@backup |
Create an LVM snapshot, mount it read-only, and back it up while the system keeps running. Remove the snapshot when done so it stops consuming space.
[lvcreate](/man/lvcreate) -s -n root_snap -L 5G /dev/vg0/root
[mount](/man/mount) -o ro /dev/vg0/root_snap /mnt/snap
[rsync](/man/rsync) -a /mnt/snap/ /mnt/backup/root/
[umount](/man/umount) /mnt/snap && [lvremove](/man/lvremove) /dev/vg0/root_snap
Btrfs and ZFS snapshots are even cheaper, create them read-only (-r) so the backup source cannot change.
[btrfs](/man/btrfs) subvolume snapshot -r / /.snapshots/2026-06-06
[zfs](/man/zfs) snapshot tank/home@2026-06-06
snapper and timeshift automate scheduled Btrfs/LVM snapshots with retention.
[snapper](/man/snapper) create --description "before upgrade"
[timeshift](/man/timeshift) --create
Snapshots live on the same disk and are not a backup by themselves. Copy them somewhere else.
rclone syncs files to dozens of cloud providers (S3, Backblaze, Google Drive, and more) after a one-time rclone config.
[rclone](/man/rclone) copy /data remote:backup/data
[rclone](/man/rclone) sync /data remote:backup/data --progress
Check what would change without transferring, verify a finished backup, or mount a remote as a local folder.
[rclone](/man/rclone) sync /data remote:backup --dry-run
[rclone](/man/rclone) check /data remote:backup/data
[rclone](/man/rclone) mount remote:backup /mnt/remote
Careful: like rsync --delete, rclone sync makes the destination match the source, including deletions. Use copy when you only want to add files.
A backup is only good if it is intact. Record checksums when you make a backup, then verify them on restore.
[sha256sum](/man/sha256sum) backup.tar.gz > backup.sha256
[sha256sum](/man/sha256sum) -c backup.sha256
Schedule backups so they actually happen. Edit your crontab and add a nightly job.
[crontab](/man/crontab) -e
0 2 * * * [rsync](/man/rsync) -a --delete /data/ /mnt/backup/data/
Automate the backup, but verify restores by hand on a schedule. The only proven backup is one you have successfully restored.