Raw New Markdown
Generating updated version of doc...
Rendered New Markdown
Generating updated version of doc...
---
title: Create and Manage Persistent Volumes with Azure Files in Azure Kubernetes Service (AKS)
description: Learn how to create and manage persistent volumes using Azure Files with the Container Storage Interface (CSI) driver in Azure Kubernetes Service (AKS) to provide scalable and reliable storage for your containerized applications.
ms.topic: how-to
ms.subservice: aks-storage
ms.service: azure-kubernetes-service
ms.custom: aeo-round-2
ms.date: 08/25/2026
author: schaffererin
ms.author: schaffererin
ai-usage: ai-assisted
# Customer intent: "As a Kubernetes administrator, I want to learn how to create and manage persistent volumes using Azure Files CSI drivers in Azure Kubernetes Service (AKS) so that I can provide scalable and reliable storage solutions for my containerized applications."
---
# Create and manage persistent volumes (PVs) with Azure Files in Azure Kubernetes Service (AKS)
Azure Files enables multiple pods to share persistent storage in AKS using SMB or NFS protocols, surviving pod restarts, node failures, and cluster scaling events. This article shows you how to dynamically and statically create an Azure file share for use by multiple pods in an Azure Kubernetes Service (AKS) cluster.
> [!NOTE]
> The Azure Files CSI driver only permits the mounting of SMB file shares using key-based (NTLM v2) authentication, and therefore doesn't support the maximum security profile of Azure File share settings. Mounting NFS file shares doesn't require key-based authentication.
> [!NOTE]
> We recommend FIO when running benchmarking tests. For more information, see [benchmarking tools and tests](/azure/storage/files/nfs-performance#benchmarking-tools-and-tests).
## Quick-start checklist
1. **Verify prerequisites**: Ensure Azure CLI 2.0.59+, Azure Files CSI driver enabled, and storage account available
1. **Choose or create a storage class**: Use built-in classes (`azurefile-csi`, `azurefile-csi-premium`) or create a custom one
1. **Create a PersistentVolumeClaim (PVC)**: Define storage size and access mode (typically ReadWriteMany)
1. **Create a pod**: Reference the PVC in your pod's volume configuration
1. **Verify the mount**: Confirm the volume is mounted correctly using `kubectl describe pod`
## Prerequisites
- Azure CLI version 2.0.59 or later installed and configured. Find the version using the `az --version` command. To install or upgrade, see [Install Azure CLI][install-azure-cli].
- The [Azure Files CSI driver](./csi-storage-drivers.md) enabled on your AKS cluster.
- An Azure [storage account][azure-storage-account].
- When choosing between SSD (Premium) and HDD (Standard) file shares, it's important you understand the provisioning model and requirements of the expected usage pattern you plan to run on Azure Files. Azure Files has three billing models: [provisioned v2](/azure/storage/files/understanding-billing#provisioned-v2-model) (recommended), [pay-as-you-go](/azure/storage/files/understanding-billing#pay-as-you-go-model), and the legacy [provisioned v1](/azure/storage/files/understanding-billing#provisioned-v1-model). For more information, see [Choosing an Azure Files performance tier based on usage patterns][azure-files-usage].
## Create a complete dynamically provisioned volume
The following example creates a general-purpose Azure Files storage class, a PVC that dynamically provisions a file share, and a pod that mounts the file share. The example uses the `Standard_LRS` SKU and SMB protocol. For production workloads, choose the [Azure Files performance tier and redundancy option][azure-files-usage] that meets your performance and availability requirements.
1. Create a file named `azure-file-dynamic.yaml` and paste in the following manifest:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-standard
provisioner: file.csi.azure.com
allowVolumeExpansion: true
reclaimPolicy: Delete
volumeBindingMode: Immediate
parameters:
skuName: Standard_LRS
protocol: smb
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: azurefile-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: azurefile-csi-standard
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
name: azurefile-app
spec:
containers:
- name: nginx
image: mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
volumeMounts:
- name: azurefile-volume
mountPath: /mnt/azure
volumes:
- name: azurefile-volume
persistentVolumeClaim:
claimName: azurefile-pvc
```
1. Apply the manifest using the [`kubectl apply`][kubectl-apply] command:
```bash
kubectl apply -f azure-file-dynamic.yaml
```
1. Confirm that the PVC is in the `Bound` state and the pod is in the `Running` state:
```bash
kubectl get storageclass azurefile-csi-standard
kubectl get pvc azurefile-pvc
kubectl get pod azurefile-app
```
1. Verify that the pod can write to and read from the dynamically provisioned file share:
```bash
kubectl exec azurefile-app -- sh -c "echo 'Azure Files is mounted' > /mnt/azure/test.txt"
kubectl exec azurefile-app -- cat /mnt/azure/test.txt
```
Your output should resemble the following example output:
```output
Azure Files is mounted
```
1. When you no longer need the example resources, delete them:
```bash
kubectl delete -f azure-file-dynamic.yaml
```
Because the storage class uses the `Delete` reclaim policy, deleting the PVC also deletes the dynamically provisioned Azure file share.
## Use built-in storage classes to create dynamic PVs with Azure Files
Storage classes define how a unit of storage is dynamically created with a persistent volume. A storage account is automatically created in the [node resource group][node-resource-group] for use with the storage class to hold the Azure Files file share. When you use CSI drivers on AKS, there are two extra built-in `StorageClasses` that use the Azure Files CSI storage drivers (the other CSI storage classes are created with the cluster alongside the in-tree default storage classes):
- `azurefile-csi`: Creates an Azure file share on HDD storage.
- `azurefile-csi-premium`: Creates an Azure file share on SSD storage.
The reclaim policy on both storage classes ensures that the underlying Azure file share is deleted when the respective PV is deleted. The storage classes also configure the file shares to be expandable, you just need to edit the [persistent volume claim][persistent-volume-claim-overview] (PVC) with the new size.
You can select one of the following [Azure storage redundancy SKUs][storage-skus] for the `skuname` parameter in the storage class definition:
- **PremiumV2_LRS** (recommended): SSD provisioned v2, locally redundant storage
- **PremiumV2_ZRS** (recommended): SSD provisioned v2, zone-redundant storage
- **Premium_LRS**: SSD provisioned v1 (legacy), locally redundant storage
- **Premium_ZRS**: SSD provisioned v1 (legacy), zone-redundant storage
- **StandardV2_LRS**: HDD provisioned v2, locally redundant storage
- **StandardV2_ZRS**: HDD provisioned v2, zone-redundant storage
- **StandardV2_GRS**: HDD provisioned v2, geo-redundant storage
- **StandardV2_GZRS**: HDD provisioned v2, geo-zone-redundant storage
- **Standard_LRS**: HDD pay-as-you-go, locally redundant storage
- **Standard_GRS**: HDD pay-as-you-go, geo-redundant storage
- **Standard_ZRS**: HDD pay-as-you-go, zone-redundant storage
> [!IMPORTANT]
> To use the provisioned v2 billing model for Azure Files, you must use the Azure Files CSI driver [version 1.35.0](https://github.com/kubernetes-sigs/azurefile-csi-driver/releases/tag/v1.35.0) or later.
> [!NOTE]
> For new deployments, we recommend SSD provisioned v2 (`PremiumV2_LRS` or `PremiumV2_ZRS`) for most workloads. SSD file shares offer higher performance and low-latency disk support for I/O-intensive workloads. The minimum file share capacity for Premium accounts is 100 GiB.
## Create custom storage classes for dynamic PVs with Azure Files
The default storage classes are suitable for most scenarios. In some cases, you might want to have your own storage class customized with your own parameters. For example, you might want to configure the `mountOptions` of the file share.
1. Create a file named `azure-file-sc.yaml` and paste in the following example manifest:
```yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: my-azurefile
provisioner: file.csi.azure.com
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- actimeo=30
- nosharesock
parameters:
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS (SSD v1), StandardV2_LRS (HDD v2), Standard_LRS (HDD pay-as-you-go)
```
1. Create the storage class using the [`kubectl apply`][kubectl-apply] command:
```bash
kubectl apply -f azure-file-sc.yaml
```
Your output should resemble the following example output:
```output
storageclass.storage.k8s.io/my-azurefile created
```
## Storage class parameters for dynamic PVs with Azure Files
The following table includes parameters you can use to define a custom storage class for your persistent volume claims (PVCs) with Azure Files:
| Name | Meaning | Available values | Required | Default value |
| ---- | ------- | ---------------- | -------- | ------------- |
| `accountAccessTier` | [Access tier for storage account][access-tiers-overview] | Standard account can choose `Hot` or `Cool`, and Premium account can only choose `Premium`. | No | Empty. Use default setting for different storage account types. |
| `accountQuota` | Limits the quota for an account. You can specify a maximum quota in GB (102400 GB by default). If the account exceeds the specified quota, the driver skips selecting the account. | | No | `102400` |
| `allowBlobPublicAccess` | Allow or disallow public access to all blobs or containers for storage account created by driver. | `true` or `false` | No | `false` |
| `createFolderIfNotExist` | Specify whether to create the folder if it doesn't exist in the Azure file share. Supported from Azure Files CSI driver v1.34.0. | `true` or `false` | No | `false` |
| `disableDeleteRetentionPolicy` | Specify whether disable DeleteRetentionPolicy for storage account created by driver. | `true` or `false` | No | `false` |
| `enableLargeFileShares` | Indicate whether the storage account should have large file shares enabled. Use this parameter only on a Standard account, because Premium accounts already support large file shares by default. | `true` or `false` | No | `false` |
| `folderName` | Specify folder name in Azure file share. Supports the `${pvc.metadata.name}`, `${pvc.metadata.namespace}`, and `${pv.metadata.name}` placeholders. | Existing folder name in Azure file share. | No | If folder name doesn't exist in file share, the mount fails. |
| `getLatestAccount` | Determines whether to get the latest account key based on the creation time. This driver gets the first key by default. | `true` or `false` | No | `false` |
| `location` | Specify the Azure region of the Azure storage account. | For example, `eastus`. | No | If empty, driver uses the same location name as current AKS cluster. |
| `matchTags` | Match tags when driver tries to find a suitable storage account. | `true` or `false` | No | `false` |
| `networkEndpointType` | Specify network endpoint type for the storage account created by driver. If `privateEndpoint` is specified, a private endpoint is created for the storage account. For other cases, a service endpoint is created by default. | "",`privateEndpoint` | No | "" |
| `protocol` | Specify file share protocol. | `smb`, `nfs` | No | `smb` |
| `provisionedBandwidth` | Provisioned throughput (MiB/s) for the [Azure Files provisioned v2 model](/azure/storage/files/understanding-billing#provisioned-v2-model). Only applicable to `PremiumV2` and `StandardV2` SKUs. Supported from driver v1.33.4. | Integer string, for example `"200"`. | No | |
| `provisionedIOPS` | Provisioned IOPS for the [Azure Files provisioned v2 model](/azure/storage/files/understanding-billing#provisioned-v2-model). Only applicable to `PremiumV2` and `StandardV2` SKUs. Supported from driver v1.33.4. | Integer string, for example `"5000"`. | No | |
| `requireInfraEncryption` | Specify whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest for storage account created by driver. | `true` or `false` | No | `false` |
| `resourceGroup` | Specify the resource group for the Azure Files storage account. | Existing resource group name | No | If empty, driver uses the same resource group name as current AKS cluster. |
| `selectRandomMatchingAccount` | Determines whether to randomly select a matching account. By default, the driver always selects the first matching account in alphabetical order (Note: This driver uses account search cache, which results in uneven distribution of file creation across multiple accounts). | `true` or `false` | No | `false` |
| `server` | Specify Azure storage account server address. | Existing server address, for example `accountname.privatelink.file.core.windows.net`. | No | If empty, driver uses default `accountname.file.core.windows.net` or other sovereign cloud account address. |
| `shareAccessTier` | [Access tier for file share][storage-tiers] | General purpose v2 account can choose between `TransactionOptimized` (default), `Hot`, and `Cool`. Premium storage account type for file shares only. | No | Empty. Use default setting for different storage account types. |
| `shareName` | Specify Azure file share name. | Existing or new Azure file share name. | No | If empty, driver generates an Azure file share name. |
| `shareNamePrefix` | Specify Azure file share name prefix created by driver. | Share name can only contain lowercase letters, numbers, hyphens, and length should be fewer than 21 characters. | No | |
| `skuName` | Azure Files storage account type (alias: `storageAccountType`) | `Standard_LRS`, `Standard_ZRS`, `Standard_GRS`, `Standard_RAGRS`, `Standard_RAGZRS`,`Premium_LRS`, `Premium_ZRS`, `StandardV2_LRS`, `StandardV2_ZRS`, `StandardV2_GRS`, `StandardV2_GZRS`, `PremiumV2_LRS`, `PremiumV2_ZRS` | No | `Standard_LRS` <br> Minimum file share size for Premium account type is 100 GB. <br> ZRS account type is supported in limited regions. <br> NFS file share only supports Premium account type. <br> Standard V2 SKU names are for [Azure Files provisioned v2 model](/azure/storage/files/understanding-billing#provisioned-v2-model). |
| `storageAccount` | Specify an Azure storage account name. | storageAccountName | No | When a specific storage account name is not provided, the driver will look for a suitable storage account that matches the account settings within the same resource group. If it fails to find a matching storage account, it will create a new one. However, if a storage account name is specified, the storage account must already exist. |
| `storageEndpointSuffix` | Specify Azure storage endpoint suffix. | `core.windows.net`, `core.chinacloudapi.cn`, etc. | No | If empty, driver uses default storage endpoint suffix according to cloud environment. For example, `core.windows.net`. |
| `subscriptionID` | Specify Azure subscription ID where Azure file share is created. | Azure subscription ID | No | If not empty, `resourceGroup` must be provided. |
| `tags` | [Tags][tag-resources] are created in new storage account. | Tag format: 'foo=aaa,bar=bbb' | No | "" |
| --- | **The following parameters are only for SMB protocol** | --- | --- | --- |
| `clientID` | Specify the Azure client ID used to create the Azure file share. If empty, the kubelet managed identity is used when mounting without an account key. | Azure client ID | No | |
| `enableMultichannel` | Specify whether to enable [SMB multichannel](/azure/storage/files/files-smb-protocol?tabs=azure-portal#smb-multichannel) for a Premium storage account. Used with the `max_channels=4` (or `2`, `3`) mount option. | `true` or `false` | No | `false` |
| `storeAccountKey` | Specify whether to store account key to Kubernetes secret. | `true` or `false` <br> `false` means driver uses kubelet identity to get account key. | No | `true` |
| `secretName` | Specify secret name to store account key. | | No | |
| `secretNamespace` | Specify the namespace of secret to store account key. <br><br> **Note**: <br> If `secretNamespace` isn't specified, the secret is created in the same namespace as the pod. | `default`,`kube-system`, etc. | No | PVC namespace, for example `csi.storage.k8s.io/pvc/namespace` |
| `useDataPlaneAPI` | Specify whether to use [data plane API][data-plane-api] for file share create/delete/resize, which could solve the SRP API throttling issue because the data plane API has almost no limit, while it would fail when there's firewall or Vnet settings on storage account. The `oauth` value (supported from driver v1.33.0) uses an OAuth token for data plane API authentication. | `true`, `false`, or `oauth` | No | `false` |
| --- | **The following parameters are only for NFS protocol** | --- | --- | --- |
| `allowSharedKeyAccess` | Allow or disallow shared key access for the storage account created by the driver. | `true` or `false` | No | `true` |
| `encryptInTransit` | Support [encryption in transit (EiT) for NFS shares](/azure/storage/files/encryption-in-transit-for-nfs-shares). | `true` or `false` | No | `false` |
| `mountPermissions` | Mounted folder permissions. The default is `0777`. If set to `0`, driver doesn't perform `chmod` after mount | `0777` | No | |
| `rootSquashType` | Specify root squashing behavior on the share. The default is `NoRootSquash` | `AllSquash`, `NoRootSquash`, `RootSquash` | No | |
| --- | **The following parameters are only for virtual network setting (for example: NFS, private endpoint)** | --- | --- | --- |
| `fsGroupChangePolicy` | Indicates how the driver changes volume's ownership. Pod `securityContext.fsGroupChangePolicy` is ignored. | `OnRootMismatch` (default), `Always`, `None` | No | `OnRootMismatch` |
| `publicNetworkAccess` | The `PublicNetworkAccess` property of the storage account created by the driver. | `Enabled`, `Disabled`, `SecuredByPerimeter` | No | |
| `subnetName` | Subnet name | Existing subnet name of the agent node. | No | If empty, driver uses the `subnetName` value in Azure cloud config file. |
| `vnetLinkName` | Virtual network link name associated with the private DNS zone. | Existing vnet link name. | No | If empty, driver uses `<vnetName>-vnetlink`. |
| `vnetName` | Virtual network name | Existing virtual network name. | No | If empty, driver will update all the subnets under the cluster virtual network. |
| `vnetResourceGroup` | Specify virtual network resource group where virtual network is defined. | Existing resource group name. | No | If empty, driver uses the `vnetResourceGroup` value in Azure cloud config file. |
> [!IMPORTANT]
> The `tags` storage class parameter is applied to the storage account when the Azure Files CSI driver provisions the volume. After the persistent volume is created, the `PersistentVolume` spec is immutable, so editing or patching the PV to change tags or other volume attributes fails. Updating the storage class later affects only newly provisioned volumes.
>
> To update tags on an existing volume, change them on the underlying storage account in Azure. If your storage class uses an existing storage account, update tags on that account. This operation doesn't interrupt existing mounts, pods, or data access, and updated Azure tags aren't synchronized back to the Kubernetes PV YAML or metadata. For example:
>
> ```azurecli-interactive
> az storage account update \
> --name mystorageaccount \
> --resource-group MC_myResourceGroup_myAKSCluster_eastus \
> --set tags.abc=ABC123
> ```
> [!NOTE]
> If the storage account is created by the driver, then you only need to specify `networkEndpointType: privateEndpoint` parameter in storage class. The CSI driver creates the private endpoint and private DNS zone (named `privatelink.file.core.windows.net`) together with the account. If you bring your own storage account, then you need to [create the private endpoint][storage-account-private-endpoint] for the storage account. If you're using Azure Files storage in a network isolated cluster, you must create a custom storage class with "networkEndpointType: privateEndpoint". You can use the following example manifest as a reference:
>
> ```yaml
> apiVersion: storage.k8s.io/v1
> kind: StorageClass
> metadata:
> name: azurefile-csi-private-custom
> provisioner: file.csi.azure.com
> allowVolumeExpansion: true
> parameters:
> skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, StandardV2_LRS, Standard_LRS
> networkEndpointType: privateEndpoint
> reclaimPolicy: Delete
> volumeBindingMode: Immediate
> mountOptions:
> # Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
> # Use 0777/uid=0/gid=0 only if app requires root or broad write access.
> - dir_mode=0755
> - file_mode=0755
> - uid=1000
> - gid=1000
> - mfsymlinks
> - cache=strict # https://linux.die.net/man/8/mount.cifs
> - nosharesock # reduce probability of reconnect race
> - actimeo=30 # reduce latency for metadata-heavy workload
> - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks
> ```
## Create a PVC with Azure Files
A PVC uses the storage class object to dynamically provision an Azure file share. You can use the example YAML manifest in this section to create a PVC that's _100 GB_ in size with _ReadWriteMany_ access. For more information on access modes, see [Kubernetes PV access modes][access-modes].
1. Create a file named `azure-file-pvc.yaml` and paste in the following YAML. Make sure the `storageClassName` matches the name of your existing storage class.
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-azurefile
spec:
accessModes:
- ReadWriteMany
storageClassName: my-azurefile
resources:
requests:
storage: 100Gi
```
> [!NOTE]
> If using the `Premium_LRS` SKU for your storage class, the minimum value for `storage` must be `100Gi`.
1. Create the PVC using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f azure-file-pvc.yaml
```
1. View the status of the PVC using the [`kubectl get`][kubectl-get] command:
```bash
kubectl get pvc my-azurefile
```
Your output should resemble the following example output, which shows that the PVC is in a `Bound` state, and a PV was dynamically created to satisfy the claim:
```output
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-azurefile Bound pvc-aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb 100Gi RWX my-azurefile 5m
```
## Use a PVC with Azure Files in a pod
The example YAML manifest in this section creates a pod that uses the PVC _my-azurefile_ to mount the Azure Files file share at the _/mnt/azure_ path. For Windows Server containers, specify a `mountPath` using the Windows path convention, such as _'D:'_.
1. Create a file named `azure-pvc-files.yaml`, and paste in the following YAML. Make sure the `claimName` matches the name of your existing PVC.
```yaml
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: mypod
image: mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
volumeMounts:
- mountPath: /mnt/azure
name: volume
readOnly: false
volumes:
- name: volume
persistentVolumeClaim:
claimName: my-azurefile
```
1. Create the pod using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f azure-pvc-files.yaml
```
1. View the status of the pod using the [`kubectl describe`][kubectl-describe] command:
```bash
kubectl describe pod mypod
```
Your output should resemble the following example output, which shows that the pod is running and the volume is mounted at the correct path:
```output
Containers:
mypod:
Container ID: docker://BB22CC33DD44EE55FF66AA77BB88CC99DD00EE11
Image: mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine
Image ID: docker-pullable://nginx@sha256:AA11BB22CC33DD44EE55FF66AA77BB88CC99DD00
State: Running
Started: Fri, 01 Mar 2019 23:56:16 +0000
Ready: True
Mounts:
/mnt/azure from volume (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-8rv4z (ro)
[...]
Volumes:
volume:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: my-azurefile
ReadOnly: false
[...]
```
## Mount options for Azure Files
The location to configure mount options (`mountOptions`) depends on whether you're provisioning dynamic or static persistent volumes:
- If you're dynamically provisioning a volume with a storage class, specify the mount options on the storage class object (kind: StorageClass).
- If you're statically provisioning a volume, specify the mount options on the PV object (kind: PersistentVolume).
- If you're [mounting the file share as an inline volume](#mount-file-share-as-an-inline-volume), specify the mount options on the Pod object (kind: Pod).
For more information, see [Mount options](https://kubernetes.io/docs/concepts/storage/storage-classes/#mount-options).
The default value for `fileMode` and `dirMode` is _0777_ for Kubernetes versions 1.13.0 and above. The following example sets _0777_:
```yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: my-azurefile
provisioner: file.csi.azure.com
allowVolumeExpansion: true
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- actimeo=30
- nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks
parameters:
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS (SSD v1), StandardV2_LRS (HDD v2), Standard_LRS (HDD pay-as-you-go)
```
### Recommended mount options for SMB shares
Recommended mount options for SMB shares are provided in the following storage class example:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-premiumv2-custom
provisioner: file.csi.azure.com
allowVolumeExpansion: true
parameters:
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, StandardV2_LRS, Standard_LRS, Standard_ZRS
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks # support symbolic links
- cache=strict # https://linux.die.net/man/8/mount.cifs
- nosharesock # reduces probability of reconnect race
- actimeo=30 # reduces latency for metadata-heavy workload
- nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks
```
If you're using premium (SSD) file shares with the SMB protocol and your workload is metadata heavy, enroll to use the [metadata caching](/azure/storage/files/smb-performance?tabs=portal#metadata-caching-for-ssd-file-shares) feature to improve performance.
For more information, see [Improve performance for SMB Azure file shares](/azure/storage/files/smb-performance).
### Recommended mount options for NFS shares
Recommended mount options for NFS shares are provided in the following storage class example:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-premiumv2-custom
provisioner: file.csi.azure.com
parameters:
protocol: nfs
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, PremiumV2_ZRS
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
- nconnect=4 # improves performance by enabling multiple connections to share
- noresvport # improves availability
- actimeo=30 # reduces latency for metadata-heavy workloads
```
Increase [read-ahead size](/azure/storage/files/nfs-performance#increase-read-ahead-size-to-improve-read-throughput) to improve read throughput.
While Azure Files supports setting nconnect up to the maximum setting of 16, we recommend configuring the mount options with the optimal setting of nconnect=4. Currently, there are no gains beyond four channels for the Azure Files implementation of nconnect.
## Create a volume snapshot from a PVC with Azure Files
The Azure Files CSI driver supports creating [snapshots of persistent volumes](https://kubernetes-csi.github.io/docs/snapshot-restore-feature.html) and the underlying file shares.
1. Create a [volume snapshot class](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/deploy/example/snapshot/volumesnapshotclass-azurefile.yaml) using the [`kubectl apply`][kubectl-apply] command:
```bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/azurefile-csi-driver/master/deploy/example/snapshot/volumesnapshotclass-azurefile.yaml
```
Your output should resemble the following example output:
```output
volumesnapshotclass.snapshot.storage.k8s.io/csi-azurefile-vsc created
```
1. Create a [volume snapshot](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/deploy/example/snapshot/volumesnapshot-azurefile.yaml) from the dynamic PVC you created earlier in this tutorial using the [`kubectl apply`][kubectl-apply] command:
```bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/azurefile-csi-driver/master/deploy/example/snapshot/volumesnapshot-azurefile.yaml
```
Your output should resemble the following example output:
```output
volumesnapshot.snapshot.storage.k8s.io/azurefile-volume-snapshot created
```
1. View the status of the volume snapshot using the [`kubectl describe`][kubectl-describe] command:
```bash
kubectl describe volumesnapshot azurefile-volume-snapshot
```
Your output should resemble the following example output, which shows that the volume snapshot isn't ready to use because the driver is still creating the snapshot of the underlying Azure file share:
```output
Name: azurefile-volume-snapshot
Namespace: default
Labels: <none>
Annotations: API Version: snapshot.storage.k8s.io/v1beta1
Kind: VolumeSnapshot
Metadata:
Creation Timestamp: 2020-08-27T22:37:41Z
Finalizers:
snapshot.storage.kubernetes.io/volumesnapshot-as-source-protection
snapshot.storage.kubernetes.io/volumesnapshot-bound-protection
Generation: 1
Resource Version: 955091
Self Link: /apis/snapshot.storage.k8s.io/v1beta1/namespaces/default/volumesnapshots/azurefile-volume-snapshot
UID: 00aa00aa-bb11-cc22-dd33-44ee44ee44ee
Spec:
Source:
Persistent Volume Claim Name: pvc-azurefile
Volume Snapshot Class Name: csi-azurefile-vsc
Status:
Bound Volume Snapshot Content Name: snapcontent-00aa00aa-bb11-cc22-dd33-44ee44ee44ee
Ready To Use: false
Events: <none>
```
## Resize a persistent volume with Azure Files
> [!NOTE]
> Shrinking persistent volumes isn't currently supported. Trying to patch an existing PVC with a smaller size than the current one leads to the following error message:
>
> `The persistentVolumeClaim "pvc-azurefile" is invalid: spec.resources.requests.storage: Forbidden: field can not be less than previous value.`
You can request a larger volume for a PVC by editing the PVC object to specify a larger size. This change triggers the expansion of the underlying volume that backs the PV. A new PV is never created to satisfy the claim. Instead, an existing volume is resized.
In AKS, the built-in `azurefile-csi` storage class supports expansion. Custom storage classes must set `allowVolumeExpansion: true`. The PVC requested a 100 GiB file share.
1. Verify the current size of the PVC and the filesystem inside the pod using the `kubectl exec` command to run the `df -h` command inside the pod:
```bash
kubectl exec -it nginx-azurefile -- df -h /mnt/azurefile
```
Your output should resemble the following example output, which shows that the filesystem is 100 GB in size:
```output
Filesystem Size Used Avail Use% Mounted on
//a123b4c567de89fghi01jk2.file.core.windows.net/pvc-00aa00aa-bb11-cc22-dd33-44ee44ee44ee 100G 128K 100G 1% /mnt/azurefile
```
1. Expand the PVC by increasing the `spec.resources.requests.storage` field using the `kubectl patch` command. In this example, we increase the file share to 200 GiB:
```bash
kubectl patch pvc pvc-azurefile --type merge --patch '{"spec": {"resources": {"requests": {"storage": "200Gi"}}}}'
```
Your output should resemble the following example output, which shows that the PVC was patched successfully:
```output
persistentvolumeclaim/pvc-azurefile patched
```
1. Verify the PVC was successfully resized and the new size is reflected in the pod by using the `kubectl get pvc` command and the `df -h` command inside the pod:
```bash
kubectl get pvc pvc-azurefile
```
Your output should resemble the following example output, which shows that the PVC is still in a `Bound` state, and the capacity was updated to 200 GiB:
```output
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-azurefile Bound pvc-00aa00aa-bb11-cc22-dd33-44ee44ee44ee 200Gi RWX azurefile-csi 64m
```
1. Verify the new size of the filesystem inside the pod using the `kubectl exec` command to run the `df -h` command inside the pod:
```bash
kubectl exec -it nginx-azurefile -- df -h /mnt/azurefile
```
Your output should resemble the following example output, which shows that the filesystem is now 200 GB in size:
```output
Filesystem Size Used Avail Use% Mounted on
//a123b4c567de89fghi01jk2.file.core.windows.net/pvc-bbbbbbbb-1111-2222-3333-cccccccccccc 200G 128K 200G 1% /mnt/azurefile
```
## Use a persistent volume with private Azure Files storage (private endpoint)
If your Azure Files resources are protected with a private endpoint, you must create your own storage class. Make sure that you've [configured your DNS settings to resolve the private endpoint IP address to the FQDN of the connection string][azure-private-endpoint-dns]. When you create the storage class using the Azure Files CSI driver, you need to specify the `networkEndpointType` parameter with the value `privateEndpoint`, and provide the following parameters:
- `resourceGroup`: The resource group where the storage account is deployed.
- `storageAccount`: The storage account name.
- `server`: The FQDN of the storage account's private endpoint.
1. Create a file named `private-azure-file-sc.yaml` and then paste in the following manifest. Make sure you replace the placeholders for `<resourceGroup>` and `<storageAccountName>`.
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-private-custom
provisioner: file.csi.azure.com
allowVolumeExpansion: true
parameters:
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, StandardV2_LRS
resourceGroup: <resourceGroup>
storageAccount: <storageAccountName>
server: <storageAccountName>.file.core.windows.net
networkEndpointType: privateEndpoint
reclaimPolicy: Delete
volumeBindingMode: Immediate
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict # https://linux.die.net/man/8/mount.cifs
- nosharesock # reduce probability of reconnect race
- actimeo=30 # reduce latency for metadata-heavy workload
- nobrl
```
1. Create the storage class using the `kubectl apply` command:
```bash
kubectl apply -f private-azure-file-sc.yaml
```
Your output should resemble the following example output:
```output
storageclass.storage.k8s.io/private-azurefile-csi created
```
1. Create a file named `private-pvc.yaml` and paste in the following manifest. Make sure the `storageClassName` matches the name of your existing storage class.
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: private-azurefile-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: private-azurefile-csi
resources:
requests:
storage: 100Gi
```
1. Create the PVC using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f private-pvc.yaml
```
## Use Azure Files with Windows containers
The Azure Files CSI driver also supports Windows nodes and containers. To use Windows containers, follow the [Windows containers quickstart](./learn/quick-windows-container-deploy-cli.md) to add a Windows node pool. After you have a Windows node pool, you can use the built-in storage classes like `azurefile-csi` or create a custom one. The example [Windows-based stateful set](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/deploy/example/windows/statefulset.yaml) in this section saves timestamps into a file `data.txt` every second, which is mounted to an Azure file share using the Azure Files CSI driver.
1. Create the stateful set using the [`kubectl apply`][kubectl-apply] command:
```bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/azurefile-csi-driver/master/deploy/example/windows/statefulset.yaml
```
Your output should resemble the following example output:
```output
statefulset.apps/busybox-azurefile created
```
1. Validate the timestamps are being written to the file share using the following `kubectl exec` commands to run the `cat` command inside the pod:
```bash
kubectl exec -it busybox-azurefile-0 -- cat c:\\mnt\\azurefile\\data.txt # on Linux/MacOS Bash
kubectl exec -it busybox-azurefile-0 -- cat c:\mnt\azurefile\data.txt # on Windows Powershell/CMD
```
Your output should resemble the following example output, which shows that timestamps are being written to the file share every second:
```output
2020-08-27 22:11:01Z
2020-08-27 22:11:02Z
2020-08-27 22:11:04Z
(...)
```
## Use NFS protocol with Azure Files
[Azure Files supports the NFS v4.1 protocol](/azure/storage/files/storage-files-how-to-create-nfs-shares). NFS version 4.1 support for Azure Files provides you with a fully managed NFS file system as a service built on a highly available and highly durable distributed resilient storage platform.
This option is optimized for random access workloads with in-place data updates and provides full POSIX file system support. This section shows you how to use NFS shares with the Azure Files CSI driver on an AKS cluster.
### Prerequisites for using NFS shares with Azure Files
- NFS requires SSD file shares (such as `PremiumV2_LRS`, `PremiumV2_ZRS`, `Premium_LRS`, or `Premium_ZRS`) and a virtual network-enabled storage account.
- Your AKS cluster _control plane_ identity (that is, your AKS cluster name) is added to the [Contributor](/azure/role-based-access-control/built-in-roles#contributor) role on the VNet and NetworkSecurityGroup.
- Your AKS cluster's service principal or managed identity must be added to the Contributor role on the storage account.
> [!NOTE]
> You can use a private endpoint instead of allowing access to the selected VNet.
### Optimize read and write size options
This section provides information about how to approach performance tuning NFS with the Azure Files CSI driver with the `rsize` and `wsize` options. The `rsize` and `wsize` options set the maximum transfer size of an NFS operation. If `rsize` or `wsize` aren't specified on mount, the client and server negotiate the largest size supported by the two. Currently, both Azure Files and modern Linux distributions support read and write sizes as large as 1,048,576 Bytes (1 MiB).
Optimal performance is based on efficient client-server communication. Increasing or decreasing the **mount** read and write option size values can improve NFS performance. The default size of the read/write packets transferred between client and server are 8 KB for NFS version 2, and 32 KB for NFS version 3 and 4. These defaults might be too large or too small. Reducing the `rsize` and `wsize` might improve NFS performance in a congested network by sending smaller packets for each NFS-read reply and write request. However, this can increase the number of packets needed to send data across the network, increasing total network traffic and CPU utilization on the client and server.
It's important that you perform testing to find an `rsize` and `wsize` that sustain efficient packet transfer and don't decrease throughput and increase latency.
The following example manifest configures the `mountOptions` section in a storage class to a maximum `rsize` and `wsize` of 256-KiB:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-premiumv2-custom
provisioner: file.csi.azure.com
allowVolumeExpansion: true
parameters:
protocol: nfs
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, PremiumV2_ZRS
mountOptions:
- nconnect=4
- noresvport
- actimeo=30
- rsize=262144
- wsize=262144
```
For a list of supported `mountOptions`, see [NFS mount options][nfs-file-share-mount-options].
### Create NFS file share storage class
> [!NOTE]
> `vers`, `minorversion`, `sec` are configured by the Azure Files CSI driver. Specifying a value in your manifest for these properties isn't supported.
1. Create a file named `nfs-sc.yaml` and paste in the following manifest. Make sure to specify `protocol: nfs` in the parameters section, and adjust the `mountOptions` as needed for your workload.
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-premiumv2-custom
provisioner: file.csi.azure.com
allowVolumeExpansion: true
parameters:
protocol: nfs
skuName: PremiumV2_LRS # SSD provisioned v2 (recommended). Alternatives: Premium_LRS, Premium_ZRS, PremiumV2_ZRS
mountOptions:
- nconnect=4
- noresvport
- actimeo=30
```
1. After editing and saving the file, create the storage class using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f nfs-sc.yaml
```
Your output should resemble the following example output:
```output
storageclass.storage.k8s.io/azurefile-csi-nfs created
```
### Create a stateful set with an NFS-backed file share
1. Create a file named `nfs-ss.yaml` and paste in the following manifest. This configuration saves timestamps into a file `data.txt`.
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-azurefile
labels:
app: nginx
spec:
podManagementPolicy: Parallel # default is OrderedReady
serviceName: statefulset-azurefile
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: statefulset-azurefile
image: mcr.microsoft.com/oss/nginx/nginx:1.19.5
command:
- "/bin/bash"
- "-c"
- set -euo pipefail; while true; do echo $(date) >> /mnt/azurefile/outfile; sleep 1; done
volumeMounts:
- name: persistent-storage
mountPath: /mnt/azurefile
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: nginx
volumeClaimTemplates:
- metadata:
name: persistent-storage
spec:
storageClassName: azurefile-csi-premiumv2-custom
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 100Gi
```
1. Create the stateful set using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f nfs-ss.yaml
```
Your output should resemble the following example output:
```output
statefulset.apps/statefulset-azurefile created
```
1. Validate the contents of the volume using the following `kubectl exec` command to run the `df -h` command inside the pod:
```bash
kubectl exec -it statefulset-azurefile-0 -- df -h
```
Your output should resemble the following example output, which shows that the NFS file share is mounted at the correct path with the correct size:
```output
Filesystem Size Used Avail Use% Mounted on
...
/dev/sda1 29G 11G 19G 37% /etc/hosts
accountname.file.core.windows.net:/accountname/pvc-cccccccc-2222-3333-4444-dddddddddddd 100G 0 100G 0% /mnt/azurefile
...
```
Because the NFS file share is in a Premium storage account, the minimum file share size is 100 GiB. If you create a PVC with a small storage size, you might encounter an error similar to the following: _failed to create file share ... size (5)..._.
### Encryption in transit (EiT) for NFS file shares
> [!NOTE]
> The EiT feature is available starting with AKS version 1.33. Ubuntu 20.04 and Windows nodes aren't currently supported.
>
> The feature is supported in all Azure regions that [support SSD Azure file shares](/azure/storage/files/redundancy-premium-file-shares).
[Encryption in Transit (EiT)](/azure/storage/files/encryption-in-transit-for-nfs-shares) ensures that all read and writes to the NFS file shares within the virtual network are encrypted, providing an extra layer of security.
By setting `encryptInTransit: "true"` in StorageClass parameters or PersistentVolume `volumeAttributes`, you can enable data encryption in transit for NFS Azure file shares.
#### Dynamic provisioning (StorageClass)
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-premiumv2-eit
provisioner: file.csi.azure.com
allowVolumeExpansion: true
parameters:
protocol: nfs
skuName: PremiumV2_LRS
encryptInTransit: "true"
mountOptions:
- nconnect=4
- noresvport
- actimeo=30
```
#### Existing persistent volumes
For PVs that were created without encryption in transit, set `encryptInTransit: "true"` in `volumeAttributes`:
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-azurefile-eit
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
mountOptions:
- nconnect=4
- noresvport
- actimeo=30
csi:
driver: file.csi.azure.com
volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}"
volumeAttributes:
protocol: nfs
encryptInTransit: "true"
resourceGroup: "{resource-group-name}"
storageAccount: "{account-name}"
shareName: "{file-share-name}"
```
## Use managed identity to access Azure Files storage
Azure Files now supports managed identity based authentication for SMB access. This enables your applications to securely access Azure Files without storing or managing credentials.
> [!NOTE]
> Managed identity support for Azure Files in AKS is available starting with AKS version 1.34 on Linux nodes.
### Prerequisites for using managed identity to access Azure Files storage
- Ensure the [user-assigned Kubelet identity](use-managed-identity.md#create-a-kubelet-managed-identity) has the `Storage File Data SMB MI Admin` role on the storage account.
- If you use your own storage account, you need to assign `Storage File Data SMB MI Admin` role to the user-assigned Kubelet identity on that storage account.
- If the storage account is created by the CSI driver, grant `Storage File Data SMB MI Admin` role to the resource group where the storage account resides.
### Enable managed identity for dynamic PVs with Azure Files
To enable managed identity for dynamically provisioned volumes, you need to create a new storage class with `mountWithManagedIdentity`: `"true"` and deploy your stateful set using this storage class.
The following example manifest configures a storage class to use managed identity to access Azure Files:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi
provisioner: file.csi.azure.com
parameters:
resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided
storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided
mountWithManagedIdentity: "true"
# optional, clientID of the managed identity, kubelet identity would be used by default if it's not provided
clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict # https://linux.die.net/man/8/mount.cifs
- nosharesock # reduce probability of reconnect race
- actimeo=30 # reduce latency for metadata-heavy workload
- nobrl # disable sending byte range lock requests to the server
```
### Enable managed identity for static PVs with Azure Files
To use managed identity with statically provisioned Azure Files persistent volumes, ensure the following configuration:
1. Enable the SMBOauth on the storage account by running:
```bash
az storage account update --name <account-name> --resource-group <resource-group-name> --enable-smb-oauth true
```
1. Create a PV with `mountWithManagedIdentity`: `"true"` and mount the PV to your application pod.
The following example manifest configures a PV to use managed identity to access Azure Files:
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-azurefile
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: azurefile-csi
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict # https://linux.die.net/man/8/mount.cifs
- nosharesock # reduce probability of reconnect race
- actimeo=30 # reduce latency for metadata-heavy workload
- nobrl # disable sending byte range lock requests to the server
csi:
driver: file.csi.azure.com
# make sure volumeHandle is unique for every identical share in the cluster
volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}"
volumeAttributes:
resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided
storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided
shareName: EXISTING_FILE_SHARE_NAME
mountWithManagedIdentity: "true"
# optional, clientID of the managed identity, kubelet identity would be used by default if it's empty
clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx"
```
## Use workload identity to access Azure Files storage
Azure Files now supports workload identity-based authentication for SMB access. Workload identity enables pod-level, least-privilege access to Azure Files without tying application identity to the node lifecycle.
> [!NOTE]
> Workload identity support for Azure Files in AKS is available starting with AKS version 1.35.0 on Linux nodes.
### Prerequisites for using workload identity to access Azure Files storage
Before using workload identity to access Azure Files from AKS, complete the following prerequisites.
#### 1. Create a cluster with oidc-issuer enabled and get the AKS cluster credential
Create a new AKS cluster with the OIDC issuer enabled, or verify that it's already enabled. Follow the official [documentation](use-oidc-issuer.md#create-an-aks-cluster-with-the-oidc-issuer) for creating a new AKS cluster with the `--enable-oidc-issuer` parameter and retrieve the cluster credentials. And set the following environment variables:
```bash
export RESOURCE_GROUP=<your resource group name>
export CLUSTER_NAME=<your cluster name>
export REGION=<your region>
```
#### 2. Prepare the storage account
Create a new storage account and file share, or use an existing one. Refer to the Azure Files [documentation](/azure/storage/files/storage-how-to-use-files-portal) for detailed instructions. Set the following environment variables:
```bash
export STORAGE_RESOURCE_GROUP=<your storage account resource group>
export ACCOUNT=<your storage account name>
export SHARE=<your fileshare name> # optional
```
#### 3. Create or reuse a managed identity and grant required permissions
Create a user‑assigned managed identity, or reuse an existing one (for example, a [managed identity](managed-identity-overview.md) associated with the AKS node resource group). And retrieve the required identity and resource details:
```bash
export UAMI=<your managed identity name>
az identity create --name $UAMI --resource-group $RESOURCE_GROUP
export USER_ASSIGNED_CLIENT_ID="$(az identity show -g $RESOURCE_GROUP --name $UAMI --query 'clientId' -o tsv)"
export IDENTITY_TENANT=$(az aks show --name $CLUSTER_NAME --resource-group $RESOURCE_GROUP --query identity.tenantId -o tsv)
export ACCOUNT_SCOPE=$(az storage account show --name $ACCOUNT --query id -o tsv)
```
Grant the `Storage File Data SMB MI Admin` role to the managed identity. This role enables Azure Files mounting using workload identity tokens only, without relying on storage account keys.
```bash
az role assignment create --role "Storage File Data SMB MI Admin" --assignee $USER_ASSIGNED_CLIENT_ID --scope $ACCOUNT_SCOPE
```
#### 4. Create a Kubernetes ServiceAccount
Create a Kubernetes ServiceAccount that your workload will use.
```bash
export SERVICE_ACCOUNT_NAME=<your sa name>
export SERVICE_ACCOUNT_NAMESPACE=<your sa namespace>
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: ${SERVICE_ACCOUNT_NAME}
namespace: ${SERVICE_ACCOUNT_NAMESPACE}
EOF
```
#### 5. Create the federated identity credential
Create the federated identity credential between the managed identity, service account issuer, and subject using the `az identity federated-credential create` command.
```bash
export FEDERATED_IDENTITY_NAME=<your federated identity name>
export AKS_OIDC_ISSUER="$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)"
az identity federated-credential create --name $FEDERATED_IDENTITY_NAME \
--identity-name $UAMI \
--resource-group $RESOURCE_GROUP \
--issuer $AKS_OIDC_ISSUER \
--subject system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}
```
After completing these steps, workloads running with the specified ServiceAccount can authenticate to Azure Files using Microsoft Entra workload identity, without using storage account keys or node‑level managed identities.
### Enable workload identity for dynamic PVs with Azure Files
To use workload identity with dynamically provisioned Azure Files persistent volumes, ensure the following configuration:
1. Grant permissions to the CSI driver control plane identity
- Assign the `Storage Account Contributor` role to the identity used by the CSI driver control plane for the target storage account.
- If the storage account is created dynamically by the CSI driver, grant `Storage Account Contributor` role to the node resource group.
- By default, AKS cluster control plane identity is already assigned the `Storage Account Contributor` role on the node resource group for the storage account creation.
1. Create a new storage class with `mountWithWorkloadIdentityToken`: `"true"` and deploy your stateful set using this storage class.
The following example manifest configures a storage class to use workload identity to access Azure Files:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azurefile-csi-wi
provisioner: file.csi.azure.com
parameters:
resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided
storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided
mountWithWorkloadIdentityToken: "true"
# optional, clientID of the managed identity, kubelet identity would be used by default if it's not provided
clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
- dir_mode=0777 # modify this permission if you want to enhance the security
- file_mode=0777
- mfsymlinks
- cache=strict # https://linux.die.net/man/8/mount.cifs
- nosharesock # reduce probability of reconnect race
- actimeo=30 # reduce latency for metadata-heavy workload
- nobrl # disable sending byte range lock requests to the server
```
### Enable workload identity for static PVs with Azure Files
To use workload identity with statically provisioned Azure Files persistent volumes, ensure the following configuration:
1. Enable the SMBOauth on the storage account by running:
```bash
az storage account update --name <account-name> --resource-group <resource-group-name> --enable-smb-oauth true
```
1. Create a PV with `mountWithWorkloadIdentityToken`: `"true"` specified and mount the PV to your application pod.
The following example manifest configures a PV to use workload identity to access Azure Files:
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-azurefile
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: azurefile-csi
mountOptions:
- dir_mode=0777 # modify this permission if you want to enhance the security
- file_mode=0777
- uid=0
- gid=0
- mfsymlinks
- cache=strict # https://linux.die.net/man/8/mount.cifs
- nosharesock # reduce probability of reconnect race
- actimeo=30 # reduce latency for metadata-heavy workload
- nobrl # disable sending byte range lock requests to the server
csi:
driver: file.csi.azure.com
# make sure volumeHandle is unique for every identical share in the cluster
volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}"
volumeAttributes:
resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, node resource group by default if it's not provided
storageAccount: EXISTING_STORAGE_ACCOUNT_NAME # optional, a new account will be created if it's not provided
shareName: EXISTING_FILE_SHARE_NAME
mountWithWorkloadIdentityToken: "true"
# optional, clientID of the managed identity, kubelet identity would be used by default if it's empty
clientID: "xxxxx-xxxx-xxx-xxx-xxxxxxx"
```
## Create a static PV with Azure Files
The following sections provide instructions for creating a static PV with Azure Files. A static PV is a persistent volume that an administrator creates manually. This PV is available for use by pods in the cluster. To use a static PV, you create a PVC that references the PV, and then create a pod that references the PVC.
### Storage class parameters for static PVs with Azure Files
The following table includes parameters you can use to define a custom storage class for your static PVCs with Azure Files:
| Name | Meaning | Available values | Required | Default value |
| ---- | ------- | ---------------- | -------- | ------------- |
| `volumeAttributes.resourceGroup` | Specify an Azure resource group name. | myResourceGroup | No | If empty, driver uses the same resource group name as current cluster. |
| `volumeAttributes.storageAccount` | Specify an existing Azure storage account name. | storageAccountName | Yes | |
| `volumeAttributes.shareName` | Specify an Azure file share name. | fileShareName | Yes | |
| `volumeAttributes.folderName` | Specify a folder name in Azure file share. | folderName | No | If folder name doesn't exist in file share, mount would fail. |
| `volumeAttributes.protocol` | Specify file share protocol. | `smb`, `nfs` | No | `smb` |
| `volumeAttributes.server` | Specify Azure storage account server address | Existing server address, for example `accountname.privatelink.file.core.windows.net`. | No | If empty, driver uses default `accountname.file.core.windows.net` or other sovereign cloud account address. |
| --- | **The following parameters are only for SMB protocol** | --- | --- | --- |
| `volumeAttributes.secretName` | Specify a secret name that stores storage account name and key. | | No | |
| `volumeAttributes.secretNamespace` | Specify a secret namespace. | `default`,`kube-system`, etc. | No | PVC namespace (`csi.storage.k8s.io/pvc/namespace`) |
| `nodeStageSecretRef.name` | Specify a secret name that stores storage account name and key. | Existing secret name. | No | If empty, driver uses kubelet identity to get account key. |
| `nodeStageSecretRef.namespace` | Specify a secret namespace. | Kubernetes namespace | No | |
| --- | **The following parameters are only for NFS protocol** | --- | --- | --- |
| `volumeAttributes.fsGroupChangePolicy` | Indicates how the driver changes a volume's ownership. Pod `securityContext.fsGroupChangePolicy` is ignored. | `OnRootMismatch` (default), `Always`, `None` | No | `OnRootMismatch` |
| `volumeAttributes.mountPermissions` | Specify mounted folder permissions. The default is `0777` | | No | |
### Create an Azure file share
Before you can use an Azure Files file share as a Kubernetes volume, you must create an Azure Storage account and the file share.
1. Get the node resource group name of your AKS cluster using the [`az aks show`][az-aks-show] command with the `--query nodeResourceGroup` parameter.
```azurecli-interactive
az aks show --resource-group myResourceGroup --name myAKSCluster --query nodeResourceGroup -o tsv
```
The output of the command resembles the following example:
```azurecli-interactive
MC_myResourceGroup_myAKSCluster_eastus
```
1. Create a storage account using the [`az storage account create`][az-storage-account-create] command with the `--sku` parameter. The following command creates a storage account using the `Standard_LRS` SKU. Make sure to replace the following placeholders:
- `myAKSStorageAccount` with the name of the storage account
- `nodeResourceGroupName` with the name of the resource group that the AKS cluster nodes are hosted in
- `location` with the name of the region to create the resource in. It should be the same region as the AKS cluster nodes.
```azurecli-interactive
az storage account create --name myAKSStorageAccount --resource-group nodeResourceGroupName --location location --sku Standard_LRS
```
1. Export the connection string as an environment variable, which you use to create the file share, using the [`az storage account show-connection-string`][az-storage-account-show-connection-string] command. Make sure to replace `storageAccountName` and `resourceGroupName` with your storage account name and resource group name.
```azurecli-interactive
export AZURE_STORAGE_CONNECTION_STRING=$(az storage account show-connection-string --name storageAccountName --resource-group resourceGroupName -o tsv)
```
> [!NOTE]
> Connection strings must be protected using key rotation or storage in an Azure Key Vault. For more information about connection strings, see [Configure Azure Storage connection strings](/azure/storage/common/storage-configure-connection-string) and [Manage storage account access keys](/azure/storage/common/storage-account-keys-manage). For production environments, Microsoft recommends using Microsoft Entra ID authentication. For more information, see [Authorize access to data in Azure Storage](/azure/storage/blobs/authorize-access-azure-active-directory).
1. Create the file share using the [`az storage share create`][az-storage-share-create] command. Make sure to replace `shareName` with your share name.
```azurecli-interactive
az storage share create --name shareName --connection-string $AZURE_STORAGE_CONNECTION_STRING
```
1. Export the storage account key as an environment variable using the [`az storage account keys list`][az-storage-account-keys-list] command. Make sure to replace `storageAccountName` and `resourceGroupName` with your storage account name and resource group name.
```azurecli-interactive
STORAGE_KEY=$(az storage account keys list --resource-group nodeResourceGroupName --account-name myAKSStorageAccount --query "[0].value" -o tsv)
```
1. Echo the storage account name and key using the following command. Make note of the storage account key, which you use to create a Kubernetes secret in the next step.
```bash
echo Storage account key: $STORAGE_KEY
```
### Create a Kubernetes secret
Kubernetes needs credentials to access the file share created in the previous step. These credentials are stored in a [Kubernetes secret][kubernetes-secret], which is referenced when you create a Kubernetes pod.
- Create the secret using the `kubectl create secret` command. The following example creates a secret named _azure-secret_ and populates the _azurestorageaccountname_ and _azurestorageaccountkey_ from the previous step. To use an existing Azure storage account, provide the account name and key.
```bash
kubectl create secret generic azure-secret --from-literal=azurestorageaccountname=myAKSStorageAccount --from-literal=azurestorageaccountkey=$STORAGE_KEY
```
### Mount file share as a persistent volume
1. Create a new file named `azurefiles-pv.yaml` and copy in the following contents. Under `csi`, update `resourceGroup`, `volumeHandle`, and `shareName`. For mount options, the default value for `fileMode` and `dirMode` is _0777_.
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
annotations:
pv.kubernetes.io/provisioned-by: file.csi.azure.com
name: azurefile
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: azurefile-csi
csi:
driver: file.csi.azure.com
volumeHandle: "{resource-group-name}#{account-name}#{file-share-name}" # make sure this volumeid is unique for every identical share in the cluster
volumeAttributes:
shareName: aksshare
nodeStageSecretRef:
name: azure-secret
namespace: default
mountOptions:
# Canonical permissions: 0755/uid=1000/gid=1000 for least privilege.
# Use 0777/uid=0/gid=0 only if app requires root or broad write access.
- dir_mode=0755
- file_mode=0755
- uid=1000
- gid=1000
- mfsymlinks
- cache=strict
- nosharesock
- actimeo=30
- nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks
```
1. Create the PV using the [`kubectl create`][kubectl-create] command.
```bash
kubectl create -f azurefiles-pv.yaml
```
1. Create a new file named _azurefiles-mount-options-pvc.yaml_ and paste in the following contents. Make sure the `storageClassName` matches the name of your existing storage class, and the `volumeName` matches the name of the PV you created in the previous step.
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: azurefile
spec:
accessModes:
- ReadWriteMany
storageClassName: azurefile-csi
volumeName: azurefile
resources:
requests:
storage: 5Gi
```
1. Create the PVC using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f azurefiles-mount-options-pvc.yaml
```
1. Verify your PVC is created and bound to the PV using the [`kubectl get`][kubectl-get] command.
```bash
kubectl get pvc azurefile
```
Your output should resemble the following example output, which shows that the PVC is in a `Bound` state, and it's bound to the PV named _azurefile_:
```console
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
azurefile Bound azurefile 5Gi RWX azurefile 5s
```
1. Update your container spec to reference your PVC and your pod in the YAML file. For example:
```yaml
...
volumes:
- name: azure
persistentVolumeClaim:
claimName: azurefile
```
1. A pod spec can't be updated in place, so delete the pod using the [`kubectl delete`][kubectl-delete] command and recreate it using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl delete pod mypod
kubectl apply -f azure-files-pod.yaml
```
### Mount file share as an inline volume
> [!NOTE]
> To avoid performance issue, we recommend you use a persistent volume instead of an inline volume when numerous pods are accessing the same file share. Inline volume can only access secrets in the same namespace as the pod. To specify a different secret namespace, use a [persistent volume][persistent-volume].
To mount the Azure Files file share into your pod, you configure the volume in the container spec.
1. Create a new file named `azure-files-pod.yaml` and copy in the following contents. If you changed the name of the file share or secret name, update the `shareName` and `secretName`. You can also update the `mountPath`, which is the path where the Files share is mounted in the pod. For Windows Server containers, specify a `mountPath` using the Windows path convention, such as _'D:'_.
```yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
nodeSelector:
kubernetes.io/os: linux
containers:
- image: 'mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine'
name: mypod
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
volumeMounts:
- name: azure
mountPath: /mnt/azure
readOnly: false
volumes:
- name: azure
csi:
driver: file.csi.azure.com
volumeAttributes:
secretName: azure-secret # required
shareName: aksshare # required
mountOptions: 'dir_mode=0755,file_mode=0755,uid=1000,gid=1000,cache=strict,actimeo=30,nosharesock,nobrl' # optional
```
1. Create the pod using the [`kubectl apply`][kubectl-apply] command.
```bash
kubectl apply -f azure-files-pod.yaml
```
1. View the status of the pod using the [`kubectl describe`][kubectl-describe] command:
```bash
kubectl describe pod mypod
```
## Related content
- [Use Azure tags in Azure Kubernetes Service (AKS)][use-tags]
<!-- LINKS -->
[kubernetes-secret]: https://kubernetes.io/docs/concepts/configuration/secret/
[smb-overview]: /windows/desktop/FileIO/microsoft-smb-protocol-and-cifs-protocol-overview
[kubectl-apply]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply
[kubectl-get]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get
[kubectl-create]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#create
[data-plane-api]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
[kubectl-describe]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#describe
[kubectl-delete]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#delete
[access-modes]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
[azure-storage-account]: /azure/storage/common/storage-introduction
[install-azure-cli]: /cli/azure/install-azure-cli
[persistent-volume]: #mount-file-share-as-a-persistent-volume
[use-tags]: use-tags.md
[node-resource-group]: faq.yml
[storage-skus]: /azure/storage/common/storage-redundancy
[az-aks-show]: /cli/azure/aks#az-aks-show
[az-storage-share-create]: /cli/azure/storage/share#az-storage-share-create
[storage-tiers]: /azure/storage/files/storage-files-planning#storage-tiers
[access-tiers-overview]: /azure/storage/blobs/access-tiers-overview
[tag-resources]: /azure/azure-resource-manager/management/tag-resources
[azure-files-usage]: /azure/storage/files/understand-performance#choosing-a-performance-tier-based-on-usage-patterns
[az-storage-account-create]: /cli/azure/storage/account#az-storage-account-create
[storage-account-private-endpoint]: /azure/storage/common/storage-private-endpoints
[nfs-overview]:/windows-server/storage/nfs/nfs-overview
[persistent-volume-claim-overview]: concepts-storage.md#persistent-volume-claims
[azure-private-endpoint-dns]: /azure/private-link/private-endpoint-dns#azure-services-dns-zone-configuration
[nfs-file-share-mount-options]: /azure/storage/files/storage-files-how-to-mount-nfs-shares#mount-options