Comment sauvegarder vos fichiers avec rsync, tar, cron et GPG

J'ai écrit cet article originellement en décembre 2011. J'étais architecte logiciel chez un registrar de noms de domaine, passionné par les outils Unix, et je voulais un script de sauvegarde qui ne dépendait ni de Time Machine ni de Dropbox. L'article a eu du succès. Le script fonctionnait.

Quinze ans plus tard, je l'ai ressorti, j'ai un peu grincé des dents, et je l'ai réécrit.

Disque dur externe connecté à un ordinateur portable sur un bureau en bois

Les quatre outils sont les mêmes : rsync, tar, cron et gpg. La stratégie est la même. Ce qui a changé, c'est tout ce qui les entoure — les choses qu'on n'apprend qu'en livrant du code assez longtemps pour le voir échouer de manières inattendues.

Voici cette réécriture. Le script est sur GitHub et fonctionne sur tout système POSIX — macOS, Linux, FreeBSD. L'original ne fonctionnait que sur macOS. C'est une des premières choses que j'ai corrigées.


La stratégie

Toujours la même approche en quatre couches :

  1. rsync — Snapshot incrémental avec liens physiques (seuls les fichiers modifiés consomment de l'espace)
  2. tar — Archives compressées ponctuelles pour les snapshots plus anciens
  3. gpg — Chiffrement avant tout envoi hors de la machine
  4. cron — Rien de tout cela ne s'exécute manuellement

Ce qui est nouveau, c'est un système de rotation : les archives quotidiennes se regroupent en hebdomadaires, les hebdomadaires en mensuelles. En 2011, j'archivais quotidiennement et je m'arrêtais là. C'est bien jusqu'au jour où vous avez besoin de quelque chose datant de trois mois et réalisez que vous l'avez écrasé.


Ce que j'avais mal fait en 2011

Le script n'avait aucune gestion d'erreurs

Le script original continuait joyeusement si rsync échouait. Si le snapshot se cassait silencieusement, vous exécutiez tar sur un répertoire vide et vous chiffriez ça. Vous le découvriez au moment de la restauration — le pire moment possible.

La correction tient en deux caractères :

set -eu

-e quitte immédiatement si une commande retourne un code non nul. -u traite les variables non définies comme des erreurs au lieu de les développer silencieusement en chaînes vides. Utilisez toujours ceci. Je ne le faisais pas en 2011 parce que je ne savais pas.

La commande date ne fonctionnait que sur macOS

L'original utilisait :

YESTERDAY=$(date -v -1d +%Y%m%d)

C'est la syntaxe BSD date — macOS. Sur Linux (GNU date), c'est :

YESTERDAY=$(date -d "1 day ago" +%Y%m%d)

Complètement différent. Le script cassait silencieusement sur toute machine Linux. La version actuelle détecte quelle variante est installée au démarrage et branche en conséquence :

date_subtract() {
    _fmt="$1" _unit="$2" _n="$3"
    if date -d "now" +%s >/dev/null 2>&1; then
        # GNU date (Linux)
        case "$_unit" in
            d) date -d "$_n day ago"   +"$_fmt" ;;
            m) date -d "$_n month ago" +"$_fmt" ;;
        esac
    else
        # BSD date (macOS)
        case "$_unit" in
            d) date -v "-${_n}d" +"$_fmt" ;;
            m) date -v "-${_n}m" +"$_fmt" ;;
        esac
    fi
}

L'astuce de détection : GNU date accepte -d ; BSD date non. Tester une fois, brancher proprement.

Des chemins en dur

Le script original avait les chemins codés en dur : mon nom d'utilisateur, mon e-mail, le chemin de mon disque. C'était un script personnel, donc ça marchait pour moi. Mais ça rendait le partage ou la réutilisation sur une autre machine un exercice de rechercher-remplacer manuel.

La version actuelle utilise un fichier de configuration (backup.conf) avec des valeurs par défaut raisonnables :

BACKUP_SOURCE_DIR="${BACKUP_SOURCE_DIR:-$HOME/Documents}"
BACKUP_HOME="${BACKUP_HOME:-$HOME/backups}"
GPG_RECIPIENT="${GPG_RECIPIENT:-}"       # vide = pas de chiffrement

${VAR:-default} est une expansion de paramètre POSIX — si VAR n'est pas définie ou vide, utiliser la valeur par défaut. Pas de commandes externes, pas de conditionnelles.


Le cœur : rsync avec liens physiques

Cette partie n'a pas beaucoup changé, parce qu'elle était déjà correcte.

rsync -aH --link-dest="$CURRENT_LINK" "$BACKUP_SOURCE_DIR" "$SNAPSHOT_DIR/$NOW"

--link-dest est la clé. Pour chaque fichier qui n'a pas changé depuis le dernier snapshot, rsync crée un lien physique au lieu d'une copie. Le résultat : un snapshot qui ressemble à une copie complète mais ne consomme presque pas d'espace disque supplémentaire.

$ du -sch backups/snapshots/*
143M  snapshots/202602211400
 16K  snapshots/202602211430
 16K  snapshots/202602211500
178M  total

Trois snapshots. Une journée de données réelles.

Un point à connaître : les liens physiques ne fonctionnent pas sur les systèmes de fichiers NTFS ou FAT (montages Samba, disques Windows). rsync bascule silencieusement vers des copies complètes. Ne vous laissez pas surprendre au pire moment.

Après chaque snapshot, mettre à jour le lien symbolique current pour pointer vers le plus récent :

ln -snf "$LATEST" "$CURRENT_LINK"

Le flag -n est important ici. Sans lui, ln suit le lien symbolique existant s'il pointe vers un répertoire et imbrique le nouveau lien à l'intérieur de l'ancien snapshot. Avec -n, il remplace le lien symbolique. Un caractère, un comportement surprenant si vous le ratez.


Archivage et rotation

Les snapshots sont non compressés et navigables — facile de récupérer un fichier d'il y a une heure. Les archives sont compressées et chiffrées — pour le stockage à plus long terme.

Le pipeline actuel :

  1. Snapshots plus anciens qu'aujourd'hui → archive quotidienne .tar.gz
  2. Archives quotidiennes de plus d'une semaine → archive hebdomadaire
  3. Archives hebdomadaires de plus d'un mois → archive mensuelle
$BACKUP_HOME/
├── current -> snapshots/202602211430
├── snapshots/
│   ├── 202602211400/
│   └── 202602211430/
└── archives/
    ├── daily/
    │   └── 20260220.tar.gz.gpg
    ├── weekly/
    │   └── 202601.WK_2.tar.gz.gpg
    └── monthly/
        └── 202512.tar.gz.gpg

Créer une archive à partir des snapshots d'hier :

if [ $(ls -d "$SNAPSHOT_DIR/$YESTERDAY"* 2>/dev/null | wc -l) != "0" ]; then
  tar -czf "$ARCHIVES_DIR/$YESTERDAY.tar.gz" \
    "$SNAPSHOT_DIR/$YESTERDAY"* \
    && rm -rf "$SNAPSHOT_DIR/$YESTERDAY"*
fi

Le && avant rm n'est pas optionnel. Ne supprimer que si l'archive a réussi.


Chiffrement avec GPG

Tout ce qui quitte la machine est chiffré. Cette partie est inchangée depuis 2011, à une addition près : --batch --yes pour l'exécution non interactive dans cron.

gpg --batch --yes -r "$GPG_RECIPIENT" --encrypt-files archive.tar.gz

Sans --batch --yes, GPG demande confirmation dans certaines situations. Un job cron ne peut pas répondre aux invites. Il échoue silencieusement, et vous le découvrez plus tard.

Si vous n'avez pas de clé asymétrique configurée, le chiffrement symétrique convient pour les sauvegardes locales/mono-utilisateur :

gpg --symmetric --cipher-algo AES256 archive.tar.gz

Pour déchiffrer quand vous en avez besoin :

gpg --decrypt-files archive.tar.gz.gpg

Planification avec cron

crontab -e

Toutes les 30 minutes pendant les heures de travail en semaine :

*/30 8-18 * * 1-5  /path/to/backup.sh

Le caractère % est spécial dans crontab — il est traité comme un saut de ligne. Si votre commande contient % (fréquent dans les chaînes de format de date), échappez-les avec \%.


Tests

En 2011, je testais le script en l'exécutant et en vérifiant la sortie. C'est comme ça que la plupart des scripts sont « testés ».

La version actuelle dispose d'une suite de tests en shell POSIX qui couvre les helpers de portabilité, le chargement de configuration et le pipeline de bout en bout — y compris la déduplication par liens physiques. Elle s'exécute sur Ubuntu (GNU) et macOS (BSD) via GitHub Actions, plus l'analyse statique ShellCheck.

Ce n'est pas parce que les scripts de sauvegarde ont besoin de tests sophistiqués. C'est parce que les scripts qui s'exécutent sans surveillance à 2h du matin, sur une machine que vous ne surveillez pas, ont besoin d'être fiables. Les tests sont la façon dont vous construisez cette confiance avant d'en avoir besoin.


Restauration

Les snapshots sont de simples répertoires — copiez simplement ce dont vous avez besoin :

cp ~/backups/current/Documents/report.txt ~/Documents/

Depuis une archive chiffrée :

gpg --decrypt-files ~/backups/archives/daily/20260220.tar.gz.gpg
tar -xzf ~/backups/archives/daily/20260220.tar.gz -C /tmp/restore/

Testez ceci avant d'en avoir besoin. Une sauvegarde que vous n'avez jamais restaurée est une sauvegarde que vous n'avez pas vérifiée.


Conclusion

Les outils — rsync, tar, cron, gpg — sont les mêmes que ceux que j'utilisais en 2011. Ils seront encore là en 2036. C'est ça, les fondamentaux Unix : ils ne deviennent pas obsolètes.

Ce qui change, c'est le savoir-faire autour d'eux. La portabilité. La gestion d'erreurs. La configuration. Les tests. La différence entre un script qui marche sur votre machine et un script auquel vous pouvez faire confiance.

Le code source complet est sur github.com/aadlani/backup-manager. Clonez-le, adaptez-le, faites-en le vôtre.

Written by Anouar Adlani, Group CTO at EBRAND, based in Luxembourg. Read more articles →