Storage Migration: Persistent Volumes to Azure CSI Drivers¶
Status: Authored 2026-04-30 Audience: Platform engineers and storage administrators migrating persistent storage from on-premises Kubernetes clusters to AKS. Scope: CSI drivers (Azure Disk, Azure Files, Azure Blob, Azure NetApp Files), storage class mapping, StatefulSet migration, and data migration strategies.
1. Storage landscape mapping¶
Source storage to Azure storage mapping¶
| Source storage | Azure equivalent | CSI driver | Access modes | Use case |
|---|---|---|---|---|
| Ceph RBD (block) | Azure Managed Disk | disk.csi.azure.com | RWO | Databases, single-writer workloads |
| CephFS (filesystem) | Azure Files NFS | file.csi.azure.com | RWX | Shared data, multi-reader workloads |
| GlusterFS | Azure Files SMB/NFS | file.csi.azure.com | RWX | Legacy shared storage |
| NFS server (in-cluster or external) | Azure Files NFS / Azure NetApp Files | file.csi.azure.com / anetappfiles.csi.trident.netapp.io | RWX | High-performance NFS |
| Local NVMe / hostPath | Azure Ultra Disk or local NVMe (LSv3/Lsv2) | disk.csi.azure.com or host path | RWO | Low-latency databases |
| Longhorn | Azure Managed Disk | disk.csi.azure.com | RWO | Block storage replacement |
| OpenEBS | Azure Managed Disk | disk.csi.azure.com | RWO | Block storage replacement |
| vSphere VMDK | Azure Managed Disk | disk.csi.azure.com | RWO | VMware storage migration |
| iSCSI | Azure Managed Disk | disk.csi.azure.com | RWO | SAN replacement |
| AWS EBS | Azure Managed Disk | disk.csi.azure.com | RWO | Cross-cloud migration |
2. Azure Disk CSI driver¶
Azure Disk provides block storage for single-writer workloads (databases, message queues, application state).
Storage classes¶
# Premium SSD (recommended for production)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-csi-premium
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
cachingMode: ReadOnly # ReadWrite for write-heavy, None for Ultra
reclaimPolicy: Retain # Retain for production data
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# Premium SSD v2 (sub-millisecond latency)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-csi-premiumv2
provisioner: disk.csi.azure.com
parameters:
skuName: PremiumV2_LRS
cachingMode: None # v2 does not support host caching
DiskIOPSReadWrite: "5000"
DiskMBpsReadWrite: "200"
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# Ultra Disk (extreme IOPS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-csi-ultra
provisioner: disk.csi.azure.com
parameters:
skuName: UltraSSD_LRS
DiskIOPSReadWrite: "10000"
DiskMBpsReadWrite: "400"
cachingMode: None
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# Zone-redundant storage (HA across zones)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-csi-premium-zrs
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_ZRS
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
Disk performance tiers¶
| SKU | IOPS (max) | Throughput (max) | Latency | Best for |
|---|---|---|---|---|
| Standard HDD | 500 | 60 MBps | ~10ms | Dev/test, backups |
| Standard SSD | 6,000 | 750 MBps | ~6ms | Light production |
| Premium SSD (P30 1TB) | 5,000 | 200 MBps | ~2ms | Most production databases |
| Premium SSD v2 | 80,000 | 1,200 MBps | <1ms | High-performance databases |
| Ultra Disk | 160,000 | 4,000 MBps | <1ms | Extreme IOPS workloads |
3. Azure Files CSI driver¶
Azure Files provides shared storage for multi-reader/multi-writer workloads (shared configuration, CMS content, ML model artifacts).
Storage classes¶
# Azure Files NFS (Linux, no SMB overhead)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-nfs-premium
provisioner: file.csi.azure.com
parameters:
protocol: nfs
skuName: Premium_LRS
mountOptions:
- nconnect=4
- noresvport
- actimeo=30
reclaimPolicy: Retain
volumeBindingMode: Immediate
allowVolumeExpansion: true
---
# Azure Files SMB (Windows or Linux)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-premium
provisioner: file.csi.azure.com
parameters:
skuName: Premium_LRS
reclaimPolicy: Retain
volumeBindingMode: Immediate
allowVolumeExpansion: true
4. Azure Blob CSI driver¶
Azure Blob provides object storage mounted as a filesystem using BlobFuse2 or NFS v3.
# Azure Blob with BlobFuse2
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: blob-fuse-premium
provisioner: blob.csi.azure.com
parameters:
skuName: Premium_LRS
protocol: fuse2
isHnsEnabled: "true" # Hierarchical namespace (ADLS Gen2)
reclaimPolicy: Retain
volumeBindingMode: Immediate
Use cases for Blob CSI:
- Mounting ADLS Gen2 containers directly into pods (for data processing)
- ML training data access (large datasets, sequential reads)
- Log aggregation (write-heavy, cheap storage)
5. Azure NetApp Files¶
Azure NetApp Files provides enterprise-grade NFS/SMB with sub-millisecond latency.
# Azure NetApp Files (Trident CSI)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: anf-premium
provisioner: csi.trident.netapp.io
parameters:
backendType: azure-netapp-files
serviceLevel: Premium
reclaimPolicy: Retain
volumeBindingMode: Immediate
allowVolumeExpansion: true
Use Azure NetApp Files when:
- Sub-millisecond NFS latency is required
- Enterprise NFS features needed (snapshots, clones, cross-region replication)
- Migrating from on-prem NetApp arrays
- SAP, Oracle, or other enterprise applications requiring certified NFS
6. Storage class mapping reference¶
| On-prem storage class | AKS storage class | CSI driver | Notes |
|---|---|---|---|
ceph-block / rook-ceph-block | managed-csi-premium | Azure Disk | Block storage 1:1 |
cephfs / rook-cephfs | azurefile-csi-nfs-premium | Azure Files NFS | Shared filesystem |
nfs-client / nfs-provisioner | azurefile-csi-nfs-premium | Azure Files NFS | NFS replacement |
local-path | managed-csi-premium or ephemeral volumes | Azure Disk | Persistent replaces local-path |
hostpath | Ephemeral volume or Azure Disk | N/A or Azure Disk | Use emptyDir for ephemeral; Azure Disk for persistent |
glusterfs | azurefile-csi-premium | Azure Files SMB | Shared filesystem |
longhorn | managed-csi-premium | Azure Disk | Block storage |
openebs-cstor / openebs-jiva | managed-csi-premium | Azure Disk | Block storage |
vsphere-volume | managed-csi-premium | Azure Disk | VMware migration |
7. StatefulSet migration¶
StatefulSets require special attention because they maintain stable storage identity across pod restarts.
Migration approach 1: Velero backup/restore¶
# On source cluster: backup the StatefulSet namespace
velero backup create postgres-backup \
--include-namespaces databases \
--include-resources statefulsets,pods,persistentvolumeclaims,persistentvolumes,services,secrets,configmaps \
--default-volumes-to-fs-backup=true \
--wait
# On AKS: create storage class mapping
cat > storage-class-mapping.yaml << 'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: change-storage-class-config
namespace: velero
labels:
velero.io/plugin-config: ""
velero.io/change-storage-class: RestoreItemAction
data:
ceph-block: managed-csi-premium
rook-ceph-block: managed-csi-premium
nfs-client: azurefile-csi-nfs-premium
EOF
kubectl apply -f storage-class-mapping.yaml
# Restore on AKS
velero restore create postgres-restore \
--from-backup postgres-backup \
--wait
Migration approach 2: Application-level replication¶
For databases with built-in replication (PostgreSQL, MongoDB, Redis):
- Deploy StatefulSet on AKS with empty PVCs
- Configure source database to replicate to AKS replica
- Wait for replication to catch up (lag = 0)
- Promote AKS replica to primary
- Update application connections to AKS endpoint
- Decommission source database
# Example: PostgreSQL streaming replication
# On AKS: configure standby to stream from on-prem primary
cat > recovery.conf << 'EOF'
standby_mode = 'on'
primary_conninfo = 'host=postgres.onprem.internal port=5432 user=replicator password=xxx sslmode=require'
trigger_file = '/tmp/promote'
EOF
# When ready to cutover:
kubectl exec -n databases postgres-0 -- touch /tmp/promote
Migration approach 3: rsync-based data copy¶
For workloads where Velero and replication are not available:
# Create PVC on AKS
kubectl apply -f pvc-aks.yaml
# Deploy rsync pod on AKS
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: rsync-receiver
namespace: databases
spec:
containers:
- name: rsync
image: csainaboxacr.azurecr.io/tools/rsync:latest
command: ["rsync", "--daemon"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data-aks
EOF
# From source cluster: rsync data to AKS
rsync -avz --progress /data/ rsync://rsync-receiver.databases.svc.cluster.local/data/
8. Data migration strategies comparison¶
| Strategy | Downtime | Data volume | Complexity | Best for |
|---|---|---|---|---|
| Velero backup/restore | Minutes to hours (depends on data size) | Any | Low | General-purpose, well-supported |
| Application replication | Seconds (near-zero) | Any | Medium | Databases with built-in replication |
| rsync | Proportional to data size | < 1 TB | Low | Simple file-based data |
| AzCopy | Proportional to data size | Any | Low | Blob/file data to Azure Storage |
| Azure Data Box | Days (ship + import) | > 10 TB | Low | Massive data sets |
| CSI volume snapshots | Minutes | < 500 GB | Low | Snapshot-based migration within Azure |
9. Volume snapshot support¶
AKS supports CSI volume snapshots for Azure Disk:
# VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-azuredisk-vsc
driver: disk.csi.azure.com
deletionPolicy: Retain
parameters:
incremental: "true"
---
# Create a snapshot
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: postgres-snapshot
namespace: databases
spec:
volumeSnapshotClassName: csi-azuredisk-vsc
source:
persistentVolumeClaimName: pgdata-postgres-0
---
# Restore from snapshot
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pgdata-postgres-restored
namespace: databases
spec:
storageClassName: managed-csi-premium
dataSource:
name: postgres-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
10. Storage migration validation¶
After migrating storage, validate:
- PVCs bound and correct size (
kubectl get pvc -A) - Storage class correct (
kubectl get pvc -o wide) - Data integrity verified (checksums, row counts, file counts)
- IOPS and throughput meet application requirements (benchmark with
fio) - Volume expansion works (
kubectl edit pvcto increase size) - Backup and restore tested (Velero or snapshot-based)
- Multi-attach works for RWX volumes (if applicable)
- Mount options correct for NFS volumes
- Encryption at rest enabled (Azure default: SSE with platform-managed keys)
- Zone redundancy configured for HA workloads (ZRS storage classes)
# Quick storage benchmark with fio
kubectl run fio-test --image=csainaboxacr.azurecr.io/tools/fio:latest --rm -it \
--overrides='{"spec":{"containers":[{"name":"fio","image":"csainaboxacr.azurecr.io/tools/fio:latest","command":["fio","--name=randwrite","--ioengine=libaio","--direct=1","--bs=4k","--numjobs=4","--size=1G","--runtime=60","--rw=randwrite","--filename=/data/test"],"volumeMounts":[{"name":"data","mountPath":"/data"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"test-pvc"}}]}}' \
-- /bin/sh
Maintainers: CSA-in-a-Box core team Last updated: 2026-04-30 Related: Cluster Migration | Workload Migration | Tutorial: Velero Migration