Managed Disks are Azure’s persistent storage solution, and the Container Storage Interface (CSI) driver is how Kubernetes talks to them.

Let’s see it in action. Imagine you have a pod that needs to store data long-term, even if the pod restarts or the node it’s on goes down.

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: my-container
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: azure
      mountPath: /mnt/azure
  volumes:
  - name: azure
    persistentVolumeClaim:
      claimName: azure-managed-disk

This pod definition references a PersistentVolumeClaim named azure-managed-disk. This PVC is the bridge between your pod’s storage requirement and an actual Azure Managed Disk.

The problem this solves is fundamental: stateless applications are easy to manage in Kubernetes, but stateful applications require reliable, persistent storage. Before CSI, Kubernetes relied on in-tree drivers, which were harder to update and extend. The CSI driver decouples storage provider logic from the core Kubernetes code, allowing for faster innovation and support for new storage features.

Internally, when Kubernetes needs to provision storage for the azure-managed-disk PVC, it interacts with the Azure Disk CSI driver. The driver then makes calls to the Azure API to create or attach an Azure Managed Disk to the node where the pod is scheduled.

Here’s how the PersistentVolumeClaim might look:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: azure-managed-disk
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: managed-csi
  resources:
    requests:
      storage: 10Gi

The storageClassName: managed-csi is crucial. It tells Kubernetes to use the CSI driver configured for Azure Managed Disks. accessModes like ReadWriteOnce define how the disk can be mounted (in this case, by a single node at a time). The resources.requests.storage specifies the desired size of the disk.

When you apply this PVC, the CSI driver’s provisioner component watches for new PVCs with the managed-csi StorageClass. It then calls the Azure API to create a Standard_LRS disk (by default) of 10Gi. Once the disk is created, the CSI driver creates a PersistentVolume object in Kubernetes that represents this actual Azure Managed Disk. The PVC is then bound to this PV.

For the pod to use the disk, the CSI driver’s node-plugin component on the node where the pod is scheduled is responsible for attaching the Managed Disk to the node and then mounting it into the pod’s filesystem at /mnt/azure. This attachment is an I/O-level operation performed by the Azure VM agent.

The managed-csi StorageClass itself is defined by an administrator and points to the CSI driver. It might look something like this:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-csi
provisioner: disk.csi.azure.com
parameters:
  skuName: Standard_LRS
  location: eastus
  kind: managed
reclaimPolicy: Delete
volumeBindingMode: Immediate

This StorageClass tells Kubernetes to use disk.csi.azure.com as the provisioner. The parameters specify details like the skuName (e.g., Standard_LRS, Premium_LRS, StandardSSD_LRS), location, and kind (which is managed for Managed Disks). reclaimPolicy: Delete means the Azure Managed Disk will be deleted when the PVC is deleted. volumeBindingMode: Immediate means the PV is provisioned as soon as the PVC is created.

A subtle but powerful aspect is how ReadWriteOnce access mode is enforced. When a CSI driver indicates a volume supports ReadWriteOnce, the Azure API ensures that the specific Managed Disk is attached to only one VM at a time. This is a hard constraint at the Azure infrastructure level, not just a Kubernetes enforcement.

The next step is understanding how to handle multiple pods needing access to the same data, which leads to ReadWriteMany access modes and Azure Files or other CSI drivers.

Want structured learning?

Take the full Aks course →