What milvus-backup does
milvus-backup reads collection metadata from Milvus and copies the collection's segment files from Milvus's object storage to a backup location (same bucket under another prefix, or another bucket). Restore creates collections again and imports those files. It doesn't stop Milvus.
It's not a replacement for being able to rebuild from source documents. It's the fast path back after a delete, a bad migration or a lost cluster.
Step 1: get the tool and a config
# download a release binary for your OS from the project's GitHub releases page
cp configs/backup.yaml backup.yaml
Key settings in backup.yaml:
configVersion: v2
milvus:
grpc:
address: milvus.milvus.svc.cluster.local:19530
storage: # where Milvus keeps its data - must match Milvus config exactly
provider: minio # minio | aws | azure | gcp ...
address: milvus-minio.milvus.svc.cluster.local
port: 9000
bucketName: milvus-bucket # Helm default; Docker Compose default is a-bucket
rootPath: file # Helm default; Docker Compose default is files
backup:
storage: # where backups go; unset values inherit from milvus.storage
bucketName: milvus-backups
rootPath: backup
Credentials are better passed as environment or --set values than stored in the file:
milvus-backup --set MILVUS_USER=root --set MILVUS_PASSWORD="$MILVUS_PASSWORD" check
milvus-backup config show # print the resolved configuration
Warning
The most common failure is a wrong
bucketNameorrootPathfor the Milvus storage. The tool then "backs up" collections without finding their files. Copy the values from the Milvus configmap (minio.bucketName,minio.rootPath), don't guess.
Step 2: run check
milvus-backup check
It validates the connection to Milvus and both storage locations. Fix every error here before relying on backups.
Step 3: create a backup
milvus-backup create -n kb_20261028_0200
milvus-backup list
milvus-backup get -n kb_20261028_0200 # use --help to confirm flags for your version
Back up specific collections or databases with the filter options (milvus-backup create --help shows them for your version). Backing up only production collections keeps backups small and fast.
Data in growing (unflushed) segments may not be included unless it's flushed first; recent versions of the tool handle flushing, but for a clean point-in-time copy flush first:
client.flush(collection_name="kb_chunks")
Step 4: restore under a new name and verify
Never restore over the live collection first. Restore with a suffix:
milvus-backup restore -n kb_20261028_0200 -s _recover
Or rename to another database/collection:
milvus-backup restore -n kb_20261028_0200 -r default.kb_chunks:restore.kb_chunks
Then build indexes if the restore didn't include them, load and check:
client.get_collection_stats(collection_name="kb_chunks_recover") # row_count
client.list_indexes(collection_name="kb_chunks_recover")
client.load_collection(collection_name="kb_chunks_recover")
| Verification | Pass condition |
|---|---|
| Row count | Matches the source at backup time |
| Index | Present, same type and params |
| Sample search | Known queries return the expected doc_ids |
| Filters | Scalar fields present and filterable |
Step 5: switch over with an alias
If applications query an alias (recommended), switching is one call:
client.alter_alias(collection_name="kb_chunks_recover", alias="kb_chunks_live")
If they use the physical name, you'll need to drop and restore with the original name, which means downtime. Move to aliases at the next change.
Then replay any documents changed after the backup time from your ingestion system.
Step 6: restore to another cluster (DR or migration)
Point a second backup.yaml at the target cluster's Milvus and storage, with backup.storage pointing at the bucket holding the backups (replicated to the DR region if needed):
milvus-backup --config backup-dr.yaml list
milvus-backup --config backup-dr.yaml restore -n kb_20261028_0200
Cross-cloud transfers depend on the tool's transfer mode and network access between storages; test it once before you need it.
Step 7: schedule and monitor
Run as a Kubernetes CronJob:
apiVersion: batch/v1
kind: CronJob
metadata: { name: milvus-backup, namespace: milvus }
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: backup
image: <your-registry>/milvus-backup:<pinned-version>
command: ["/bin/sh", "-c"]
args: ["milvus-backup create -n kb_$(date +%Y%m%d_%H%M)"]
envFrom: [{ secretRef: { name: milvus-backup-credentials } }]
volumeMounts: [{ name: cfg, mountPath: /app/configs }]
volumes: [{ name: cfg, configMap: { name: milvus-backup-config } }]
- Alert when the job fails or hasn't succeeded in 26 hours.
- Delete old backups with a retention job (
milvus-backup delete) or object storage lifecycle rules on the backup prefix. - Keep backups in a different bucket (and ideally account/subscription) from the Milvus data.
Recovery decision table
| Incident | Path |
|---|---|
| Dropped or corrupted collection | Restore with suffix, verify, alias switch |
| Bad data for days | Older backup + replay, or rebuild from source |
| Cluster lost | Restore all collections on a new cluster from replicated backups |
| Migration to a new cluster/version | Backup on old, restore on new (check version compatibility notes) |
Prevent it next time
- Copy storage settings from the Milvus configmap into
backup.yamland runcheckin CI. - Use aliases for every collection the application reads.
- Store backups outside the Milvus bucket and outside the cluster's failure domain.
- Run a timed restore drill every quarter and record the result.