Respuesta corta: respaldar un clúster de Kubernetes consiste en respaldar tres cosas distintas — los objetos de la API (Deployments, Services, Secrets, CRD), los volúmenes persistentes y los datos de la aplicación en su formato lógico (un pg_dump, no solo un snapshot de bloque). Velero cubre los dos primeros: instalado con un bucket S3 en OVHcloud o Scaleway, cuesta entre 1 y 5 € al mes de almacenamiento para el clúster de una pyme y respalda un namespace en unos minutos. El tercero sigue siendo responsabilidad tuya, y es el que de verdad te salvará.
La conversación siempre empieza igual. El clúster funciona, la CI despliega, los volúmenes están en block storage replicado del proveedor — así que «ya está respaldado». Luego alguien borra el namespace equivocado con un kubectl delete ns un viernes por la tarde, o un helm uninstall se lleva por delante un PVC con reclaimPolicy en Delete, y se descubre que el block storage replicado protege frente al fallo de un disco, no frente a un error humano.
Este artículo describe la instalación que desplegamos por defecto en los clústeres que operamos y, sobre todo, el procedimiento de restauración que repetimos periódicamente. Porque una copia de seguridad que nunca se ha restaurado no es una copia de seguridad: es una línea en un panel de control.

Qué hay que respaldar en un clúster, exactamente
Desglosemos el problema. En un clúster de Kubernetes que sirve la aplicación de una pyme hay cuatro categorías de datos, y no se respaldan igual.
| Categoría | Ejemplo | Herramienta adecuada |
|---|---|---|
| Objetos de la API | Deployments, Ingress/HTTPRoute, ConfigMaps, Secrets, CRD | Velero (exportación a S3) |
| Volúmenes persistentes | subidas de usuarios, índice de búsqueda, caché | Velero + snapshots CSI, o File System Backup (Kopia) |
| Bases de datos | PostgreSQL, Redis persistente | copia lógica dedicada (pg_dump, archivado WAL) |
| Infraestructura del clúster | nodos, red, IAM | Terraform / OpenTofu, no una copia de seguridad |
Dos trampas clásicas en esta tabla.
La primera: un snapshot de volumen no es una copia de seguridad coherente de una base de datos. Un snapshot CSI tomado mientras PostgreSQL escribe equivale, en el mejor de los casos, a un apagón brusco: PostgreSQL reproducirá su journal al arrancar y normalmente saldrá adelante, pero no tienes ninguna garantía de restauración a un instante concreto ni ninguna protección frente a una corrupción lógica (un UPDATE sin WHERE que acaba en producción). La documentación de PostgreSQL solo considera válido el snapshot del sistema de archivos si el volumen es único y el snapshot atómico — una condición que en la práctica rara vez se cumple en almacenamiento en red. Para la base de datos conviene mantener un archivado WAL continuo, que es precisamente lo que gestiona un operador como CloudNativePG; detallamos los compromisos de coste en nuestra comparación entre PostgreSQL gestionado y PostgreSQL dentro del clúster.
La segunda: el clúster en sí no debe estar en la copia de seguridad. Si la única forma de reconstruir tu clúster es restaurar un snapshot de etcd, tienes un problema de infraestructura, no de copias de seguridad. Los nodos, la red, los grupos de seguridad y los roles IAM deben venir de un repositorio Terraform u OpenTofu. Velero reconstruye las cargas de trabajo, no el plano de control.
Instalar Velero con un bucket S3 de OVHcloud o Scaleway
Velero es un proyecto de la CNCF, hoy en incubación, desarrollado inicialmente por Heptio y luego por VMware. Funciona con cualquier almacenamiento de objetos compatible con S3, lo que incluye OVHcloud Object Storage (oferta antigua y nueva oferta S3), Scaleway Object Storage y, por supuesto, Amazon S3.
Crea primero el bucket y un juego de claves de acceso dedicado. En Scaleway, el endpoint tiene la forma https://s3.fr-par.scw.cloud con la región fr-par; en OVHcloud, https://s3.gra.io.cloud.ovh.net con la región gra para la oferta compatible con S3. Verifica siempre el valor exacto en la consola del proveedor: los endpoints han cambiado varias veces.
El archivo de credenciales, en el formato clásico de AWS:
# velero-credentials
[default]
aws_access_key_id=SCWXXXXXXXXXXXXXXXXX
aws_secret_access_key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Y luego la instalación, con la CLI de Velero (disponible en las releases de GitHub del proyecto):
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
Tres opciones merecen una explicación.
--use-node-agentdespliega un DaemonSet capaz de leer el contenido de los volúmenes montados en cada nodo. Es imprescindible para la copia a nivel de archivo (File System Backup) de los volúmenes cuyo driver CSI no gestiona snapshots.--uploader-type kopiaselecciona Kopia en lugar de Restic. Restic está obsoleto en Velero desde la v1.15; en un clúster nuevo, Kopia es la opción por defecto.--default-volumes-to-fs-backup=falseevita respaldar todo a nivel de archivo por defecto: damos prioridad a los snapshots CSI, mucho más rápidos, y pasamos al modo archivo caso por caso con una anotación.
Comprueba que la ubicación de copia es accesible antes de seguir adelante:
kubectl -n velero get backupstoragelocation
# NAME PHASE LAST VALIDATED AGE
# default Available 12s 2m
Un PHASE: Unavailable significa nueve veces de cada diez un endpoint erróneo, un s3ForcePathStyle olvidado o claves sin permiso de escritura en el bucket.
Activar los snapshots CSI
Para que Velero solicite VolumeSnapshot al driver del proveedor en lugar de copiar los archivos, hace falta una VolumeSnapshotClass etiquetada:
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 # adaptar al driver del proveedor
deletionPolicy: Retain
Ojo con un detalle que se descubre demasiado tarde a menudo: en muchos proveedores, un snapshot de volumen se queda en la misma región, o incluso en la misma zona, que el volumen de origen. Si el objetivo es sobrevivir a la pérdida de una zona, hay que activar el DataMover de Velero (snapshotMoveData: true), que lee el snapshot y lo envía al bucket de objetos. Es más lento, pero es la única configuración que saca realmente tus datos del clúster.
Las planificaciones que ponemos en marcha
Una única planificación de «todo el clúster, una vez por noche» no basta: los namespaces no tienen el mismo valor ni el mismo ritmo de cambio. Nosotros usamos tres 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
Observaciones salidas de la operación diaria:
- Nunca
0 * * * *. Todas las tareas programadas del mundo se ejecutan en punto; un desfase de unos minutos evita contenciones en el almacenamiento de objetos. - El
ttlgobierna la retención, y es él el que gobierna la factura. 72 h de copias horarias más 30 días de copias diarias es un compromiso razonable para la aplicación de una pyme. - Los
Secretse incluyen por defecto. Por tanto, van en claro al bucket, cifrados del lado del servidor pero legibles por cualquiera que tenga las claves de acceso. Para datos sensibles, Velero puede cifrar del lado del cliente mediante el repositorio Kopia (repositoryMaintenancey contraseña del repositorio): hay que configurarlo explícitamente, no es el ajuste por defecto.

Lo que cuesta realmente
Tomemos un clúster típico de nuestros clientes: tres nodos, un namespace de producción, 60 GB de volúmenes persistentes, de los cuales 25 GB son de base PostgreSQL y 35 GB de subidas.
| Partida | Volumen | Coste mensual indicativo |
|---|---|---|
| Almacenamiento de objetos (copias Velero, ~90 GB tras deduplicación de Kopia) | 90 GB | 0,70 a 2 € |
| Snapshots de block storage conservados en la región | 60 GB | 2 a 5 € |
| Tráfico de salida durante una restauración completa | 60 GB puntuales | 0 € en OVHcloud y Scaleway |
| Tiempo de operación (prueba de restauración trimestral) | 2 h / trimestre | el coste real |
Las tarifas públicas de Scaleway y OVHcloud sitúan el almacenamiento de objetos en torno a 0,01 € por GB y mes en clase estándar, y ninguno de los dos proveedores factura la salida de datos — una diferencia notable con AWS S3, donde la transferencia de salida se factura y una restauración de 60 GB cuesta unos cuantos euros. Verifica siempre la tarifa del día: estos precios se mueven.
En otras palabras: respaldar el clúster de una pyme cuesta menos de 10 € al mes en infraestructura. La partida real es la hora de ingeniería dedicada a comprobar que funciona. Es también lo que incluimos en los contratos de operación descritos en nuestra página de servicios de infraestructura.
La restauración, paso a paso
Este es el procedimiento que repetimos, en condiciones reales, en un namespace de preproducción.
1. Listar las copias disponibles e inspeccionar la que nos interesa.
velero backup get
velero backup describe prod-hourly-20261109021700 --details
El --details muestra los recursos incluidos y el estado de cada volumen. Una copia en PartiallyFailed es frecuente y a menudo benigna (un recurso de webhook no respaldable), pero hay que leer los avisos antes de usarla.
2. Restaurar en un namespace nuevo, no encima de producción.
velero restore create recette-20261109 \
--from-backup prod-hourly-20261109021700 \
--namespace-mappings app-prod:app-recette \
--wait
El remapeo de namespace es la clave de una prueba no destructiva: se obtiene una copia completa de producción en app-recette, sin tocar app-prod.
3. Neutralizar lo que no debe despertarse.
Una copia de producción restaurada conserva sus CronJobs, sus hooks de salida y sus credenciales SMTP. Antes de hacerla accesible, suspende los CronJobs y corta las integraciones externas:
kubectl -n app-recette get cronjob -o name \
| xargs -I{} kubectl -n app-recette patch {} \
-p '{"spec":{"suspend":true}}'
Es también el momento de revisar los objetos de exposición de red: un HTTPRoute restaurado con el mismo nombre de host que producción creará un conflicto de enrutamiento. Describimos estos objetos en detalle en el artículo sobre la sustitución de ingress-nginx por la Gateway API.
4. Verificar los datos, no los pods.
La trampa clásica de la prueba de restauración: todos los pods están Running, se canta victoria y nadie ha abierto la base de datos. Nuestras tres comprobaciones mínimas:
# el recuento de filas de la tabla más grande
kubectl -n app-recette exec deploy/postgres -- \
psql -U app -d app -c "select count(*) from orders;"
# la fecha del último registro
kubectl -n app-recette exec deploy/postgres -- \
psql -U app -d app -c "select max(created_at) from orders;"
# un archivo conocido en el volumen de subidas
kubectl -n app-recette exec deploy/web -- \
ls -l /app/uploads/2026/
El segundo comando da el verdadero RPO (pérdida máxima de datos): la diferencia entre max(created_at) y la hora de la copia. Si tu objetivo es un RPO de una hora y el último registro es de cuatro horas antes de la copia, hay un problema en la cadena, no en la restauración.
5. Cronometrar y anotar.
Anota la duración real: ese es tu RTO. En los clústeres que operamos, una restauración completa de un namespace de 60 GB con DataMover tarda entre 20 y 45 minutos, la mayor parte en la transferencia desde el almacenamiento de objetos. Si tu compromiso es «restablecido en 30 minutos», acabas de descubrir que es falso.
6. Eliminar la copia.
velero restore delete recette-20261109
kubectl delete ns app-recette

Los cuatro errores que vemos más a menudo
- El
reclaimPolicy: Deleteen las StorageClass de producción. Unhelm uninstallo unkubectl delete pvcdestruye entonces el volumen subyacente, de inmediato. Pon enRetainlas StorageClass que soportan datos, aunque eso implique limpiar manualmente los volúmenes huérfanos. - Las copias en la misma cuenta o proyecto que el clúster. Si el compromiso de un juego de claves permite borrar a la vez el clúster y el bucket de copias, no tienes copia de seguridad. Usa un proyecto distinto, claves de solo escritura y el bloqueo de objetos (Object Lock) cuando el proveedor lo ofrezca.
- Ninguna supervisión de los
Schedule. Una copia que falla silenciosamente durante tres semanas es el escenario más habitual. Velero expone métricas Prometheus:velero_backup_last_successful_timestampyvelero_backup_failure_totalbastan para una alerta de «última copia correcta hace más de 26 h». - Los secretos que no están en la copia porque vienen de otro sitio. Con External Secrets Operator o Sealed Secrets, los
Secretrestaurados pueden estar vacíos o ser inválidos. Prueba la restauración con el gestor de secretos indisponible para medir la dependencia — es exactamente el tipo de detalle que se descubre al mismo tiempo que la avería, como la autenticación al registro privado que comentamos en nuestra guía sobre las imágenes privadas de GitLab en Kubernetes.
Preguntas frecuentes
¿Velero sustituye a la copia de seguridad de mi base PostgreSQL?
No. Velero sabe tomar un snapshot del volumen de la base, lo que te protege de un borrado accidental del PVC, pero no ofrece ni restauración a un instante concreto ni garantía de coherencia transaccional. Mantén una cadena dedicada: archivado WAL continuo con un operador, o copias automáticas de la base gestionada. En la práctica, excluimos los volúmenes de base de datos de las copias de Velero (anotación backup.velero.io/backup-volumes-excludes) para no pagar dos veces el almacenamiento.
¿Hay que respaldar etcd además?
En un clúster gestionado (OVHcloud Managed Kubernetes, Scaleway Kapsule, EKS), no: el plano de control es asunto del proveedor y no tienes acceso. En un clúster autogestionado, un snapshot diario con etcdctl snapshot save es útil para restaurar un clúster completo, pero también restaura todos los errores. Velero sigue siendo preferible para volver atrás de forma selectiva.
¿Con qué frecuencia hay que probar una restauración?
Una vez por trimestre como mínimo, y después de cualquier cambio de versión mayor de Kubernetes o del driver CSI. La prueba lleva dos horas cuando está automatizada. La incluimos como tarea programada en el pipeline, con el informe archivado: fecha, copia utilizada, RTO medido, anomalías.
¿Se puede usar Velero para migrar un clúster a otro proveedor?
Sí, y es uno de sus usos más útiles: copia desde el clúster origen con snapshotMoveData: true, lectura del mismo bucket desde el clúster destino y luego velero restore create. Los puntos de friction son las StorageClass (nombres distintos, que hay que remapear con un ConfigMap de tipo change-storage-class-config), los LoadBalancer y las anotaciones específicas del proveedor. Cuenta con un buen día de ajustes, no con un solo comando.
¿Dónde situar la prueba de restauración en la CI?
En una tarea programada semanal, separada del pipeline de la aplicación: restauración en un namespace desechable, consultas de verificación, borrado y publicación del RTO como métrica. El modelo de pipeline que usamos está descrito en nuestro artículo sobre el pipeline GitLab CI/CD para Next.js en Kubernetes.
En resumen
- Un clúster se respalda en tres planos: objetos de la API, volúmenes persistentes, datos lógicos. Velero cubre los dos primeros, no el tercero.
- Instala Velero con Kopia (Restic está obsoleto), un bucket S3 en OVHcloud o Scaleway, y
snapshotMoveData: truesi quieres sobrevivir a la pérdida de una zona. - Tres planificaciones valen más que una: horaria en producción con un
ttlcorto, diaria sobre todo el clúster con retención larga. - El coste de infraestructura es bajo — menos de 10 € al mes para 60 GB de datos en el clúster de una pyme; el coste real es el tiempo de pruebas.
- Restaura siempre en un namespace remapeado, suspende los CronJobs y luego verifica los datos (recuento de filas, fecha del último registro), no el estado de los pods.
- Mide el RPO y el RTO en cada prueba y déjalos por escrito. Un compromiso de restablecimiento sin cronometrar es una hipótesis.
- Pon las StorageClass que soportan datos en
reclaimPolicy: Retain, aísla el bucket de copias de la cuenta del clúster y alerta sobrevelero_backup_last_successful_timestamp.
Si quieres auditar tu cadena de copias de seguridad, o simplemente cronometrar una restauración real en tu clúster, háblalo con nuestro equipo o escríbenos a través de la página de contacto.


