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-destcré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 (
SRCse 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
flockettimeouten cron pour éviter les chevauchements et blocages.