You are looking at the documentation of a prior release. To read the documentation of the latest release, please
visit here.
New to KubeDB? Please start here.
Storage Migration of DocumentDB
StorageMigration moves a DocumentDB database from one StorageClass to another without losing
data, using a DocumentDBOpsRequest of type StorageMigration. This is the tool you reach for
when you need to change the storage backend of a running database — for example moving from one
CSI provisioner to another. This guide migrates a 3-node cluster from longhorn to
standard-custom.
Unlike provisioning a fresh replica (which seeds standbys with pg_basebackup), storage
migration performs a block-level copy of each existing PVC into a new PVC on the target
StorageClass, one pod at a time, then re-points the pod at the migrated volume. The data
directory is copied verbatim, so the migrated replica does not have to re-stream a base backup.
Before You Begin
- You need a Kubernetes cluster and the
kubectlCLI configured to talk to it. - Install KubeDB following the steps here.
- This tutorial uses a namespace called
demo(kubectl create ns demo). - Deploy a
DocumentDBcluster (documentdb-cls-sample) and wait for it to becomeReady. - Confirm both the source and target StorageClasses exist (
kubectl get sc).
Note: YAML files used in this tutorial are stored in docs/examples/documentdb folder in GitHub repository kubedb/docs.
PVCs before
The cluster is on longhorn, 10Gi per replica:
$ kubectl get pvc -n demo -l app.kubernetes.io/instance=documentdb-cls-sample \
-o custom-columns=NAME:.metadata.name,SIZE:.status.capacity.storage,SC:.spec.storageClassName,STATUS:.status.phase
NAME SIZE SC STATUS
data-documentdb-cls-sample-0 10Gi longhorn Bound
data-documentdb-cls-sample-1 10Gi longhorn Bound
data-documentdb-cls-sample-2 10Gi longhorn Bound
$ kubectl get docdb -n demo documentdb-cls-sample -o jsonpath='{.spec.storage.storageClassName}'
longhorn
Create the StorageMigration OpsRequest
migration.storageClassName is the target; oldPVReclaimPolicy: Delete cleans up the source
PersistentVolumes once their data has been copied:
apiVersion: ops.kubedb.com/v1alpha1
kind: DocumentDBOpsRequest
metadata:
name: documentdb-cls-storage-migration
namespace: demo
spec:
type: StorageMigration
databaseRef:
name: documentdb-cls-sample
timeout: 10m
migration:
storageClassName: standard-custom
oldPVReclaimPolicy: Delete
$ kubectl apply -f cluster-storage-migration.yaml
documentdbopsrequest.ops.kubedb.com/documentdb-cls-storage-migration created
$ kubectl get dcops -n demo documentdb-cls-storage-migration
NAME TYPE STATUS AGE
documentdb-cls-storage-migration StorageMigration Successful 8m13s
What happened
The operator migrates standbys first and the primary last, switching leadership off the
primary just before its turn so the cluster stays writable throughout. For each pod it: mounts a
temporary helper pod on a new PVC, runs a migrator job to copy the data directory, deletes the
old PVC, binds the new one under the original PVC name, recreates the pod, and waits for it to
be ready. The condition stream (trimmed) captures the loop:
$ kubectl get dcops -n demo documentdb-cls-storage-migration \
-o jsonpath='{range .status.conditions[*]}{.type}={.status} :: {.message}{"\n"}{end}'
Running=True :: StorageClass migration is in progress
PetSetDeleted--documentdb-cls-sample=True :: pet set deleted
GetStorageClass=True :: get storage class
# --- per replica (sample-0 shown) ---
PVCCreated--data-migrate-documentdb-cls-sample-0=True :: p v c created
PodCreated--pvcmounter-documentdb-cls-sample-0=True :: pod created
JobCreated--migrator-documentdb-cls-sample-0=True :: job created
JobDeleted--migrator-documentdb-cls-sample-0=True :: job deleted
PVCDeleted--data-documentdb-cls-sample-0=True :: p v c deleted
PVCCreated--data-documentdb-cls-sample-0=True :: p v c created
PodCreated--documentdb-cls-sample-0=True :: pod created
PodReady--documentdb-cls-sample-0=True :: pod ready
PodMigrationCompleted-documentdb-cls-sample-0=True :: PVC Migration Completed for documentdb-cls-sample-0
# --- leadership switched before migrating the last (primary) pod ---
SwitchPrimary--documentdb-cls-sample-2=True :: Successfully switched primary from documentdb-cls-sample-2 to documentdb-cls-sample-0 before its migration
PodMigrationCompleted-documentdb-cls-sample-2=True :: PVC Migration Completed for documentdb-cls-sample-2
StorageMigration=True :: Successfully migrated StorageClass for DocumentDB Database
Successful=True :: Successfully Migrated DocumentDB StorageClass
UnsetRaftKeyOpsRequestProgressing=True :: Successfully Unset Raft Key OpsRequestProgressing
PVCs after
All three data volumes are now backed by standard-custom, keeping their 10Gi size and
original PVC names, and the DocumentDB object reflects the new StorageClass:
$ kubectl get pvc -n demo -l app.kubernetes.io/instance=documentdb-cls-sample \
-o custom-columns=NAME:.metadata.name,SIZE:.status.capacity.storage,SC:.spec.storageClassName,STATUS:.status.phase
NAME SIZE SC STATUS
data-documentdb-cls-sample-0 10Gi standard-custom Bound
data-documentdb-cls-sample-1 10Gi standard-custom Bound
data-documentdb-cls-sample-2 10Gi standard-custom Bound
$ kubectl get docdb -n demo documentdb-cls-sample -o jsonpath='sc={.spec.storage.storageClassName} phase={.status.phase}'
sc=standard-custom phase=Ready
The cluster is Ready, all pods 2/2, and previously written data survived the migration
intact:
$ PASS=$(kubectl get secret -n demo documentdb-cls-sample-auth -o jsonpath='{.data.password}' | base64 -d)
$ kubectl exec -n demo documentdb-cls-sample-0 -c documentdb -- \
mongosh "mongodb://default_user:${PASS}@localhost:10260/?tls=true&tlsAllowInvalidCertificates=true" \
--quiet --eval 'printjson(db.runCommand({ping:1}));'
{ ok: 1 }
[!NOTE] In the test environment, migrating to
standard-custom(backed by thelocal-pathprovisioner) succeeded and the cluster returned toReady, even though a freshly provisioned multi-replica DocumentDB onlocal-pathcan fail to bring up standbys (becausepg_basebackuptrips a mount check on that provisioner). Storage migration avoids that path entirely — it copies the already-initialized data directory block-for-block rather than re-seeding the standby.
Standalone
The same DocumentDBOpsRequest applies to a standalone (replicas: 1) instance — point
spec.databaseRef.name at documentdb-sa-sample. On this build standalone instances did not
finish bootstrapping (see the Restart guide), so the
standalone migration could not be exercised live.
Cleaning Up
kubectl delete documentdbopsrequest -n demo documentdb-cls-storage-migration
kubectl delete documentdb -n demo documentdb-cls-sample
kubectl delete ns demo
Next Steps
- Volume expansion of a DocumentDB cluster.
- Storage autoscaling of a DocumentDB cluster.
































