Aller au contenu

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
# 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

# Sur OVH (temporaire)
df -h /tmp

# Sur S3 Garage
s3cmd du s3://backup-ovh/

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 .s3cfg contient 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)