En 2017, j'ai perdu deux semaines de logs serveur à cause d'une rotation cron mal configurée qui écrasait les archives au lieu de les incrémenter. Depuis, j'administre 4 serveurs Linux personnels (deux VPS Hetzner, un NAS QNAP maison, un Raspberry Pi 4 comme backup local) avec un système de backup automatisé cron + rsync qui tourne depuis 7 ans sans interruption. Ce guide documente exactement ce que j'utilise en 2026, erreurs comprises.
La prémisse est simple : l'automatisation n'est pas optionnelle pour les backups. Un humain oublie. Un cron, non. Sur mes 4 serveurs, les backups tournent à 02h00 chaque nuit — je n'y pense plus depuis 2018, et pourtant j'ai restauré des données deux fois en 2024 (once une corruption de base de données PostgreSQL, once une suppression accidentelle de 180 Go d'archives photo).
Pourquoi automatiser ses backups
La réponse courte : parce que la mémoire humaine est un piètre planificateur de tâches récurrentes critiques.
Les humains oublient, les scripts non. En 2024, l'enquête annuelle de Backblaze sur les habitudes de sauvegarde révèle que 67 % des utilisateurs qui déclarent « sauvegarder régulièrement » le font en réalité de manière irrégulière — avec des gaps de 2 à 6 semaines entre les sauvegardes. La régularité perçue est systématiquement supérieure à la régularité réelle. Un job cron à 02h00 quotidien tourne sans exception : pendant les vacances, les week-ends, les nuits de coupure de courant (si le serveur a un UPS).
La sauvegarde incrémentielle rend l'automatisation viable. Sans rsync et ses transferts delta, sauvegarder 500 Go de données chaque nuit serait prohibitif (50 à 100 Go de transfert réseau pour un répertoire photos standard). Avec rsync, seuls les octets modifiés depuis le dernier backup sont transférés. Sur mes sauvegardes nocturnes vers le NAS LAN (180 Go de données totales), le transfert quotidien oscille entre 200 Mo et 2 Go selon l'activité. Le backup complet initial a pris 4h. Chaque backup suivant prend entre 3 et 25 minutes.
La scalabilité vers plusieurs machines. Quand on passe de 1 à 4 serveurs, le backup manuel devient une procédure de 45 minutes qui n'est jamais faite correctement. Un script cron centralisé qui tire les backups de toutes les machines vers un point central prend la même chose en temps machine et zéro en temps humain.
Le monitoring automatique détecte les échecs. Un backup silencieusement défaillant depuis 3 semaines est plus dangereux qu'un backup jamais configuré — au moins, on sait qu'il n'existe pas. Avec un ping Healthchecks.io en fin de script, je reçois un email dès qu'un backup échoue ou ne tourne pas à l'heure prévue.
rsync en 2026 : syntaxe et options essentielles
rsync est un outil de synchronisation incrémentielle de fichiers développé par Andrew Tridgell en 1996. Son algorithme delta-transfer transfère uniquement les blocs modifiés d'un fichier plutôt que le fichier entier — c'est le fondement de son efficacité pour les backups quotidiens.
Syntaxe de base :
rsync -avz --delete SOURCE DESTINATION
Options expliquées :
-a(archive) : préserve permissions, timestamps, liens symboliques, propriétaire, groupe. Équivalent à-rlptgoD.-v(verbose) : affiche les fichiers transférés.-z(compress) : active la compression pendant le transfert. Utile sur WAN, inutile sur LAN Gigabit.--delete: supprime sur la destination les fichiers qui n'existent plus à la source.
Backup local vers NAS :
rsync -av --delete /home/eric/ /mnt/nas/backup/eric-home/
Backup vers serveur distant via SSH :
rsync -avz --delete -e "ssh -i /home/eric/.ssh/backup_key -p 22" \
/var/www/ \
backup@192.168.1.100:/data/backups/www/
Options avancées 2026 :
# --mkpath : crée les répertoires destination si absents (option récente, rsync 3.2.3+)
rsync -av --mkpath /source/ user@host:/chemin/inexistant/
# --exclude : exclure des patterns
rsync -av --exclude='*.log' --exclude='tmp/' --exclude='.git/' /source/ /dest/
# --bwlimit : limiter la bande passante (en Ko/s)
rsync -av --bwlimit=20000 /source/ user@host:/dest/
# --checksum : force la vérification par hash (plus lent mais plus fiable)
rsync -avc --checksum /source/ /dest/
# Dry run : simuler sans rien modifier
rsync -avn --delete /source/ /dest/
Exemple complet : backup VPS vers Hetzner Storage Box :
rsync -avz --delete \
--exclude='*.sock' \
--exclude='/proc' \
--exclude='/sys' \
--exclude='/dev' \
--exclude='/run' \
--bwlimit=30000 \
-e "ssh -i /root/.ssh/hetzner_backup -p 23" \
/etc/ /home/ /var/www/ /var/backups/ \
u123456@u123456.your-storagebox.de:/backups/vps-main/
Ce script s'exécute sur mon VPS principal depuis 2021. Il transfère en moyenne 800 Mo par nuit vers le Storage Box Hetzner (prix 2026 : 3,81 €/mois pour 100 Go).
Cron : syntaxe complète et exemples pratiques
Cron est le scheduler de tâches Unix standard, présent sur toutes les distributions Linux. La commande crontab -e édite la table des tâches cron de l'utilisateur courant.
Syntaxe des cinq champs :
# ┌───── minute (0 - 59)
# │ ┌───── heure (0 - 23)
# │ │ ┌───── jour du mois (1 - 31)
# │ │ │ ┌───── mois (1 - 12)
# │ │ │ │ ┌───── jour de la semaine (0 - 7, 0 et 7 = dimanche)
# │ │ │ │ │
# * * * * * commande
Exemples d'horaires courants :
# Toutes les minutes (test / monitoring)
* * * * * /usr/local/bin/monitor.sh
# Toutes les heures à H:00
0 * * * * /usr/local/bin/hourly-backup.sh
# Tous les jours à 02h00
0 2 * * * /usr/local/bin/daily-backup.sh
# Tous les dimanches à 03h00
0 3 * * 0 /usr/local/bin/weekly-backup.sh
# Le 1er de chaque mois à 04h00
0 4 1 * * /usr/local/bin/monthly-backup.sh
# Toutes les 6 heures
0 */6 * * * /usr/local/bin/incremental.sh
# Jours ouvrés (lundi-vendredi) à 08h30
30 8 * * 1-5 /usr/local/bin/workday-sync.sh
Variables d'environnement importantes dans crontab :
# cron n'hérite pas du PATH utilisateur — toujours le définir
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=admin@mondomaine.com
# Timezone explicite (évite les surprises horaires)
CRON_TZ=Europe/Paris
0 2 * * * /usr/local/bin/daily-backup.sh >> /var/log/backup.log 2>&1
systemd-timers : alternative moderne
Sur les systèmes avec systemd (Ubuntu 16.04+, Debian 9+, CentOS 7+), les timers systemd offrent une meilleure traçabilité :
# /etc/systemd/system/backup-daily.timer
[Unit]
Description=Backup quotidien rsync
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true # Rattrape les jobs manqués si le serveur était éteint
Unit=backup-daily.service
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup-daily.service
[Unit]
Description=Backup quotidien rsync
After=network.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/daily-backup.sh
Nice=19
IOSchedulingClass=idle
# Activation
systemctl enable backup-daily.timer
systemctl start backup-daily.timer
systemctl list-timers # Vérifier l'état
journalctl -u backup-daily.service # Logs
L'avantage clé de Persistent=true : si le serveur était éteint à 02h00, le job s'exécute au prochain démarrage. Cron standard manque les jobs si la machine est hors ligne.
Script backup complet : logging, error handling, alertes
Voici le script que j'utilise sur mes serveurs depuis 2019, amélioré itérativement. Il couvre le logging structuré, la gestion d'erreur, et les notifications.
#!/bin/bash
# /usr/local/bin/daily-backup.sh
# Backup quotidien rsync avec logging et alertes
# Auteur : configuration personnelle, adaptable librement
set -euo pipefail
# ── Configuration ──────────────────────────────────────────────────────────────
BACKUP_SOURCE="/home /etc /var/www /var/backups"
BACKUP_DEST="/mnt/nas/backups/vps-main"
LOG_FILE="/var/log/backup-daily.log"
MAX_LOG_SIZE_MB=50 # Rotation log si >50 Mo
LOCK_FILE="/tmp/backup-daily.lock"
HEALTHCHECK_URL="https://hc-ping.com/VOTRE-UUID-ICI" # Healthchecks.io
NOTIFY_EMAIL="admin@mondomaine.com"
RSYNC_OPTIONS="-avz --delete --delete-after --exclude='*.sock' --exclude='*.pid'"
SSH_KEY="/root/.ssh/backup_key"
REMOTE_HOST="backup@192.168.1.100"
REMOTE_PATH="/data/backups"
BWLIMIT=30000 # Ko/s — 30 Mo/s max
# ── Fonctions ──────────────────────────────────────────────────────────────────
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
send_alert() {
local subject="$1"
local body="$2"
echo "$body" | mail -s "$subject" "$NOTIFY_EMAIL" 2>/dev/null || true
# Healthchecks.io : ping /fail pour notifier l'échec
curl -fsS --retry 3 --max-time 10 "${HEALTHCHECK_URL}/fail" \
--data-raw "$body" > /dev/null 2>&1 || true
}
rotate_log() {
local size_mb
size_mb=$(du -sm "$LOG_FILE" 2>/dev/null | cut -f1 || echo 0)
if [ "$size_mb" -gt "$MAX_LOG_SIZE_MB" ]; then
mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d)"
gzip "${LOG_FILE}.$(date +%Y%m%d)" 2>/dev/null || true
log "Log rotaté (taille dépassée)"
fi
}
cleanup() {
rm -f "$LOCK_FILE"
}
# ── Vérifications préliminaires ────────────────────────────────────────────────
# Lock : éviter les exécutions parallèles
if [ -f "$LOCK_FILE" ]; then
log "ERREUR : backup déjà en cours (lockfile présent). Abandon."
send_alert "[BACKUP] Conflit lockfile sur $(hostname)" \
"Le backup était déjà en cours. Vérifiez le PID dans $LOCK_FILE."
exit 1
fi
trap cleanup EXIT
echo $$ > "$LOCK_FILE"
rotate_log
log "═══ Début backup quotidien ═══"
# Vérifier la connectivité destination
if ! ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=yes \
"$REMOTE_HOST" "echo OK" > /dev/null 2>&1; then
log "ERREUR : impossible de joindre $REMOTE_HOST"
send_alert "[BACKUP] Échec connexion destination $(hostname)" \
"SSH vers $REMOTE_HOST impossible. Vérifier réseau/clé."
exit 2
fi
# ── Exécution rsync ────────────────────────────────────────────────────────────
ERRORS=0
START_TIME=$(date +%s)
for SOURCE_DIR in $BACKUP_SOURCE; do
if [ ! -d "$SOURCE_DIR" ]; then
log "AVERTISSEMENT : répertoire source absent : $SOURCE_DIR"
continue
fi
DEST_DIR="${REMOTE_PATH}/$(basename "$SOURCE_DIR")"
log "Synchronisation : $SOURCE_DIR → $REMOTE_HOST:$DEST_DIR"
if rsync $RSYNC_OPTIONS \
--bwlimit="$BWLIMIT" \
-e "ssh -i $SSH_KEY -o BatchMode=yes" \
"$SOURCE_DIR/" \
"$REMOTE_HOST:$DEST_DIR/" \
>> "$LOG_FILE" 2>&1; then
log "OK : $SOURCE_DIR synchronisé"
else
log "ERREUR : échec rsync pour $SOURCE_DIR (code retour : $?)"
ERRORS=$((ERRORS + 1))
fi
done
# ── Résultat final ─────────────────────────────────────────────────────────────
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
DURATION_MIN=$((DURATION / 60))
if [ "$ERRORS" -eq 0 ]; then
log "Backup terminé avec succès en ${DURATION_MIN} min"
# Ping Healthchecks.io : succès
curl -fsS --retry 3 --max-time 10 "$HEALTHCHECK_URL" > /dev/null 2>&1 || true
else
log "Backup terminé avec $ERRORS erreur(s) en ${DURATION_MIN} min"
send_alert "[BACKUP] $ERRORS erreur(s) sur $(hostname)" \
"Backup terminé avec $ERRORS erreur(s). Durée : ${DURATION_MIN} min. Voir $LOG_FILE."
fi
log "═══ Fin backup ═══"
Ajout crontab :
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 2 * * * /usr/local/bin/daily-backup.sh
Ce script tourne sur 3 de mes 4 serveurs. La durée moyenne est de 8 minutes pour 180 Go de données totales (environ 600 Mo transférés par nuit).
rsnapshot : rotation automatique des snapshots
rsnapshot est un wrapper rsync qui implémente automatiquement la rotation des snapshots avec des hard links — chaque snapshot ressemble à une copie complète mais ne stocke réellement que les fichiers nouveaux ou modifiés.
Installation :
apt install rsnapshot # Debian/Ubuntu
yum install rsnapshot # CentOS/RHEL
Configuration /etc/rsnapshot.conf (extrait) :
# IMPORTANT : tabs obligatoires entre les champs (pas d'espaces)
config_version 1.2
# Répertoire racine des snapshots
snapshot_root /mnt/nas/rsnapshot/
# Commande rsync
cmd_rsync /usr/bin/rsync
# Intervalles de rotation
# Syntaxe : retain <nom> <nombre>
retain hourly 6 # 6 snapshots horaires
retain daily 7 # 7 jours
retain weekly 4 # 4 semaines
retain monthly 12 # 12 mois
# Options rsync globales
rsync_short_args -az
rsync_long_args --delete --delete-excluded --numeric-ids
# Sources à sauvegarder
backup /home/eric/ localhost/
backup /etc/ localhost/
backup /var/www/ localhost/
backup root@192.168.1.10:/home/ web-server/
backup root@192.168.1.10:/etc/ web-server/
# Exclusions
exclude *.log
exclude tmp/
exclude .cache/
Crontab rsnapshot :
# Snapshots horaires (6h - 22h)
0 6-22 * * * /usr/bin/rsnapshot hourly
# Daily à 02h30
30 2 * * * /usr/bin/rsnapshot daily
# Weekly le lundi à 03h00
0 3 * * 1 /usr/bin/rsnapshot weekly
# Monthly le 1er à 04h00
0 4 1 * * /usr/bin/rsnapshot monthly
Résultat sur disque :
/mnt/nas/rsnapshot/
├── daily.0/ ← hier
│ ├── localhost/
│ │ ├── home/eric/
│ │ ├── etc/
│ │ └── var/www/
│ └── web-server/
├── daily.1/ ← avant-hier
├── daily.2/
...
├── weekly.0/ ← semaine dernière
├── monthly.0/ ← mois dernier
La restauration est triviale : cp -a /mnt/nas/rsnapshot/daily.2/localhost/home/eric/fichier.txt /home/eric/. Pas d'outil spécial, juste un copier-coller depuis un répertoire de snapshot.
Espace disque : avec 180 Go de données source et une rotation 7 daily + 4 weekly + 12 monthly = 23 snapshots, j'utilise environ 320 Go sur le NAS (160 Go de données dupliquées car les fichiers inchangés partagent des hard links entre snapshots).
BorgBackup : déduplication et chiffrement pour les backups sensibles
rsync est excellent pour la synchronisation de fichiers. BorgBackup est supérieur quand on a besoin de déduplication bloc, chiffrement natif, et compression variable. C'est mon outil pour les backups offsite vers Hetzner Storage Box et les sauvegardes contenant des données personnelles.
Comparaison rsync vs BorgBackup :
| Critère | rsync | BorgBackup |
|---|---|---|
| Déduplication | Non (hard links via rsnapshot) | Oui (bloc variable ~512KB) |
| Chiffrement at-rest | Non | AES-256 natif |
| Compression | Pendant transfert (-z) | LZ4/ZSTD/ZLIB intégré |
| Restauration fichier unique | Simple (cp depuis snapshot) | borg extract |
| Espace disque | Supérieur (no dedup réel) | 40-60% inférieur sur données mixtes |
| Complexité setup | Faible | Modérée |
Installation et initialisation :
apt install borgbackup # Ubuntu 22.04 : version 1.2.x
# Initialiser un dépôt chiffré (mode recommandé)
borg init --encryption=repokey-blake2 user@nas:/data/borg-repo/
# Sauvegarder le passphrase dans un gestionnaire de mots de passe
# ET dans un fichier sécurisé hors de la machine à sauvegarder
Script backup Borg avec prune :
#!/bin/bash
export BORG_PASSPHRASE="VOTRE_PASSPHRASE_LONGUE_ET_COMPLEXE"
export BORG_REPO="user@nas:/data/borg-repo"
# Création du snapshot avec timestamp
borg create \
--verbose \
--compression lz4 \
--exclude-caches \
--exclude '/home/*/.cache' \
--exclude '/home/*/.local/share/Trash' \
"${BORG_REPO}::$(hostname)-$(date +%Y%m%d-%H%M%S)" \
/home /etc /var/www
# Politique de rétention : 7 daily + 4 weekly + 12 monthly
borg prune \
--verbose \
--list \
--keep-daily=7 \
--keep-weekly=4 \
--keep-monthly=12 \
"${BORG_REPO}"
# Vérification intégrité (hebdomadaire recommandé)
# borg check "${BORG_REPO}"
Restauration :
# Lister les archives
borg list "${BORG_REPO}"
# Restaurer un fichier spécifique
borg extract "${BORG_REPO}::serveur-20260608-020000" home/eric/documents/important.pdf
# Restauration complète
borg extract "${BORG_REPO}::serveur-20260608-020000"
Quand préférer Borg à rsync :
- Données sensibles (documents personnels, bases de données contenant des PII)
- Backups vers stockage cloud ou hôte non contrôlé
- Volume de données avec beaucoup de redondance (photos, code source avec historique git)
- Besoin d'un historique de snapshots compressés sur espace limité
Sur mon Hetzner Storage Box 100 Go, je stocke 4 mois de snapshots Borg de 85 Go de données source, compressés à 52 Go — un ratio de 0,61. Avec rsync pur, j'aurais besoin de 4× plus d'espace pour le même historique.
Monitoring et alerting : ne jamais supposer que le backup tourne
Un backup qui échoue silencieusement depuis 3 semaines est une catastrophe en devenir. Le monitoring n'est pas optionnel.
Healthchecks.io : le plus simple
Healthchecks.io est un service de monitoring cron par ping. Créez un check avec l'intervalle attendu (daily 24h + 1h de grâce), ajoutez le ping en fin de script. Si le ping ne vient pas, vous recevez un email.
# En fin de script backup, si succès :
curl -fsS --retry 3 --max-time 10 \
"https://hc-ping.com/VOTRE-UUID" > /dev/null 2>&1 || true
# En cas d'échec, pinger l'endpoint /fail :
curl -fsS --retry 3 --max-time 10 \
"https://hc-ping.com/VOTRE-UUID/fail" > /dev/null 2>&1 || true
Plan gratuit : 20 checks. Suffisant pour 4 serveurs avec daily + weekly. Plan Business à 20 USD/an pour les équipes.
Vérification des logs avec logcheck ou simple grep :
# Crontab : vérifier les erreurs dans les logs backup toutes les heures
0 * * * * grep -i "erreur\|error\|fail" /var/log/backup-daily.log \
| tail -5 \
| mail -s "[$(hostname)] Erreurs backup" admin@mondomaine.com 2>/dev/null || true
Script de vérification de fraîcheur des backups :
#!/bin/bash
# Vérifie que le backup le plus récent est bien de moins de 26h
BACKUP_DIR="/mnt/nas/backups"
MAX_AGE_HOURS=26
find "$BACKUP_DIR" -name "*.log" -newer \
<(date -d "$MAX_AGE_HOURS hours ago") > /tmp/recent_backups 2>/dev/null
if [ ! -s /tmp/recent_backups ]; then
echo "ALERTE : Aucun backup récent sur $(hostname)" \
| mail -s "[BACKUP] Backup trop ancien !" admin@mondomaine.com
fi
Prometheus + Grafana pour les homelab avancés :
Pour les environnements avec plusieurs serveurs, le node_exporter Prometheus expose des métriques de fichiers système. Une règle d'alerte Alertmanager peut déclencher une notification Slack si le timestamp du dernier backup dépasse un seuil :
# prometheus/rules/backup.yml
groups:
- name: backup_freshness
rules:
- alert: BackupStaleness
expr: |
(time() - node_filesystem_file_content_mtime_seconds{
mountpoint="/mnt/nas",
path="/data/backups/vps-main"
}) > 90000
for: 1h
labels:
severity: warning
annotations:
summary: "Backup trop ancien sur {{ $labels.instance }}"
description: "Dernier backup il y a {{ $value | humanizeDuration }}"
Ce dashboard Grafana tourne sur mon Raspberry Pi 4 (8 Go RAM) qui sert aussi de monitoring central pour mes 4 serveurs. Le coût total de l'infrastructure monitoring : 0 € (Prometheus + Grafana open source, hébergés sur le Pi).
La stratégie 3-2-1 reste le cadre de référence pour tout plan de backup. Pour comprendre comment cron + rsync s'intègre dans une architecture 3-2-1 complète, voir la stratégie backup 3-2-1 expliquée. Si vous gérez également des machines Windows ou Mac, le guide backup automatique Windows et Mac couvre les équivalents GUI pour ces plateformes.
Pour la récupération de données quand la prévention a échoué, le guide récupération données disque dur 2026 documente les options DIY et professionnelles, et le comparatif des meilleurs logiciels de récupération de données 2026 compare les outils du marché. Si vous devez estimer le coût d'une récupération pro, consultez notre guide des prix récupération données disque dur 2026.
★ Éditeur fondé en 2004 · ✓ Garantie 30 jours · Version gratuite jusqu'à 2 Go
Backup automatique GUI pour Windows et MacSi tu administres aussi des machines Windows/Mac en parallèle de tes serveurs Linux — EaseUS Todo Backup automatise les sauvegardes sans ligne de commande · Version gratuite disponible→★ Éditeur fondé en 2004 · ✓ Garantie 30 jours · Version gratuite jusqu'à 2 Go
Voir l'offre EaseUS Data Recovery Wizard30 jours satisfait ou remboursé→