How to create snapshot backups with rsync

Snapshot backups with rsync give each backup run its own dated directory while unchanged files share the same inode through hard links. An operator can browse or restore from any snapshot like a full copy, but the backup volume stores another copy only for files that changed.

The --link-dest option compares the new destination with a previous snapshot and links matching files into the new tree. A relative link target is resolved from the destination directory, so --link-dest=../2026-06-06 points from /backup/rsync/2026-06-07/ back to the prior dated snapshot.

Create every snapshot in an empty directory and let source writes settle before the run. Rsync normally selects changed files by size and modification time, hard-linked snapshots are not application-aware database backups, and --inplace does not belong in this workflow because it can rewrite an older snapshot through a shared inode.

Steps to create snapshot backups with rsync:

  1. Record the source directory, snapshot root, and dated destination names before running rsync.
    Source: /srv/data/
    Snapshot root: /backup/rsync/
    First snapshot: /backup/rsync/2026-06-06/
    Next snapshot: /backup/rsync/2026-06-07/
  2. Create the first dated snapshot directory.
    $ mkdir -p /backup/rsync/2026-06-06
  3. Copy the source tree into the first snapshot.
    $ rsync -ai --delete /srv/data/ /backup/rsync/2026-06-06/
    >f+++++++++ index.html
    cd+++++++++ releases/
    >f+++++++++ releases/v1.txt

    The trailing slash on /srv/data/ copies the contents of the source directory into the snapshot directory.

    Use a new dated destination for each snapshot. Reusing an older snapshot path lets --delete remove files from that snapshot.

  4. Create an empty directory for the next snapshot after the source changes.
    $ mkdir -p /backup/rsync/2026-06-07
  5. Run the next snapshot with the previous snapshot as the hard-link source.
    $ rsync -ai --delete --link-dest=../2026-06-06 /srv/data/ /backup/rsync/2026-06-07/
    >f.s....... index.html
    >f+++++++++ releases/v2.txt

    The relative --link-dest value is evaluated from /backup/rsync/2026-06-07/, so ../2026-06-06 points to the prior snapshot in the same root. Unchanged files that were linked from the prior snapshot do not appear in the itemized output.

  6. Compare one unchanged file across both snapshots to confirm hard-link reuse.
    $ ls -li /backup/rsync/2026-06-06/releases/v1.txt /backup/rsync/2026-06-07/releases/v1.txt
    2627130 -rw-r--r-- 2 root root 10 Jun  6 03:41 /backup/rsync/2026-06-06/releases/v1.txt
    2627130 -rw-r--r-- 2 root root 10 Jun  6 03:41 /backup/rsync/2026-06-07/releases/v1.txt

    The same inode number and link count of 2 show that both snapshot paths refer to one unchanged file on disk.

  7. Create a temporary restore directory for a smoke test.
    $ mkdir -p /tmp/restore-test
  8. Restore the new snapshot into the temporary directory.
    $ rsync -ai /backup/rsync/2026-06-07/ /tmp/restore-test/
    >f+++++++++ index.html
    cd+++++++++ releases/
    >f+++++++++ releases/v1.txt
    >f+++++++++ releases/v2.txt

    A restore test proves the snapshot tree can be copied back out, not only that the backup command exited.

  9. Read a restored file that came from the hard-linked snapshot content.
    $ cat /tmp/restore-test/releases/v1.txt
    release 1
  10. Remove the temporary restore directory after the smoke test.
    $ rm -rf /tmp/restore-test