Backup Docker OVH vers S3 Garage¶
Vue d'ensemble¶
Sauvegarde automatisée quotidienne des services Docker du serveur OVH vers le stockage S3 Garage hébergé sur le LXC 133 (Proxmox3), avec rétention GFS (Grandfather-Father-Son).
Architecture¶
┌─────────────────────┐ HTTPS ┌─────────────────────┐
│ Serveur OVH │ ─────────────────────► │ S3 Garage │
│ ovh.mangi.fr │ s3.mangi.fr │ LXC 133 │
│ │ │ 192.168.1.183 │
│ ┌───────────────┐ │ │ │
│ │ n8n │ │ │ ┌───────────────┐ │
│ │ (PostgreSQL) │──┼─── pg_dumpall ─────────┼─►│ backup-ovh/ │ │
│ └───────────────┘ │ │ │ ├─ daily/ │ │
│ ┌───────────────┐ │ │ │ ├─ weekly/ │ │
│ │ Shlink │ │ │ │ ├─ monthly/ │ │
│ │ (MariaDB) │──┼─── mariadb-dump ───────┼─►│ └─ yearly/ │ │
│ └───────────────┘ │ │ └───────────────┘ │
└─────────────────────┘ └─────────────────────┘
Configuration¶
Stockage S3¶
| Paramètre | Valeur |
|---|---|
| Endpoint | https://s3.mangi.fr |
| Bucket | backup-ovh |
| Access Key | GKcb3fc56e37dabc4ce9f7cef7 |
| Région | EU (Garage) |
Services sauvegardés¶
| Service | Base de données | Utilisateur DB | Configs |
|---|---|---|---|
| n8n | PostgreSQL 16 | jeremie |
/root/docker/n8n-hosting/docker-compose/withPostgres/ |
| Shlink | MariaDB 10.8 | root |
/home/debian/docker/docker-shlink/ |
Planification¶
| Paramètre | Valeur |
|---|---|
| Fréquence | Quotidienne à 03:00 |
| Logs | /var/log/backup-docker.log |
Rétention GFS¶
Le script utilise une stratégie GFS (Grandfather-Father-Son) configurable :
| Type | Rétention | Déclencheur | Stockage estimé |
|---|---|---|---|
| daily | 7 derniers | Tous les jours | ~120 MB |
| weekly | 4 derniers | Dimanche | ~70 MB |
| monthly | 6 derniers | 1er du mois | ~100 MB |
| yearly | 2 derniers | 1er janvier | ~35 MB |
Total estimé : ~325 MB
Pour modifier la rétention, éditer les variables en haut du script :
# Rétention GFS (Grandfather-Father-Son)
KEEP_DAILY=7 # Nombre de backups quotidiens à conserver
KEEP_WEEKLY=4 # Nombre de backups hebdomadaires (dimanche)
KEEP_MONTHLY=6 # Nombre de backups mensuels (1er du mois)
KEEP_YEARLY=2 # Nombre de backups annuels (1er janvier)
Fichiers sur OVH¶
Script principal¶
Chemin : /root/scripts/backup-docker-s3.sh
#!/bin/bash
# Backup Docker volumes vers S3 Garage
set -euo pipefail
BACKUP_DIR="/tmp/docker-backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
S3_BUCKET="s3://backup-ovh"
RETENTION_DAYS=30
# Logging
log() {
echo "[$(date "+%Y-%m-%d %H:%M:%S")] $1"
}
# Cleanup on exit
cleanup() {
log "Nettoyage du répertoire temporaire..."
rm -rf "$BACKUP_DIR"
}
trap cleanup EXIT
mkdir -p "$BACKUP_DIR"
log "=== Démarrage backup Docker ==="
# n8n (PostgreSQL)
log "Backup n8n - PostgreSQL..."
docker exec withpostgres-postgres-1 pg_dumpall -U jeremie > "$BACKUP_DIR/n8n-postgres-$TIMESTAMP.sql"
log "Backup n8n - Configs..."
tar czf "$BACKUP_DIR/n8n-configs-$TIMESTAMP.tar.gz" -C /root/docker/n8n-hosting/docker-compose/withPostgres .
# Shlink (MariaDB)
log "Backup Shlink - MariaDB..."
docker exec docker-shlink_database_1 mariadb-dump -u root -pMbjutgo7 --all-databases > "$BACKUP_DIR/shlink-mariadb-$TIMESTAMP.sql" 2>/dev/null
log "Backup Shlink - Configs..."
tar czf "$BACKUP_DIR/shlink-configs-$TIMESTAMP.tar.gz" -C /home/debian/docker/docker-shlink .
# Upload vers S3
log "Upload vers S3 Garage..."
for file in "$BACKUP_DIR"/*; do
filename=$(basename "$file")
s3cmd put "$file" "$S3_BUCKET/$TIMESTAMP/$filename" --quiet
log " Uploadé: $filename"
done
# Rotation (supprimer backups > 30 jours)
log "Rotation des anciens backups (> ${RETENTION_DAYS} jours)..."
CUTOFF_DATE=$(date -d "-${RETENTION_DAYS} days" +%Y%m%d)
s3cmd ls "$S3_BUCKET/" | awk '{print $NF}' | grep -oE '[0-9]{8}_[0-9]{6}' | sort -u | while read -r backup_date; do
date_only=${backup_date%%_*}
if [[ "$date_only" < "$CUTOFF_DATE" ]]; then
log " Suppression backup: $backup_date"
s3cmd del --recursive "$S3_BUCKET/$backup_date/" --quiet 2>/dev/null || true
fi
done
log "=== Backup terminé avec succès ==="
Configuration s3cmd¶
Chemin : /root/.s3cfg
[default]
access_key = GKcb3fc56e37dabc4ce9f7cef7
secret_access_key = 943eeb3f55b5fa875d93acc59b6a500e76313672fd1336651f3e5e9c36c96bea
host_base = s3.mangi.fr
host_bucket = s3.mangi.fr
use_https = True
signature_v2 = False
Crontab¶
# Backup Docker vers S3 - tous les jours à 3h
0 3 * * * /root/scripts/backup-docker-s3.sh >> /var/log/backup-docker.log 2>&1
Commandes utiles¶
Exécution manuelle¶
# Lancer le backup manuellement
/root/scripts/backup-docker-s3.sh
# Voir les logs en temps réel
tail -f /var/log/backup-docker.log
Gestion des backups S3¶
# Lister les types de backup
s3cmd ls s3://backup-ovh/
# Lister les backups quotidiens
s3cmd ls s3://backup-ovh/daily/
# Lister le contenu d'un backup spécifique
s3cmd ls s3://backup-ovh/daily/20260131_030000/
# Voir la taille totale
s3cmd du s3://backup-ovh/
# Télécharger un backup complet
s3cmd get --recursive s3://backup-ovh/daily/20260131_030000/ /tmp/restore/
# Télécharger un fichier spécifique
s3cmd get s3://backup-ovh/monthly/20260101_030000/n8n-postgres-20260101_030000.sql /tmp/
Restauration¶
Restaurer PostgreSQL (n8n)¶
# Télécharger le dump (exemple: backup mensuel du 1er janvier)
s3cmd get s3://backup-ovh/monthly/20260101_030000/n8n-postgres-20260101_030000.sql /tmp/
# Restaurer dans le conteneur
cat /tmp/n8n-postgres-*.sql | docker exec -i withpostgres-postgres-1 psql -U jeremie
Restaurer MariaDB (Shlink)¶
# Télécharger le dump
s3cmd get s3://backup-ovh/daily/20260131_030000/shlink-mariadb-20260131_030000.sql /tmp/
# Restaurer dans le conteneur
cat /tmp/shlink-mariadb-*.sql | docker exec -i docker-shlink_database_1 mariadb -u root -pMbjutgo7
Restaurer les configs Docker¶
# Télécharger et extraire
s3cmd get s3://backup-ovh/weekly/20260126_030000/n8n-configs-20260126_030000.tar.gz /tmp/
tar xzf /tmp/n8n-configs-*.tar.gz -C /root/docker/n8n-hosting/docker-compose/withPostgres/
# Recréer les conteneurs
cd /root/docker/n8n-hosting/docker-compose/withPostgres && docker compose up -d
Diagnostic¶
Vérifier la connectivité S3¶
# Test basique
s3cmd ls
# Test d'écriture
echo "test" > /tmp/test.txt
s3cmd put /tmp/test.txt s3://backup-ovh/test.txt
s3cmd del s3://backup-ovh/test.txt
Vérifier le cron¶
# Voir les tâches cron
crontab -l
# Vérifier les logs système
grep backup /var/log/syslog | tail -20
Vérifier l'espace disque¶
Structure des backups¶
s3://backup-ovh/
├── daily/
│ ├── 20260131_030000/
│ │ ├── n8n-postgres-20260131_030000.sql # ~5 MB
│ │ ├── n8n-configs-20260131_030000.tar.gz # ~3 KB
│ │ ├── shlink-mariadb-20260131_030000.sql # ~12 MB
│ │ └── shlink-configs-20260131_030000.tar.gz # ~1 MB
│ └── 20260130_030000/
│ └── ...
├── weekly/
│ └── 20260126_030000/ # Dimanche
│ └── ...
├── monthly/
│ └── 20260101_030000/ # 1er du mois
│ └── ...
└── yearly/
└── 20260101_030000/ # 1er janvier
└── ...
Notes techniques¶
Pourquoi s3cmd et non rclone ?¶
Rclone présente une incompatibilité avec Garage via le proxy NPM (erreur signed header 'accept-encoding' is not present). s3cmd fonctionne correctement avec cette configuration.
Sécurité¶
- Les credentials MariaDB sont en clair dans le script (acceptable car root-only)
- Le fichier
.s3cfgcontient les clés S3 (permissions 600) - Les dumps sont supprimés localement après upload
Limitations¶
- Pas de chiffrement des backups (à implémenter si nécessaire)
- Pas de notification en cas d'échec (utiliser les logs)
- Le backup est synchrone (environ 30 secondes)