New to KubeDB? Please start here.
Neo4j StorageClass Migration
This guide shows how to migrate the StorageClass of a KubeDB-managed Neo4j cluster using Neo4jOpsRequest with type: StorageMigration.
Before You Begin
- You need a Kubernetes cluster and
kubectlconfigured. - Install KubeDB operator following setup guide.
- Ensure at least two
StorageClassresources are available in your cluster.
Use a dedicated namespace for this walkthrough:
$ kubectl create ns demo
namespace/demo created
Prepare Neo4j Database
First, verify available storage classes:
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
custom-longhorn driver.longhorn.io Delete WaitForFirstConsumer true 3h38m
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 4h26m
longhorn (default) driver.longhorn.io Delete Immediate true 3h43m
longhorn-static driver.longhorn.io Delete Immediate true 3h43m
We will deploy Neo4j with local-path, then migrate to custom-longhorn.
Both old and new PVCs should stay on the same node. If the old class uses
WaitForFirstConsumer, use a new class withWaitForFirstConsumeras well.
Apply the Neo4j database manifest:
$ cat <<'EOF' | kubectl apply -f -
apiVersion: kubedb.com/v1alpha2
kind: Neo4j
metadata:
name: neo4j-test
namespace: demo
spec:
replicas: 3
deletionPolicy: WipeOut
version: "2025.12.1"
storage:
storageClassName: "local-path"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
EOF
neo4j.kubedb.com/neo4j-test created
$ kubectl get neo4j,pvc -n demo
NAME VERSION STATUS AGE
neo4j.kubedb.com/neo4j-test 2025.12.1 Ready 2m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/data-neo4j-test-0 Bound ... 2Gi RWO local-path 2m
persistentvolumeclaim/data-neo4j-test-1 Bound ... 2Gi RWO local-path 2m
persistentvolumeclaim/data-neo4j-test-2 Bound ... 2Gi RWO local-path 2m
Apply StorageMigration OpsRequest
To migrate StorageClass, create a Neo4jOpsRequest:
$ cat <<'EOF' | kubectl apply -f -
apiVersion: ops.kubedb.com/v1alpha1
kind: Neo4jOpsRequest
metadata:
name: storage-migration
namespace: demo
spec:
type: StorageMigration
databaseRef:
name: neo4j-test
migration:
storageClassName: custom-longhorn
oldPVReclaimPolicy: Delete
timeout: 3000s
EOF
neo4jopsrequest.ops.kubedb.com/storage-migration created
Here,
spec.typemust beStorageMigration.spec.databaseRef.namepoints to target Neo4j database.spec.migration.storageClassNameis the destinationStorageClass.spec.migration.oldPVReclaimPolicycontrols old PV reclaim policy.
To retain old PVs after migration, use
oldPVReclaimPolicy: Retain.
Verify StorageClass Migration
Watch the OpsRequest status:
$ kubectl get neo4jopsrequest -n demo -w
NAME TYPE STATUS AGE
storage-migration StorageMigration Successful 8m
Check PVC storage class after migration:
$ kubectl get pvc -n demo
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-neo4j-test-0 Bound ... 2Gi RWO custom-longhorn 14m
data-neo4j-test-1 Bound ... 2Gi RWO custom-longhorn 14m
data-neo4j-test-2 Bound ... 2Gi RWO custom-longhorn 14m
The PVCs now use custom-longhorn, which confirms successful StorageClass migration.
Cleanup
$ kubectl delete neo4jopsrequest -n demo storage-migration
$ kubectl delete neo4j -n demo neo4j-test
$ kubectl delete ns demo































