Guide
Dated restore points on plain storage, with one rsync flag.
A mirror tells you what your server looks like now — including now's mistakes. Hardlink snapshots give you every day's version, at roughly the cost of one copy plus the daily changes.
How the trick works
--link-dest=DIR tells rsync: when a file is unchanged compared to DIR, don't copy it — create a hardlink to the existing copy. A hardlink is another directory entry pointing at the same data on disk, so it costs essentially nothing. Point --link-dest at yesterday's snapshot, write today's into a fresh dated directory, and you get:
daily/2026-07-08/ ← full tree, browsable
daily/2026-07-09/ ← full tree, browsable
daily/2026-07-10/ ← full tree, browsable
Every directory looks and restores like a complete backup, but files unchanged across days exist on disk once. Thirty days of a 200GB server with 2% daily churn stores roughly 200GB + 30×4GB ≈ 320GB — not 6TB. Deleting any snapshot never breaks the others; the data survives until the last hardlink to it is gone.
A complete rotation script
#!/bin/bash
# /usr/local/bin/snapshot-backup.sh — hardlink snapshots to RSYNCIT
set -euo pipefail
REMOTE="usr1042@usr1042.rsyncit.com"
KEY="/root/.ssh/rsyncit"
TODAY=$(date +%F)
KEEP_DAYS=30
# 1. today's snapshot, hardlinked against the most recent one
LATEST=$(ssh -i "$KEY" "$REMOTE" 'ls -1d daily/????-??-?? 2>/dev/null | tail -1' || true)
rsync -a --delete \
-e "ssh -i $KEY" \
${LATEST:+--link-dest="/home/usr1042/$LATEST"} \
--exclude-from=/etc/backup-excludes.txt \
/etc /home /var/www \
"$REMOTE:daily/$TODAY/"
# 2. rotate: drop snapshots older than KEEP_DAYS
ssh -i "$KEY" "$REMOTE" \
"ls -1d daily/????-??-?? | head -n -$KEEP_DAYS | xargs -r rm -rf"
Note --link-dest takes a path as seen from the destination — absolute, or relative to the target directory. The most common failure is handing it a path relative to the wrong side and silently re-uploading everything; if a run transfers far more than your daily change, check that path first.
Restoring
This is the payoff: restores are just rsync (or SFTP, or a WebDAV browse) in the other direction, from whichever date you need. rsync -a "$REMOTE:daily/2026-07-03/etc/nginx/" /etc/nginx/ pulls Tuesday's nginx config. No restore software, no catalog, no vendor — the snapshot is a directory tree, readable by anything. That property is worth more than most backup features.
Pitfalls worth knowing
- Metadata changes break the link. A file whose permissions or owner changed gets stored fresh even if contents match — expected, occasionally surprising in size terms after a mass chmod.
- Renames are copies. Hardlink matching is by path; renaming a 50GB directory stores it again. Tolerable for configs and code, worth knowing for media libraries.
- Rotation edge: the
head -n -Nrotation assumes the date-sorted listing is the whole story — keep snapshot names machine-generated and don't hand-create directories indaily/. - Databases: same rule as always — dump first, snapshot the dump (see the base guide).
Storage that hardlinks happily
Snapshots need a real filesystem on the far end — that's exactly what RSYNCIT is. Flat $6.95/TB, unmetered uploads for the day-one seed.
See pricingTL;DR
- Dated dirs, hardlinked against yesterday
- Each snapshot browsable & independently deletable
- Cost ≈ one copy + daily changes
- Restore = rsync the other way
--link-destpath is destination-relative