← retour aux snippets

rsync: snapshots incrémentaux avec hardlinks

Créer des sauvegardes quotidiennes en snapshots incrémentaux via --link-dest.


objectif

Sauvegarder en créant un snapshot par jour qui ne duplique pas les fichiers inchangés (hardlinks), avec rotation simple et restauration rapide.

code minimal

# snapshot journalier incrémental avec hardlinks
SRC="/srv/data.pm/current/"
DST="/srv/backup/data.pm"
DATE="$(date -u +%Y-%m-%d)"
LAST="$(ls -1 "$DST" 2>/dev/null | sort | tail -n1 || true)"

mkdir -p "$DST/$DATE"
if [ -n "$LAST" ] && [ -d "$DST/$LAST" ]; then
  rsync -a --delete --numeric-ids --link-dest="$DST/$LAST" \
    --human-readable --info=stats2,progress2 \
    "$SRC" "$DST/$DATE/"
else
  rsync -a --delete --numeric-ids \
    --human-readable --info=stats2,progress2 \
    "$SRC" "$DST/$DATE/"
fi

utilisation

# 1) préparer l'arborescence de sauvegarde
sudo install -d -m 0750 /srv/backup/data.pm

# 2) premier snapshot (complet), suivants incrémentaux
# (placez le script minimal ci-dessus dans /usr/local/bin/backup-www-data-pm.sh)
sudo install -m 0755 /dev/stdin /usr/local/bin/backup-www-data-pm.sh <<'SH'
#!/usr/bin/env bash
set -Eeuo pipefail
SRC="/srv/data.pm/current/"
DST="/srv/backup/data.pm"
DATE="$(date -u +%Y-%m-%d)"
LAST="$(ls -1 "$DST" 2>/dev/null | sort | tail -n1 || true)"
mkdir -p "$DST/$DATE"
OPTS=(-a --delete --numeric-ids --human-readable --info=stats2)
if [ -n "$LAST" ] && [ -d "$DST/$LAST" ]; then
  rsync "${OPTS[@]}" --link-dest="$DST/$LAST" "$SRC" "$DST/$DATE/"
else
  rsync "${OPTS[@]}" "$SRC" "$DST/$DATE/"
fi
SH

# exécuter
sudo /usr/local/bin/backup-www-data-pm.sh

# 3) rotation: garder 14 jours
find /srv/backup/data.pm -mindepth 1 -maxdepth 1 -type d -mtime +14 -print0 \
  | xargs -0 -r rm -rf

# 4) vérifier l'espace (taille apparente vs réelle)
du -sh /srv/backup/data.pm/* | tail
du -s /srv/backup/data.pm/* | awk '{sum+=$1} END{print "total(KB)=",sum}'

# 5) restaurer un fichier/dossier depuis une date
cp -a /srv/backup/data.pm/2025-08-10/assets/logo.png /srv/data.pm/current/assets/
# ou restauration ciblée avec rsync (préserve droits/ids)
rsync -a /srv/backup/data.pm/2025-08-10/ /srv/data.pm/current/

variante(s) utile(s)

# snapshot distant via SSH (pull depuis prod vers bastion de backup)
SRC="deploy@data.pm:/srv/data.pm/current/"
DST="/srv/backup/data.pm"
DATE="$(date -u +%Y-%m-%d)"
LAST="$(ls -1 "$DST" 2>/dev/null | sort | tail -n1 || true)"
mkdir -p "$DST/$DATE"
RSYNC_SSH="ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3"
if [ -n "$LAST" ] && [ -d "$DST/$LAST" ]; then
  rsync -a --delete --numeric-ids --link-dest="$DST/$LAST" -e "$RSYNC_SSH" "$SRC" "$DST/$DATE/"
else
  rsync -a --delete --numeric-ids -e "$RSYNC_SSH" "$SRC" "$DST/$DATE/"
fi

# exclure caches et artefacts
EXC=(--exclude '.git/' --exclude 'node_modules/' --exclude '*.tmp' --exclude 'cache/')
rsync -a --delete --numeric-ids --link-dest="/srv/backup/data.pm/2025-08-14" \
  "${EXC[@]}" "/srv/data.pm/current/" "/srv/backup/data.pm/2025-08-15/"

# vérifier que les fichiers inchangés sont bien hardlinkés (compte de liens > 1)
# Linux:
find "/srv/backup/data.pm/2025-08-15" -type f -printf '%n %p\n' | awk '$1>1{print}' | head
# macOS/BSD:
find "/srv/backup/data.pm/2025-08-15" -type f -exec stat -f '%l %N' {} \; | awk '$1>1{print}' | head

notes

  • --link-dest crée des hardlinks vers le snapshot précédent pour les fichiers identiques: énorme gain d’espace.
  • utilisez des chemins sans variables ambiguës et des slashs finaux corrects (SRC se termine par / pour copier le contenu).
  • conservez les IDs numériques (--numeric-ids) pour des restaurations fidèles même si les noms d’utilisateurs diffèrent.
  • stockez les snapshots sur un filesystem local fiable (évitez NFS pour des lots massifs de hardlinks).
  • combinez avec flock et timeout en cron pour éviter les chevauchements et blocages.