Dynamic Provisioning (Enterprise)
Dynamic provisioning allows Kubernetes to automatically create a new flexFS volume when a PersistentVolumeClaim is submitted. The CSI controller calls the admin server REST API to create the volume, retrieves a volume token, and makes it available to pods.
Prerequisites
Section titled “Prerequisites”- The flexFS CSI driver is installed
- An Enterprise admin server is running and reachable from the cluster
- You have an account token with volume creation permissions
Step 1: Create a Secret
Section titled “Step 1: Create a Secret”The CSI driver needs the admin server address and an account token to create volumes. Store these in a Kubernetes Secret:
apiVersion: v1kind: Secretmetadata: name: flexfs-secret namespace: defaultstringData: adminAddr: <ADMIN-ADDR> token: <ACCOUNT-TOKEN>Replace <ADMIN-ADDR> with the address of your admin server (for example, admin.example.com:443) and <ACCOUNT-TOKEN> with a valid account token.
kubectl apply -f secret.yamlStep 2: Create a StorageClass
Section titled “Step 2: Create a StorageClass”The StorageClass tells Kubernetes to use the flexFS CSI provisioner and specifies volume creation parameters:
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: flexfs-dynamicprovisioner: csi.flexfs.ioallowVolumeExpansion: trueparameters: csi.storage.k8s.io/provisioner-secret-namespace: default csi.storage.k8s.io/provisioner-secret-name: flexfs-secret csi.storage.k8s.io/node-publish-secret-namespace: default csi.storage.k8s.io/node-publish-secret-name: flexfs-secret csi.storage.k8s.io/controller-expand-secret-namespace: default csi.storage.k8s.io/controller-expand-secret-name: flexfs-secret provider: aws region: us-east-1provider and region are not optional decoration: the admin server needs them (or an explicit metaStore + blockStore) to place the volume, and rejects a request carrying neither with a 400. See StorageClass parameters for the full list.
You can add optional parameters to control volume settings at creation time. See the StorageClass parameters reference for all options.
Example with custom parameters
Section titled “Example with custom parameters”apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: flexfs-encryptedprovisioner: csi.flexfs.ioallowVolumeExpansion: trueparameters: csi.storage.k8s.io/provisioner-secret-namespace: default csi.storage.k8s.io/provisioner-secret-name: flexfs-secret csi.storage.k8s.io/node-publish-secret-namespace: default csi.storage.k8s.io/node-publish-secret-name: flexfs-secret csi.storage.k8s.io/controller-expand-secret-namespace: default csi.storage.k8s.io/controller-expand-secret-name: flexfs-secret provider: aws region: us-east-1 blockSize: 4Mi compression: "true" compressionAlgo: lz4 encryption: "true" maxInodes: "1000000"Step 3: Create a PersistentVolumeClaim
Section titled “Step 3: Create a PersistentVolumeClaim”apiVersion: v1kind: PersistentVolumeClaimmetadata: name: flexfs-dynamic namespace: defaultspec: storageClassName: flexfs-dynamic accessModes: - ReadWriteMany resources: requests: storage: 100Gikubectl apply -f dynamic-volume.yamlOnce the PVC is created, the CSI provisioner will:
- Call
CreateVolumeon the flexFS CSI controller - The controller sends a
POST /v1/volumesrequest to the admin server to create a new volume - A PersistentVolume is automatically created and bound to the PVC
Step 4: Use the volume in a pod
Section titled “Step 4: Use the volume in a pod”apiVersion: v1kind: Podmetadata: name: my-appspec: containers: - name: app image: ubuntu:latest command: ["sleep", "infinity"] volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: flexfs-dynamicVolume lifecycle
Section titled “Volume lifecycle”One volume per claim. The provisioner names each volume pvc-<PVC UID>, so every claim gets its own flexFS volume. Volumes are never reused across claims: delete a PVC and create another with the same name and you get a new UID, and therefore a new, empty volume. Binding to an existing volume is what static provisioning is for.
Retries adopt rather than duplicate. If provisioning is retried after a lost response, the admin server returns the volume it already created for that name instead of making a second one. If a volume of that name exists but has a smaller quota than the claim asks for, the driver reports ALREADY_EXISTS rather than binding the claim to something too small.
Deletion retires; cleanup follows. When the claim is deleted and the reclaim policy is Delete, the driver calls DELETE /v1/volumes/for-name/<volume-name>, which retires the volume: it is marked retired and its volume tokens are removed, but its data is still there. Block data is removed later, in a separate cleanup phase performed by the metadata server and bounded by the volume’s retention. So data outlives the claim for the retention window — which is also what makes point-in-time mounts of a deleted volume possible while it lasts. Deleting a claim whose volume is already gone succeeds rather than failing.
With reclaimPolicy: Retain, the driver is never asked to delete anything: the volume, its tokens, and its data stay until someone retires them with configure.flexfs delete volume.
A retired name is taken until cleanup finishes. Creating a volume whose name still belongs to a retired one is refused with a conflict rather than handing the retired volume back, since its data is queued for removal and would be emptied underneath the new claim. Dynamically provisioned names are derived from the claim’s UID, so this only arises when creating volumes with explicit names — wait for the retention period to elapse, or choose another name.
Storage quotas
Section titled “Storage quotas”The claim’s resources.requests.storage is enforced on the created volume, not merely recorded (if the claim also sets resources.limits.storage, the limit is what the quota is set to, since that is the ceiling the claim says must not be exceeded):
- It is rounded up to a whole block, so a 1 GiB claim on a 4 MiB block size becomes 256 blocks.
dfinside the pod reports the quota, and writing past it fails with ENOSPC.- The limit counts stored blocks, after compression. Compressible data therefore fits more logical bytes than the claim asked for, and the quota is a cap on what the volume consumes rather than a promise about file sizes.
- Because the limit is checked when blocks are recorded rather than when a write is buffered, a burst already in flight can overshoot slightly before subsequent writes start failing.
maxInodeson the StorageClass caps inodes; it is unlimited unless set, since a claim expresses only bytes.
Growing a volume. Set allowVolumeExpansion: true on the StorageClass and edit the claim’s request. The csi-resizer sidecar calls the driver, which raises the volume’s quota. The new limit is recorded at once, but a mount that is already running picks it up on its next volume-settings refresh — about a minute in practice — so for up to that long an already-full volume keeps returning ENOSPC and df keeps showing the old size. No remount or pod restart is needed; it simply is not instant.
Expansion only ever raises a quota. Kubernetes refuses to shrink a claim below its recorded capacity, so a dynamically provisioned volume’s limit is whatever its claim last asked for.
One request is refused: expanding a volume that has no quota. A volume with no limit already permits more than any request, so there is nothing to raise, and recording the unlimited sentinel — exabytes — as the claim’s capacity would be worse than an error. Any volume whose max_blocks is 0 is in this state, including one bound through a static PV. Set a limit deliberately with configure.flexfs update volume --maxBlocks/--maxInodes if you want one.
A pre-existing volume bound through a static PV is not affected by any of this: its PV capacity is informational, and quotas are set with configure.flexfs update volume --maxBlocks/--maxInodes.
Access modes
Section titled “Access modes”FlexFS supports the following CSI access modes:
| Access mode | Supported | Description |
|---|---|---|
ReadWriteMany | Yes | Multiple pods can read and write simultaneously |
ReadWriteOnce | Yes | Single pod read-write access |
ReadOnlyMany | Yes | Multiple pods with read-only access |
Next steps
Section titled “Next steps”- StorageClass parameters reference
- Static provisioning for pre-existing volumes
- Troubleshooting