A Kubernetes cluster is ephemeral by design: pods come and go, but your data, your manifests, and the state of your applications shouldn't disappear with them. Velero is the go-to tool for backing up and restoring entire clusters—API resources and persistent volumes—directly to S3-compatible object storage. In this guide we set up Velero pointing at OtterStorage, schedule automatic backups, make them immutable with Object Lock, and prepare cross-cluster disaster recovery. Complete commands, no shortcuts.
What Velero backs up (and what it doesn't)
Velero works on two planes that are worth understanding separately, because each has its own mechanism and its own guarantees.
- Cluster resources: all Kubernetes API objects (deployments, services, configmaps, secrets, CRDs, namespaces…). Velero queries them via the API server, serializes them, and uploads them as a compressed tarball to your S3 bucket. This is what lets you rebuild the "shape" of the cluster.
- Persistent volumes: the actual data inside your PersistentVolumeClaims. There are two routes here: CSI snapshots (storage-level snapshots via the CSI driver and
VolumeSnapshotClass) or File System Backup with Restic or Kopia (the node-agent reads the volume files and uploads them to S3). - Migrations and disaster recovery: since all the state lives in object storage, you can restore to a new cluster, in another region, or after a total incident.
Velero is not a substitute for backing up your databases specifically: for PostgreSQL or MySQL under load you want a consistent dump. Take a look at our guide on PostgreSQL backups to S3 to combine it with Velero.
CSI snapshots vs. File System Backup
The choice of volume method depends on your storage driver and your portability needs. This table summarizes the practical differences.
| Aspect | CSI Snapshots | File System Backup (Restic/Kopia) |
|---|---|---|
| Requirement | CSI driver with snapshot support | node-agent deployed on every node |
| Granularity | Entire volume | File level |
| Portability across providers | Limited (depends on the backend) | High: the data goes in full to S3 |
| Performance | Very fast (copy-on-write) | Slower on the first backup, incremental afterward |
| Final target | S3 (snapshot data replicated) | S3 (Restic/Kopia repository) |
Practical rule: if you want maximum portability toward OtterStorage and to migrate across providers without tying yourself to their snapshot system, File System Backup with Kopia is the most robust option. If your CSI already integrates efficient snapshots and you're only after speed, use them.
1. Create the bucket and credentials
First you need a target bucket in OtterStorage and a pair of access keys. Ideally, generate a key isolated per bucket so Velero has no access to anything else; in OtterStorage access keys are isolated per bucket out of the box. Create the bucket from the console or with the AWS CLI following the create bucket guide.
aws s3 mb s3://mi-bucket-velero \
--endpoint-url https://es-mad-1.s3.otterstorage.io \
--region eu-mad
Then prepare the credentials file that Velero will read during installation. The format is the same as an AWS profile:
cat > credentials-velero <<EOF
[default]
aws_access_key_id=TU_ACCESS_KEY
aws_secret_access_key=TU_SECRET_KEY
EOF
2. Install Velero pointing at OtterStorage
Here's the heart of the integration. The velero-plugin-for-aws plugin speaks S3, and the s3Url and s3ForcePathStyle flags are what redirect all the traffic to the https://es-mad-1.s3.otterstorage.io endpoint instead of AWS. This installation uses File System Backup with the node-agent (recommended for portability).
velero install \
--provider aws \
--plugins velero/velero-plugin-for-aws:v1.10.0 \
--bucket mi-bucket-velero \
--secret-file ./credentials-velero \
--use-volume-snapshots=false \
--use-node-agent \
--default-volumes-to-fs-backup \
--backup-location-config \
region=eu-mad,s3ForcePathStyle=true,s3Url=https://es-mad-1.s3.otterstorage.io
Breakdown of the flags that matter:
--bucket mi-bucket-velero: the OtterStorage bucket where the backups will live.s3Url=https://es-mad-1.s3.otterstorage.io: the S3-compatible endpoint. Without this, Velero would try to talk to AWS.s3ForcePathStyle=true: forces path style (endpoint/bucket) instead of virtual subdomains; essential for compatible endpoints.region=eu-mad: the OtterStorage region (Madrid). You also haveeu-fraandus-east.--use-node-agentand--default-volumes-to-fs-backup: enable volume backup via the file system (Restic/Kopia) by default.
The BackupStorageLocation
The installation creates a BackupStorageLocation (BSL) object that defines where and how the backups are stored. If you need to add more targets—for example, a second region for DR—you can declare them as manifests. Here's how a BSL pointing at OtterStorage in Frankfurt looks:
apiVersion: velero.io/v1
kind: BackupStorageLocation
metadata:
name: otter-fra
namespace: velero
spec:
provider: aws
objectStorage:
bucket: mi-bucket-velero-dr
config:
region: eu-fra
s3Url: https://es-mad-1.s3.otterstorage.io
s3ForcePathStyle: "true"
Check that the location is operational before relying on it:
velero backup-location get
# NAME PROVIDER BUCKET PHASE LAST VALIDATED
# default aws mi-bucket-velero Available 2026-06-12 09:14:02
3. Create your first backup
With the target validated, you can start running backups. Begin with a specific namespace to verify the full end-to-end flow.
velero backup create backup-produccion \
--include-namespaces produccion \
--wait
velero backup describe backup-produccion --details
velero backup logs backup-produccion
The --wait flag blocks until it finishes and returns the final status (Completed or PartiallyFailed). Always review the logs the first time: that's where the volumes backed up via File System Backup, and any skipped resource, appear.
Scheduled backups with schedule
A manual backup is fine for a test, but real protection comes from automation. Velero uses cron syntax for schedules. Here you define one daily at 2:00 with 30-day retention (720h):
velero schedule create diario-produccion \
--schedule="0 2 * * *" \
--include-namespaces produccion \
--ttl 720h0m0s
# Weekly full backup, 90-day retention
velero schedule create semanal-completo \
--schedule="0 3 * * 0" \
--ttl 2160h0m0s
The TTL must be consistent with your retention objective and your RPO (how much data you can afford to lose). Since on OtterStorage neither requests nor deletes are billed, scheduling frequent backups and expiring old ones adds no hidden cost: you only pay per TB stored.
4. Restore: full and selective
Restoring is where you find out whether your strategy works. Velero lets you recover an entire backup or filter by namespace, labels, or resource type.
Full restore
velero restore create --from-backup backup-produccion --wait
Selective restore by namespace
Maybe you only need to recover a specific namespace, or remap it to another name (useful for cloning environments):
# Only one namespace from the backup
velero restore create restore-app \
--from-backup backup-produccion \
--include-namespaces produccion
# Remap produccion -> staging on restore
velero restore create clonar-a-staging \
--from-backup backup-produccion \
--namespace-mappings produccion:staging
Inspect the result the same way as with backups:
velero restore describe restore-app --details
velero restore logs restore-app
Immutability with Object Lock
A backup that an attacker can delete doesn't protect against ransomware. The defense is immutability: enable Object Lock (WORM mode) on the Velero bucket so that not even with your credentials can the copies be deleted or overwritten within the retention period. OtterStorage supports Governance and Compliance modes, and Legal Hold per bucket.
# Create the Velero bucket with Object Lock enabled
aws s3api create-bucket \
--bucket mi-bucket-velero \
--object-lock-enabled-for-bucket \
--endpoint-url https://es-mad-1.s3.otterstorage.io
# Default retention policy: 30 days in Compliance mode
aws s3api put-object-lock-configuration \
--bucket mi-bucket-velero \
--object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":30}}}' \
--endpoint-url https://es-mad-1.s3.otterstorage.io
The Compliance mode is the strictest: nobody, not even the account owner, can shorten the retention. For environments where you need operational flexibility, Governance allows exceptions with special permissions. If you want to go deeper, you have the immutable backups solution (OtterVault) and the Legal Hold guide.
Cross-cluster disaster recovery
The scenario that really matters: your primary cluster stops existing. Since all the state lives in OtterStorage, spinning up a new cluster and restoring is a reproducible procedure.
- Provision a new cluster (another region, another provider, on-premise…).
- Install Velero pointing at the same bucket in OtterStorage with the same credentials.
- Velero will automatically detect the existing backups in the bucket.
- Restore the latest valid backup.
# On the recovery cluster, after velero install with the same --bucket:
velero backup get # lists the backups found in S3
velero restore create dr-completo --from-backup semanal-completo-20260609030012 --wait
For real geographic DR, keep a copy in a second region. You can use OtterStorage's replication (OtterSync) between eu-mad and eu-fra, or configure a second BackupStorageLocation and duplicate the schedule. That way, if you lose an entire region, the other still has your backups.
Best practices
- Test your restores on a staging cluster periodically. A copy that has never been restored is only a hypothesis.
- Enable Object Lock from day one; adding it later doesn't protect the copies that already exist.
- Exclude what adds nothing: volatile system namespaces or generated resources can inflate the backup with no recovery value.
- Watch the status with
velero backup getand alert onPartiallyFailedphases. - Document your DR runbook: in a real incident you don't want to improvise the restore commands.
For the endpoint, region, and security configuration details, check the documentation and the security guide.
Frequently asked questions
Does Velero back up the volume data too, or only the manifests? +
--default-volumes-to-fs-backup flag, the PVC data goes in full to your OtterStorage bucket.What flags do I need for Velero to use OtterStorage instead of AWS? +
--backup-location-config you must specify s3Url=https://es-mad-1.s3.otterstorage.io, s3ForcePathStyle=true, and the region (eu-mad, eu-fra, or us-east). The velero-plugin-for-aws plugin does the rest, since OtterStorage is 100% compatible with the S3 API.Do frequent backups drive up the bill? +
OtterStorage Team
Protect your clusters
An S3-compatible target for Velero, immutable and with no surprises.
Hazte Founding Otter