aws s3 cp backup.tar.gz.gpg s3://backup-mairie/ --endpoint-url https://s3.fr-par.scw.cloud : cette seule ligne, une fois le script écrit, remplace une routine manuelle qui prenait auparavant vingt minutes chaque semaine. Le client concerné, une collectivité territoriale, imposait une clause simple mais stricte dans son cahier des charges : aucune donnée, y compris de sauvegarde, ne devait transiter ni être stockée hors du territoire français.
Cette exigence a écarté d’office les offres de stockage objet des grands fournisseurs américains, malgré leurs régions européennes, et orienté le projet vers un hébergeur français proposant une API compatible S3. Le script qui en résulte reste volontairement simple : il tient en une soixantaine de lignes de bash, sans dépendance exotique, pensé pour être audité en cinq minutes par quiconque le reprendrait après nous.
Le problème à résoudre
Une sauvegarde WordPress complète comporte deux éléments distincts : le dump de la base de données et le dossier wp-content/uploads. Les deux doivent être chiffrés avant de quitter le serveur, et non après leur arrivée sur le stockage distant — sans quoi la fenêtre de transit reste en clair et la garantie de souveraineté perd tout son sens. Le script devait donc chiffrer localement, envoyer le résultat chiffré, et purger les anciennes sauvegardes selon une politique de rétention définie contractuellement.
Le script commenté

Voici la structure retenue, avec GnuPG pour le chiffrement symétrique et le client aws-cli configuré pour pointer vers l’endpoint du fournisseur français plutôt que vers Amazon :
#!/usr/bin/env bash
set -euo pipefail
DATE=$(date +%Y-%m-%d-%H%M)
DEST_DB="db-${DATE}.sql.gz.gpg"
DEST_FILES="uploads-${DATE}.tar.gz.gpg"
BUCKET="s3://backup-mairie"
ENDPOINT="https://s3.fr-par.scw.cloud"
# Export et chiffrement de la base
wp db export - --path=/var/www/site | gzip \
| gpg --batch --passphrase-file /etc/backup/passphrase --symmetric -o "/tmp/${DEST_DB}"
# Archivage et chiffrement des médias
tar czf - wp-content/uploads \
| gpg --batch --passphrase-file /etc/backup/passphrase --symmetric -o "/tmp/${DEST_FILES}"
# Envoi vers le stockage objet souverain
aws s3 cp "/tmp/${DEST_DB}" "${BUCKET}/db/" --endpoint-url "${ENDPOINT}"
aws s3 cp "/tmp/${DEST_FILES}" "${BUCKET}/uploads/" --endpoint-url "${ENDPOINT}"
# Nettoyage local
rm -f "/tmp/${DEST_DB}" "/tmp/${DEST_FILES}"
# Purge des sauvegardes de plus de 30 jours
aws s3 ls "${BUCKET}/db/" --endpoint-url "${ENDPOINT}" \
| awk '{print $4}' \
| while read -r key; do
# logique de purge par âge omise ici pour la lisibilité
echo "Vérification de rétention pour ${key}"
done
La politique de rétention
Le cahier des charges imposait une rétention minimale de 30 jours, mais nous avons opté pour trois cycles distincts afin de couvrir des scénarios de restauration différents :
- Sauvegardes quotidiennes, conservées 7 jours, pour rattraper une erreur de saisie récente ;
- Sauvegardes hebdomadaires, conservées 8 semaines, pour un incident détecté tardivement ;
- Sauvegardes mensuelles, conservées 12 mois, pour répondre à une demande d’audit rétrospectif.
Chaque cycle vit dans un préfixe distinct du bucket, ce qui simplifie l’application de règles de cycle de vie côté fournisseur plutôt que de tout gérer depuis le script.
La passphrase, le maillon fragile
Le chiffrement symétrique repose entièrement sur un fichier de passphrase local, lisible uniquement par l’utilisateur système exécutant la tâche cron. Perdre ce fichier revient à perdre l’accès à toutes les sauvegardes passées — un risque à ne jamais sous-estimer. Nous en conservons une copie hors ligne, dans un coffre physique, distincte de toute copie numérique accessible depuis le serveur lui-même.
Tester la restauration, pas seulement l’envoi
Un script de sauvegarde qui fonctionne n’est pas un script qui restaure correctement. Une fois par mois, une sauvegarde est récupérée depuis le stockage objet, déchiffrée, puis restaurée sur un environnement de test isolé du réseau de production. Cette étape a déjà révélé un problème d’encodage sur un export de base contenant des caractères multioctets, invisible tant qu’on ne restaure pas réellement le fichier.
En résumé
Répondre à une exigence de souveraineté ne se limite pas à changer d’endpoint dans un client S3 : cela implique de repenser où et quand le chiffrement intervient, comment la rétention est structurée, et surtout de vérifier régulièrement que la chaîne complète — export, chiffrement, envoi, restauration — fonctionne de bout en bout. Un script de sauvegarde qu’on n’a jamais restauré n’est, au fond, qu’une hypothèse.