Réponse courte : sauvegarder un cluster Kubernetes, c'est sauvegarder trois choses distinctes — les objets de l'API (Deployments, Services, Secrets, CRD), les volumes persistants, et les données applicatives dans leur format logique (un pg_dump, pas seulement un snapshot de bloc). Velero couvre les deux premières : installé avec un bucket S3 chez OVHcloud ou Scaleway, il coûte environ 1 à 5 € par mois de stockage pour un cluster de PME et sauvegarde un namespace en quelques minutes. La troisième reste à votre charge, et c'est celle qui vous sauvera vraiment.
La discussion commence toujours de la même manière. Le cluster tourne, la CI déploie, les volumes sont sur du block storage répliqué chez le fournisseur — donc « c'est sauvegardé ». Puis quelqu'un supprime le mauvais namespace avec un kubectl delete ns un vendredi soir, ou un helm uninstall emporte un PVC marqué Delete en reclaimPolicy, et on découvre que le block storage répliqué protège contre la panne d'un disque, pas contre une erreur humaine.
Cet article décrit l'installation que nous déployons par défaut sur les clusters que nous exploitons, et surtout la procédure de restauration que nous rejouons périodiquement. Parce qu'une sauvegarde jamais restaurée n'est pas une sauvegarde : c'est une ligne dans un tableau de bord.

Ce qu'il faut sauvegarder dans un cluster, exactement
Découpons le problème. Dans un cluster Kubernetes qui sert une application de PME, il y a quatre catégories de données, et elles ne se sauvegardent pas pareil.
| Catégorie | Exemple | Outil adapté |
|---|---|---|
| Objets de l'API | Deployments, Ingress/HTTPRoute, ConfigMaps, Secrets, CRD | Velero (export vers S3) |
| Volumes persistants | uploads utilisateurs, index de recherche, cache | Velero + snapshots CSI, ou File System Backup (Kopia) |
| Bases de données | PostgreSQL, Redis persistant | sauvegarde logique dédiée (pg_dump, archivage WAL) |
| Infrastructure du cluster | nœuds, réseau, IAM | Terraform / OpenTofu, pas une sauvegarde |
Deux pièges classiques dans ce tableau.
Le premier : un snapshot de volume n'est pas une sauvegarde de base de données cohérente. Un snapshot CSI pris pendant que PostgreSQL écrit donne, au mieux, l'équivalent d'un arrêt brutal : PostgreSQL rejouera son journal au démarrage et s'en sortira généralement, mais vous n'avez aucune garantie de restauration à un instant donné, et aucune protection contre une corruption logique (un UPDATE sans WHERE qui part en production). La documentation de PostgreSQL ne considère le snapshot de système de fichiers comme valide que si le volume est unique et le snapshot atomique — une condition rarement vérifiée en pratique sur du stockage réseau. Pour la base, on garde un archivage WAL en continu, ce qui est précisément ce que gère un opérateur comme CloudNativePG ; nous avons détaillé les arbitrages de coût dans notre comparaison entre PostgreSQL managé et PostgreSQL dans le cluster.
Le second : le cluster lui-même ne doit pas être dans la sauvegarde. Si la seule façon de reconstruire votre cluster est de restaurer un snapshot d'etcd, vous avez un problème d'infrastructure, pas de sauvegarde. Les nœuds, le réseau, les groupes de sécurité et les rôles IAM doivent venir d'un dépôt Terraform ou OpenTofu. Velero reconstruit les charges de travail, pas le plan de contrôle.
Installer Velero avec un bucket S3 OVHcloud ou Scaleway
Velero est un projet de la CNCF, aujourd'hui en incubation, développé initialement par Heptio puis VMware. Il fonctionne avec n'importe quel stockage objet compatible S3, ce qui inclut OVHcloud Object Storage (ancienne et nouvelle offre S3), Scaleway Object Storage et évidemment Amazon S3.
Créez d'abord le bucket et un jeu de clés d'accès dédié. Chez Scaleway, l'endpoint est de la forme https://s3.fr-par.scw.cloud avec la région fr-par ; chez OVHcloud, https://s3.gra.io.cloud.ovh.net avec la région gra pour l'offre S3 compatible. Vérifiez toujours la valeur exacte dans la console du fournisseur, les endpoints ont changé plusieurs fois.
Le fichier de credentials, au format AWS classique :
# velero-credentials
[default]
aws_access_key_id=SCWXXXXXXXXXXXXXXXXX
aws_secret_access_key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Puis l'installation, avec la CLI Velero (disponible sur les releases GitHub du projet) :
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.11.0 \
--bucket studio-cluster-backup \
--secret-file ./velero-credentials \
--backup-location-config \
region=fr-par,s3ForcePathStyle=true,s3Url=https://s3.fr-par.scw.cloud \
--use-node-agent \
--uploader-type kopia \
--default-volumes-to-fs-backup=false \
--namespace velero
Trois options méritent une explication.
--use-node-agentdéploie un DaemonSet qui sait lire le contenu des volumes montés sur chaque nœud. C'est indispensable pour la sauvegarde au niveau fichier (File System Backup) des volumes dont le driver CSI ne gère pas les snapshots.--uploader-type kopiasélectionne Kopia plutôt que Restic. Restic est déprécié côté Velero depuis la v1.15 ; sur un cluster neuf, Kopia est le choix par défaut.--default-volumes-to-fs-backup=falseévite de tout sauvegarder au niveau fichier par défaut : on privilégie les snapshots CSI, beaucoup plus rapides, et on bascule en mode fichier au cas par cas avec une annotation.
Vérifiez que l'emplacement de sauvegarde est joignable avant d'aller plus loin :
kubectl -n velero get backupstoragelocation
# NAME PHASE LAST VALIDATED AGE
# default Available 12s 2m
Un PHASE: Unavailable signifie neuf fois sur dix un endpoint erroné, un s3ForcePathStyle oublié, ou des clés sans droit d'écriture sur le bucket.
Activer les snapshots CSI
Pour que Velero demande des VolumeSnapshot au driver du fournisseur plutôt que de recopier les fichiers, il faut une VolumeSnapshotClass étiquetée :
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-block-snapclass
labels:
velero.io/csi-volumesnapshot-class: "true"
driver: csi.cinder.openstack.org # adapter au driver du fournisseur
deletionPolicy: Retain
Attention à un point souvent découvert trop tard : chez beaucoup de fournisseurs, un snapshot de volume reste dans la même région, voire la même zone, que le volume d'origine. Si le but est de survivre à la perte d'une zone, il faut activer le DataMover de Velero (snapshotMoveData: true), qui lit le snapshot et le pousse vers le bucket objet. C'est plus lent, mais c'est la seule configuration qui met réellement vos données hors du cluster.
Les planifications que nous mettons en place
Une seule planification « tout le cluster, une fois par nuit » est insuffisante : les namespaces n'ont ni la même valeur ni le même rythme de changement. Nous utilisons trois Schedule.
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: prod-hourly
namespace: velero
spec:
schedule: "17 * * * *"
template:
includedNamespaces:
- app-prod
snapshotVolumes: true
snapshotMoveData: true
ttl: 72h
---
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: full-cluster-daily
namespace: velero
spec:
schedule: "25 2 * * *"
template:
excludedNamespaces:
- kube-system
- velero
snapshotVolumes: true
ttl: 720h
Remarques issues de l'exploitation :
- Jamais
0 * * * *. Toutes les tâches planifiées du monde tournent à l'heure pile ; un décalage de quelques minutes évite les contentions sur le stockage objet. - Le
ttlpilote la rétention, et c'est lui qui pilote la facture. 72 h de sauvegardes horaires plus 30 jours de sauvegardes quotidiennes est un compromis raisonnable pour une application de PME. - Les
Secretsont inclus par défaut. Ils partent donc en clair dans le bucket, chiffré côté serveur mais lisible par quiconque a les clés d'accès. Pour les données sensibles, Velero sait chiffrer côté client via le dépôt Kopia (repositoryMaintenanceet mot de passe du dépôt) : à configurer explicitement, ce n'est pas le réglage par défaut.

Ce que ça coûte réellement
Prenons un cluster typique de nos clients : trois nœuds, un namespace de production, 60 Go de volumes persistants dont 25 Go de base PostgreSQL et 35 Go d'uploads.
| Poste | Volume | Coût mensuel indicatif |
|---|---|---|
| Stockage objet (sauvegardes Velero, ~90 Go après dédup Kopia) | 90 Go | 0,70 à 2 € |
| Snapshots de block storage conservés en région | 60 Go | 2 à 5 € |
| Trafic sortant lors d'une restauration complète | 60 Go ponctuels | 0 € chez OVHcloud et Scaleway |
| Temps d'exploitation (test de restauration trimestriel) | 2 h / trimestre | le vrai coût |
Les grilles publiques de Scaleway et OVHcloud situent le stockage objet autour de 0,01 € par Go et par mois en classe standard, et les deux fournisseurs ne facturent pas la sortie de données — un écart notable avec AWS S3, où le transfert sortant est facturé et où une restauration de 60 Go se chiffre en quelques euros. Vérifiez toujours la grille du jour : ces prix bougent.
Autrement dit : la sauvegarde d'un cluster de PME coûte moins de 10 € par mois en infrastructure. Le poste réel, c'est l'heure d'ingénieur passée à vérifier qu'elle fonctionne. C'est aussi ce que nous intégrons dans les contrats d'exploitation décrits sur notre page services d'infrastructure.
La restauration, étape par étape
Voici la procédure que nous rejouons, en conditions réelles, sur un namespace de recette.
1. Lister les sauvegardes disponibles et inspecter la cible.
velero backup get
velero backup describe prod-hourly-20261109021700 --details
Le --details affiche les ressources incluses et l'état de chaque volume. Une sauvegarde en PartiallyFailed est fréquente et souvent bénigne (une ressource de webhook non sauvegardable), mais il faut lire les avertissements avant de s'en servir.
2. Restaurer dans un namespace neuf, pas par-dessus la production.
velero restore create recette-20261109 \
--from-backup prod-hourly-20261109021700 \
--namespace-mappings app-prod:app-recette \
--wait
Le remappage de namespace est la clé d'un test non destructif : on obtient une copie complète de la production dans app-recette, sans toucher à app-prod.
3. Neutraliser ce qui ne doit pas se réveiller.
Une copie de production restaurée garde ses CronJobs, ses hooks sortants et ses identifiants SMTP. Avant de la rendre joignable, suspendez les CronJobs et coupez les intégrations externes :
kubectl -n app-recette get cronjob -o name \
| xargs -I{} kubectl -n app-recette patch {} \
-p '{"spec":{"suspend":true}}'
C'est aussi là qu'on vérifie les objets d'exposition réseau : un HTTPRoute restauré avec le même nom d'hôte que la production créera un conflit de routage. Nous décrivons ces objets en détail dans l'article sur le remplacement d'ingress-nginx par la Gateway API.
4. Vérifier les données, pas les pods.
Le piège classique du test de restauration : tous les pods sont Running, on déclare la victoire, et personne n'a ouvert la base. Nos trois vérifications minimales :
# le compte de lignes de la table la plus volumineuse
kubectl -n app-recette exec deploy/postgres -- \
psql -U app -d app -c "select count(*) from orders;"
# la date du dernier enregistrement
kubectl -n app-recette exec deploy/postgres -- \
psql -U app -d app -c "select max(created_at) from orders;"
# un fichier connu dans le volume d'uploads
kubectl -n app-recette exec deploy/web -- \
ls -l /app/uploads/2026/
La deuxième commande donne le vrai RPO (perte de données maximale) : l'écart entre max(created_at) et l'heure de la sauvegarde. Si vous visez un RPO d'une heure et que le dernier enregistrement date de quatre heures avant la sauvegarde, il y a un problème dans la chaîne, pas dans la restauration.
5. Chronométrer et noter.
Notez la durée réelle : c'est votre RTO. Sur les clusters que nous exploitons, une restauration complète d'un namespace de 60 Go avec DataMover prend de 20 à 45 minutes, dont l'essentiel en transfert depuis le stockage objet. Si votre engagement est « rétabli en 30 minutes », vous venez de découvrir qu'il est faux.
6. Supprimer la copie.
velero restore delete recette-20261109
kubectl delete ns app-recette

Les quatre erreurs que nous voyons le plus souvent
- La
reclaimPolicy: Deletesur les StorageClass de production. Unhelm uninstallou unkubectl delete pvcdétruit alors le volume sous-jacent, immédiatement. Passez les StorageClass qui portent des données enRetain, quitte à nettoyer manuellement les volumes orphelins. - Les sauvegardes dans le même compte ou projet que le cluster. Si la compromission d'un jeu de clés permet de supprimer à la fois le cluster et le bucket de sauvegarde, vous n'avez pas de sauvegarde. Utilisez un projet distinct, des clés en écriture seule, et le verrouillage d'objets (Object Lock) quand le fournisseur le propose.
- Aucune supervision des
Schedule. Une sauvegarde qui échoue silencieusement pendant trois semaines est le scénario le plus courant. Velero expose des métriques Prometheus :velero_backup_last_successful_timestampetvelero_backup_failure_totalsuffisent pour une alerte « dernière sauvegarde réussie de plus de 26 h ». - Les secrets qui ne sont pas dans la sauvegarde parce qu'ils viennent d'ailleurs. Avec External Secrets Operator ou Sealed Secrets, les
Secretrestaurés peuvent être vides ou invalides. Testez la restauration avec le coffre indisponible pour mesurer la dépendance — c'est exactement le genre de détail qu'on découvre en même temps que la panne, comme l'authentification au registre privé évoquée dans notre guide sur les images GitLab privées sur Kubernetes.
Questions fréquentes
Velero remplace-t-il la sauvegarde de ma base PostgreSQL ?
Non. Velero sait prendre un snapshot du volume de la base, ce qui vous protège d'une suppression accidentelle du PVC, mais il ne donne ni restauration à un instant donné ni garantie de cohérence transactionnelle. Gardez une chaîne dédiée : archivage WAL continu avec un opérateur, ou sauvegardes automatiques de la base managée. En pratique, nous excluons les volumes de base de données des sauvegardes Velero (annotation backup.velero.io/backup-volumes-excludes) pour éviter de payer deux fois le stockage.
Faut-il sauvegarder etcd en plus ?
Sur un cluster managé (OVHcloud Managed Kubernetes, Scaleway Kapsule, EKS), non : le plan de contrôle est l'affaire du fournisseur et vous n'y avez pas accès. Sur un cluster auto-géré, un snapshot etcdctl snapshot save quotidien est utile pour restaurer un cluster complet, mais il restaure aussi toutes les erreurs. Velero reste préférable pour revenir en arrière de façon ciblée.
À quelle fréquence tester une restauration ?
Une fois par trimestre au minimum, et après tout changement de version majeure de Kubernetes ou du driver CSI. Le test prend deux heures quand il est scripté. Nous l'inscrivons comme tâche planifiée dans le pipeline, avec le compte-rendu archivé : date, sauvegarde utilisée, RTO mesuré, anomalies.
Peut-on utiliser Velero pour migrer un cluster vers un autre fournisseur ?
Oui, et c'est l'un de ses usages les plus utiles : sauvegarde depuis le cluster source avec snapshotMoveData: true, lecture du même bucket depuis le cluster cible, puis velero restore create. Les points de friction sont les StorageClass (noms différents, à remapper avec un ConfigMap de type change-storage-class-config), les LoadBalancer et les annotations spécifiques au fournisseur. Comptez une bonne journée de mise au point, pas une commande.
Où placer le test de restauration dans la CI ?
Dans une tâche planifiée hebdomadaire, séparée du pipeline applicatif : restauration dans un namespace jetable, requêtes de vérification, suppression, publication du RTO en métrique. Le modèle de pipeline que nous utilisons est décrit dans notre article sur le pipeline GitLab CI/CD pour Next.js sur Kubernetes.
En résumé
- Un cluster se sauvegarde sur trois plans : objets de l'API, volumes persistants, données logiques. Velero couvre les deux premiers, pas le troisième.
- Installez Velero avec Kopia (Restic est déprécié), un bucket S3 chez OVHcloud ou Scaleway, et
snapshotMoveData: truesi vous voulez survivre à la perte d'une zone. - Trois planifications valent mieux qu'une : horaire sur la production avec un
ttlcourt, quotidienne sur tout le cluster avec une rétention longue. - Le coût d'infrastructure est faible — moins de 10 € par mois pour 60 Go de données sur un cluster de PME ; le coût réel est le temps de test.
- Restaurez toujours dans un namespace remappé, suspendez les CronJobs, puis vérifiez les données (compte de lignes, date du dernier enregistrement), pas l'état des pods.
- Mesurez RPO et RTO à chaque test et écrivez-les. Un engagement de rétablissement non chronométré est une hypothèse.
- Passez les StorageClass porteuses de données en
reclaimPolicy: Retain, isolez le bucket de sauvegarde du compte du cluster, et alertez survelero_backup_last_successful_timestamp.
Si vous voulez faire auditer votre chaîne de sauvegarde, ou simplement faire chronométrer une restauration réelle sur votre cluster, parlez-en à notre équipe ou écrivez-nous via la page contact.


