Velero es una herramienta de código abierto que hace copias de seguridad de los recursos de un clúster de Kubernetes (Deployments, Services, ConfigMaps, Secrets, etc.) y de los datos de sus volúmenes persistentes, y las guarda en un almacenamiento de objetos compatible con S3. En este tutorial instalarás Velero con copia de volúmenes a nivel de sistema de ficheros, harás una copia de un namespace con datos, lo borrarás, lo restaurarás y programarás copias diarias con retención automática.

Requisitos previos

Para seguir esta guía necesitas:

  • Un clúster de Kubernetes 1.29 o superior con una StorageClass por defecto, por ejemplo desplegado sobre VPS de CubePath.
  • Una estación de trabajo Linux (por ejemplo Ubuntu 24.04) con kubectl configurado contra el clúster con permisos de administrador.
  • Un bucket en un almacenamiento de objetos compatible con S3 (AWS S3, MinIO, Wasabi, Cloudflare R2 u otro), con su endpoint, región, y una clave de acceso con permisos de lectura y escritura sobre ese bucket. En esta guía el bucket se llama velero-backups.

Cómo funciona Velero

Velero tiene dos partes: la CLI velero, que ejecutas en tu estación de trabajo, y un Deployment en el namespace velero del clúster que hace el trabajo real. Al crear una copia:

  1. Velero consulta la API de Kubernetes y guarda los recursos seleccionados como JSON en el bucket.
  2. Si activas la copia de volúmenes a nivel de sistema de ficheros, el DaemonSet node-agent lee los datos de cada volumen desde el nodo donde está montado y los sube al bucket con Kopia, de forma incremental y deduplicada.

La copia de ficheros funciona con cualquier tipo de volumen, sin depender de que el proveedor de almacenamiento soporte snapshots. Es la opción más portátil, y la que usarás aquí.

Paso 1: Instalar la CLI de Velero

Descarga la CLI desde las versiones publicadas en GitHub. Consulta la última versión estable en la página de versiones y ajusta la variable si hay una más reciente:

VELERO_VERSION=v1.17.0
curl -fsSLO "https://github.com/vmware-tanzu/velero/releases/download/${VELERO_VERSION}/velero-${VELERO_VERSION}-linux-amd64.tar.gz"
tar -xzf "velero-${VELERO_VERSION}-linux-amd64.tar.gz"
sudo install -m 0755 "velero-${VELERO_VERSION}-linux-amd64/velero" /usr/local/bin/velero

En una máquina arm64, sustituye linux-amd64 por linux-arm64. Comprueba la instalación:

velero version --client-only
Client:
	Version: v1.17.0
	Git commit: ...

Paso 2: Crear el fichero de credenciales

Velero usa el plugin de AWS para hablar con cualquier almacenamiento compatible con S3. Guarda las claves en un fichero con formato de credenciales de AWS:

nano credentials-velero
[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key

Restringe los permisos del fichero, ya que contiene claves en texto plano:

chmod 600 credentials-velero

Paso 3: Instalar Velero en el clúster

La orden velero install crea el namespace velero, los CRD, el Deployment y un Secret con tus credenciales. Cada versión de Velero es compatible con una versión concreta del plugin de AWS: para Velero 1.17 es la v1.13. Si usas otra versión, consulta la tabla de compatibilidad en el repositorio del plugin.

Sustituye https://your_s3_endpoint y la región por los de tu proveedor:

velero install \
  --provider aws \
  --plugins velero/velero-plugin-for-aws:v1.13.0 \
  --bucket velero-backups \
  --secret-file ./credentials-velero \
  --backup-location-config region=us-east-1,s3ForcePathStyle="true",s3Url=https://your_s3_endpoint \
  --use-volume-snapshots=false \
  --use-node-agent

Qué hace cada opción relevante:

  • s3ForcePathStyle="true" y s3Url: necesarios para proveedores distintos de AWS. Con AWS S3 puedes omitir ambos.
  • --use-volume-snapshots=false: no configura snapshots del proveedor de discos; los datos de los volúmenes se copiarán como ficheros.
  • --use-node-agent: despliega el DaemonSet node-agent, que hace esa copia de ficheros.

Comprueba que los pods están en marcha y que Velero puede acceder al bucket:

kubectl get pods -n velero
velero backup-location get
NAME                      READY   STATUS    RESTARTS   AGE
node-agent-7bq2m          1/1     Running   0          60s
node-agent-kx9fd          1/1     Running   0          60s
velero-6c8f7d9b5d-4lpzn   1/1     Running   0          60s

NAME      PROVIDER   BUCKET/PREFIX    PHASE       LAST VALIDATED                  ACCESS MODE   DEFAULT
default   aws        velero-backups   Available   2026-09-25 10:12:03 +0200 CEST   ReadWrite     true

La columna PHASE debe indicar Available. Si muestra Unavailable, revisa la sección de solución de problemas antes de continuar.

Paso 4: Crear una aplicación de prueba con datos

Para comprobar que la restauración recupera también los datos, crea un namespace con un pod que escribe un fichero en un volumen persistente:

nano demo-app.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: demo-data
  namespace: demo
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
        - name: app
          image: busybox:1.36
          command: ["sh", "-c", "sleep infinity"]
          volumeMounts:
            - name: data
              mountPath: /data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: demo-data

Aplícalo y escribe un fichero en el volumen:

kubectl apply -f demo-app.yaml
kubectl rollout status deployment/demo -n demo
kubectl exec -n demo deploy/demo -- sh -c 'echo "datos importantes" > /data/prueba.txt'
kubectl exec -n demo deploy/demo -- cat /data/prueba.txt
datos importantes

Paso 5: Hacer una copia de seguridad

Crea una copia del namespace demo. La opción --default-volumes-to-fs-backup indica que los volúmenes de los pods se copien con el node-agent; sin ella solo se guardarían los recursos:

velero backup create demo-backup \
  --include-namespaces demo \
  --default-volumes-to-fs-backup \
  --wait
Backup request "demo-backup" submitted successfully.
Waiting for backup to complete. You may safely press ctrl-c to stop waiting - your backup will continue in the background.
...
Backup completed with status: Completed. You may check for more information using the commands `velero backup describe demo-backup` and `velero backup logs demo-backup`.

Revisa el detalle, incluidos los volúmenes copiados:

velero backup describe demo-backup --details

En la salida, Phase debe ser Completed y en la sección de copias de volúmenes debe aparecer demo/demo-...: data como completada. Si Phase es PartiallyFailed, consulta los errores con velero backup logs demo-backup.

Paso 6: Restaurar el namespace

Simula un desastre borrando el namespace completo, con su volumen:

kubectl delete namespace demo
kubectl get namespace demo
Error from server (NotFound): namespaces "demo" not found

Restaura desde la copia:

velero restore create demo-restore --from-backup demo-backup --wait
Restore request "demo-restore" submitted successfully.
...
Restore completed with status: Completed. You may check for more information using the commands `velero restore describe demo-restore` and `velero restore logs demo-restore`.

Velero recrea el namespace, el PVC y el Deployment, y el node-agent vuelca los datos en el nuevo volumen antes de que arranque el contenedor. Comprueba que el fichero ha vuelto:

kubectl rollout status deployment/demo -n demo
kubectl exec -n demo deploy/demo -- cat /data/prueba.txt
datos importantes

Si necesitas restaurar en otro namespace para comparar sin tocar el original, añade --namespace-mappings demo:demo-copia al velero restore create.

Paso 7: Programar copias automáticas

Una copia manual no protege de nada si nadie se acuerda de hacerla. Crea una programación diaria a las 03:00 UTC que conserve cada copia 7 días:

velero schedule create demo-daily \
  --schedule="0 3 * * *" \
  --include-namespaces demo \
  --default-volumes-to-fs-backup \
  --ttl 168h0m0s

Comprueba la programación:

velero schedule get
NAME         STATUS    CREATED                          SCHEDULE    BACKUP TTL   LAST BACKUP   SELECTOR   PAUSED
demo-daily   Enabled   2026-09-25 10:30:12 +0200 CEST   0 3 * * *   168h0m0s     n/a           <none>     false

Para lanzar una ejecución inmediata sin esperar a la hora programada:

velero backup create --from-schedule demo-daily --wait

Las copias generadas se llaman demo-daily-<fecha> y aparecen en velero backup get. Cuando su TTL caduca, Velero las borra del clúster y del bucket.

Para proteger todo el clúster, omite --include-namespaces y excluye los namespaces del sistema que no quieras restaurar, por ejemplo con --exclude-namespaces kube-system,velero.

Migrar aplicaciones a otro clúster

Como las copias viven en el bucket, puedes restaurarlas en un clúster distinto. Instala Velero en el clúster de destino con el mismo bucket y las mismas credenciales (paso 3). Velero sincroniza la lista de copias desde el bucket en un minuto aproximadamente:

velero backup get

Cuando aparezca la copia, restáurala igual que en el paso 6. Ten en cuenta que el clúster de destino debe tener una StorageClass por defecto (o una con el mismo nombre que la del origen) y una versión de Kubernetes que admita las APIs de los recursos copiados. Para evitar que el clúster de destino borre o modifique copias del origen, cambia su ubicación a solo lectura:

kubectl patch backupstoragelocation default -n velero --type merge -p '{"spec":{"accessMode":"ReadOnly"}}'

Solución de problemas

  • velero backup-location get muestra Unavailable. Revisa los logs con kubectl logs -n velero deploy/velero | grep -i error. Las causas habituales son credenciales incorrectas, un bucket que no existe, un s3Url sin https:// o una región distinta de la que espera el proveedor.
  • La copia termina en PartiallyFailed con errores de volúmenes. Comprueba que hay un pod node-agent en Running en el nodo donde corre el pod de la aplicación: kubectl get pods -n velero -o wide.
  • La restauración deja el pod en Init. Velero añade un contenedor init que espera a que termine la restauración de datos. Revisa el avance con velero restore describe demo-restore --details.
  • Recursos existentes no se sobrescriben. Por defecto Velero no modifica objetos que ya existen en el clúster. Borra antes el namespace o usa --existing-resource-policy=update en la restauración.

Conclusión

Has instalado Velero con un almacenamiento S3, copiado y restaurado un namespace con sus datos y programado copias diarias con caducidad automática. Una copia solo vale si se restaura, así que conviene repetir el paso 6 de forma periódica en un namespace o clúster de pruebas. Como siguientes pasos, puedes activar el versionado y el bloqueo de objetos en el bucket para protegerte de borrados accidentales, añadir una segunda ubicación en otra región con velero backup-location create, o usar snapshots CSI si tu proveedor de almacenamiento los soporta.