A hard-linked rsync snapshot tree can hold many dated restore points while storing unchanged file data only once. Retention is therefore enforced by unlinking expired snapshot directories, not by treating each date as an independent full backup.

Files copied with --link-dest can share one inode across several dates. Removing one dated pathname decreases the inode's link count, while the data remains reachable through retained snapshots until the last hard link is removed.

Pruning is safe only when the backup writer is idle and the removal arguments contain reviewed, completed dates. On Linux, GNU rm can use --one-file-system to skip nested mounts on other filesystems; the newest successful snapshot must stay outside the prune list.

Steps to prune old rsync snapshots:

  1. Check for an in-progress snapshot before reviewing the prune list.
    $ find /snapshots -maxdepth 1 -type d -name '.*.in-progress' -print

    If this command prints a path, stop. Wait for the backup writer to finish, or investigate the exact staging directory as an interrupted run before pruning any completed snapshot.

  2. Confirm that rm provides the cross-filesystem safeguard.
    $ rm --help
    Usage: rm [OPTION]... [FILE]...
    Remove (unlink) the FILE(s).
     
    ##### snipped #####
          --one-file-system  when removing a hierarchy recursively, skip any
                              directory that is on a file system different from
                              that of the corresponding command line argument
    ##### snipped #####

    If --one-file-system is absent, do not copy the removal command unchanged. Use the deletion tool and filesystem-boundary protection supported by that operating system.

  3. List the completed snapshots against the retention record.
    $ ls -1 /snapshots
    2026-07-07
    2026-07-08
    2026-07-09
    2026-07-10
    2026-07-11

    The sample policy prunes only 2026-07-07 and 2026-07-08. Replace every date with reviewed values from the real retention record, and keep the newest successful restore point out of the removal arguments.

  4. Check a known unchanged file in an expired snapshot and the newest retained snapshot.
    $ stat -c '%i %h %n' /snapshots/2026-07-07/releases/v1.txt /snapshots/2026-07-11/releases/v1.txt
    378158 5 /snapshots/2026-07-07/releases/v1.txt
    378158 5 /snapshots/2026-07-11/releases/v1.txt

    The matching inode and link count of 5 show that five snapshot paths still share this file before pruning.

  5. Recursive removal is irreversible; remove only the reviewed expired directories.
    $ rm -r --one-file-system -- /snapshots/2026-07-07 /snapshots/2026-07-08

    --one-file-system skips nested mounts on other filesystems, but it does not protect an incorrectly named directory on the same filesystem. Stop if either explicit path is not an expired snapshot.

  6. List the snapshot root again and confirm only retained dates remain.
    $ ls -1 /snapshots
    2026-07-09
    2026-07-10
    2026-07-11
  7. Recheck the shared file across the retained dates.
    $ stat -c '%i %h %n' /snapshots/2026-07-09/releases/v1.txt /snapshots/2026-07-11/releases/v1.txt
    378158 3 /snapshots/2026-07-09/releases/v1.txt
    378158 3 /snapshots/2026-07-11/releases/v1.txt

    The inode is unchanged and the link count fell from 5 to 3 because two expired pathnames were unlinked.

  8. Read the file from the newest successful snapshot.
    $ cat /snapshots/2026-07-11/releases/v1.txt
    release 1

    The retained restore point still exposes the expected file content after pruning.