La protection des données MongoDB n'est réelle que lorsque vous prouvez régulièrement que vous pouvez les restaurer. Ce guide vous donne des étapes pratiques, prêtes à copier-coller, pour les sauvegardes, restaurations, validations, planification de rollback et reprise après sinistre. Vous terminerez avec un pilote local testé et un chemin clair vers la production.

## Pour qui ce guide

- Développeurs, consultants DevOps et équipes de startups qui ont besoin d'un plan fiable et sans drame

- Toute personne souhaitant un workflow répétable testable d'abord en local

## Ce que vous allez faire

- Effectuer des sauvegardes MongoDB cohérentes (mongodump et snapshots filesystem)

- Restaurer vers une cible sûre, valider minutieusement et planifier une bascule propre

- Répéter des scénarios de sinistre et mesurer RTO/RPO

## Aperçu du workflow

Un workflow de production fiable est simple et explicite :

- Choisir votre méthode de sauvegarde

- mongodump pour les sauvegardes logiques ; utilisez --oplog pour les replica sets

- Snapshots filesystem pour les charges de travail à haut débit et les clusters shardés

- Planifier et stocker les sauvegardes

- Exécuter sur un secondaire quand possible, compresser et chiffrer au repos, conserver plusieurs générations

- Tester les restaurations régulièrement

- Restaurer vers un namespace de staging, jamais directement en production

- Valider les résultats

- Comptages, hachages, requêtes d'échantillon, index, utilisateurs/rôles, fCV

- Planifier le rollback et la bascule

- Restaurer vers des collections parallèles, puis permuter atomiquement

- Répéter la reprise après sinistre

- Chronométrer chaque étape, corriger les goulots d'étranglement et documenter le runbook

## Exemples de sauvegarde (Replica Set)

Créez des utilisateurs aux privilèges minimaux pour la sauvegarde et la restauration.

// Dans mongosh sur le primaire
use admin
// Utilisateur de sauvegarde
db.createUser({
 user: "backup",
 pwd: "REDACTED_STRONG_PW",
 roles: [{ role: "backup", db: "admin" }]
})
// Utilisateur de restauration (utiliser seulement en non-prod ou cible DR)
db.createUser({
 user: "restore",
 pwd: "REDACTED_STRONG_PW",
 roles: [{ role: "restore", db: "admin" }]
}) 
 Variables d'environnement utiles :

export RS_URI='mongodb://backup:REDACTED_STRONG_PW@rs0/yourdb?replicaSet=rs0&authSource=admin'
export TS=$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p backups 
 Vidage complet de la base depuis un secondaire avec oplog pour la sécurité point-in-time :

mongodump \
 --uri "$RS_URI" \
 --readPreference secondaryPreferred \
 --db yourdb \
 --archive "backups/yourdb-$TS.archive" \
 --gzip \
 --oplog 
 Vidage d'une seule collection (filtrer un sous-ensemble actif pour réduire la taille) :

mongodump \
 --uri "$RS_URI" \
 --db yourdb \
 --collection orders \
 --archive "backups/yourdb-orders-$TS.archive" \
 --gzip 
 Chiffrer l'archive au repos (exemple avec GnuPG) :

gpg --symmetric --cipher-algo AES256 "backups/yourdb-$TS.archive"
shred -u "backups/yourdb-$TS.archive" # supprimer le texte en clair proprement 
 Idée de rétention :

- Conserver N archives quotidiennes, W hebdomadaires, M mensuelles

- Stocker des copies hors site et tester les restaurations depuis hors site aussi

## Exemples de sauvegarde (Snapshots)

mongod standalone : verrouiller, snapshot, déverrouiller.

// Dans mongosh sur le nœud
use admin
db.adminCommand({ fsync: 1, lock: true }) 
 Effectuez un snapshot LVM, EBS ou autre au niveau filesystem, puis :

// De retour dans mongosh
db.fsyncUnlock() 
 Clusters shardés : désactiver le balancer et snapshots chaque shard et config server de façon cohérente.

sh.stopBalancer()
// Vérifier l'état : sh.getBalancerState() doit être false 
 Après avoir pris des snapshots filesystem synchronisés sur tous les shards et config servers, réactiver le balancer :

sh.startBalancer() 
 Notes :

- mongodump --oplog fonctionne pour les replica sets, pas pour un cluster shardé via mongos

- Pour la cohérence cross-shard, préférez les snapshots filesystem coordonnés ou une solution de sauvegarde gérée

## Exemples de restauration

Restaurez toujours vers une cible sûre d'abord (DB de staging ou nouveau namespace) et validez avant la bascule.

Restauration de base d'une archive de base complète :

export RESTORE_URI='mongodb://restore:REDACTED_STRONG_PW@primary/yourdb?authSource=admin'

mongorestore \
 --uri "$RESTORE_URI" \
 --archive "backups/yourdb-$TS.archive" \
 --gzip \
 --nsFrom 'yourdb.*' \
 --nsTo 'yourdb_restored.*' \
 --drop \
 --writeConcern '{"w":"majority","wtimeout":60000}' 
 Restauration sélective d'une seule collection :

mongorestore \
 --uri "$RESTORE_URI" \
 --archive "backups/yourdb-orders-$TS.archive" \
 --gzip \
 --nsInclude 'yourdb.orders' \
 --drop 
 Récupération point-in-time (PITR) avec un dump incluant l'oplog :

- La commande mongodump ci-dessus a écrit un segment d'oplog dans l'archive

- Vous pouvez rejouer cet oplog pendant la restauration, en limitant optionnellement par un timestamp

# Restaurer la base plus rejouer l'oplog jusqu'au dernier dans l'archive
mongorestore \
 --uri "$RESTORE_URI" \
 --archive "backups/yourdb-$TS.archive" \
 --gzip \
 --oplogReplay 
 Limiter l'oplog à un timestamp (format : secondes[:ordinal]) :

# Exemple : arrêter à l'epoch UNIX 1719931200
mongorestore \
 --uri "$RESTORE_URI" \
 --archive "backups/yourdb-$TS.archive" \
 --gzip \
 --oplogReplay \
 --oplogLimit 1719931200 
 Astuce : restaurer dans un namespace différent ( yourdb_pitr.* ) pour pouvoir comparer avant tout échange.

## Vérifications de validation après restauration

Exécutez des vérifications rapides et déterministes avant de considérer la restauration comme bonne.

- Comptages

// Comparer source vs restauré (exécuter sur chaque)
use yourdb
const expected = db.orders.countDocuments({})
// Sur le namespace restauré
use yourdb_restored
const actual = db.orders.countDocuments({})
printjson({ expected, actual, ok: expected === actual }) 

- Hachages (par collection)

 // Sur chaque environnement
db.adminCommand({ dbHash: 1, collections: ["orders" ] }) 

- Index

 db.orders.getIndexes() 

- Requêtes représentatives

 db.orders.find({ status: "open" }).limit(5).toArray()
db.orders.aggregate([{ $match: { createdAt: { $gte: ISODate("2026-01-01" ) } } }, { $count: "n" }]) 

- Utilisateurs et rôles

 use admin
db.getUsers() 

- Version de compatibilité des fonctionnalités (fCV)

 use admin
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 }) 
 Script Node.js de vérification automatisé (optionnel) :

// verify.js
const { MongoClient } = require('mongodb');
(async () => {
 const srcUri = process.env.SRC_URI; // e.g. mongodb://.../yourdb
 const dstUri = process.env.DST_URI; // e.g. mongodb://.../yourdb_restored
 const coll = 'orders';
 const src = new MongoClient(srcUri);
 const dst = new MongoClient(dstUri);
 try {
 await src.connect();
 await dst.connect();
 const srcCount = await src.db().collection(coll).countDocuments();
 const dstCount = await dst.db().collection(coll).countDocuments();
 console.log({ srcCount, dstCount, match: srcCount === dstCount });
 process.exit(srcCount === dstCount ? 0 : 2);
 } catch (e) {
 console.error(e);
 process.exit(1);
 } finally {
 await src.close();
 await dst.close();
 }
})(); 

## Planification du rollback et bascules

 Évitez de restaurer directement en production. Préférez :

- Restaurer vers des collections parallèles dans la même base (suffixe _new )

- Réchauffer les caches avec des requêtes de lecture

- Permuter les noms de collections atomiquement

- Conserver les anciennes collections pour un revert rapide, puis nettoyer

Exemple de plan de renommage (privilèges admin requis) :

use admin
// Échanger orders_new -> orders avec sauvegarde de l'ancienne
// Étape 1 : archiver l'ancien nom de collection
db.adminCommand({ renameCollection: 'yourdb.orders', to: 'yourdb.orders_old', dropTarget: true })
// Étape 2 : promouvoir la nouvelle collection
db.adminCommand({ renameCollection: 'yourdb.orders_new', to: 'yourdb.orders', dropTarget: true }) 
 Notes :

- renameCollection fonctionne dans la même base de données

- Faire une collection à la fois pour des rollbacks clairs

- Si rollback nécessaire, inverser les étapes de renommage

## Planification de la reprise après sinistre

Définir les cibles :

- RTO (Recovery Time Objective, objectif de temps de reprise) : temps maximal acceptable pour une restauration

- RPO (Recovery Point Objective, objectif de point de reprise) : quantité maximale de données qu'on peut se permettre de perdre

Pratiques recommandées :

- Réplication : exécuter des replica sets across failure domains (domaines de panne)

- Sauvegardes : conserver des copies chiffrées, versionnées, hors site

- PITR : combiner des dumps de base réguliers ou snapshots avec archivage d'oplog

- Runbooks : documenter noms d'hôtes, URIs, gestion des credentials, commandes et critères de succès

- Répétitions : exécuter des exercices de restauration trimestriels, enregistrer les temps, corriger les goulots

- Surveillance : alerter sur échecs de jobs de sauvegarde, dérive de données et capacité de stockage

## Plan de pilote local

Commencez petit et mesurable avant de déployer à grande échelle :

Périmètre

- Une base de données ( yourdb ), une collection critique ( orders ), taille cible sous quelques Go

Étapes

- Faire un mongodump avec --oplog depuis un secondaire de replica set

- Restaurer dans yourdb_restored sur un cluster de dev

- Exécuter le script de validation (comptages, hachages, requêtes d'échantillon)

- Enregistrer taille du dump, temps de dump, temps de restauration, résultats de validation

- Documenter les constats et optimiser si nécessaire (compression, parallélisme, index)

Critères de sortie

- La restauration passe tous les contrôles

- Le temps de bout en bout est sous votre budget RTO pour la taille du pilote

- Les membres de l'équipe peuvent répéter le processus sans aide

## Erreurs courantes et correctifs

<div class="my-stack-md overflow-x-auto">
<table class="w-full min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Erreur</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Correctif</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Ne pas tester les restaurations</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Planifier des exercices de restauration mensuels ou trimestriels avec validation</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Omettre <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">--oplog</code> pour les replica sets</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Toujours ajouter <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">--oplog</code> avec mongodump sur un replica set</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Sauvegarder un cluster shardé via mongos en espérant une cohérence cross-shard</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Utiliser des snapshots filesystem coordonnés avec le balancer arrêté, ou une solution gérée</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Restaurer directement en production</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Restaurer dans un namespace de staging, valider, puis basculer avec renommage</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Oublier index ou utilisateurs dans la validation</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Comparer les sorties de <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">getIndexes</code> et <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">db.getUsers</code></td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Surcharger le primaire pendant la sauvegarde</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Lire depuis un secondaire avec <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">--readPreference secondaryPreferred</code></td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Artefacts de sauvegarde non sécurisés</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Chiffrer au repos, restreindre l&#39;accès et faire tourner les clés</td></tr></tbody>
</table>
</div>

## Conclusion

Vous disposez maintenant d'un workflow pratique de sauvegarde et restauration MongoDB :

- Pour les replica sets, utilisez mongodump avec --oplog et restaurez avec validation

- Pour les clusters shardés, préférez les snapshots filesystem coordonnés

- Validez comptages, hachages, requêtes d'échantillon, index et utilisateurs

- Planifiez les rollbacks avec renommage de collections, et répétez la DR pour prouver RTO/RPO

Prochaines étapes

- Implémenter le Plan de pilote local cette semaine

- Automatiser la validation et ajouter la surveillance des échecs

- Étendre le périmètre et la fréquence une fois le pilote validé