Amazon EKS — StorageClasses and Dynamic Provisioning
StorageClasses define how volumes are provisioned on-demand. When a pod creates a PVC, Kubernetes automatically provisions the backing AWS storage.
Default StorageClass on EKS
EKS ships with a gp2 StorageClass, but gp3 is more cost-effective:
kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer
Replace with gp3:
# Remove default from gp2
kubectl patch storageclass gp2 \
-p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
# Create gp3 as default
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
parameters:
type: gp3
fsType: ext4
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: true
Custom StorageClasses
High-Performance (io2)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: io2-high-perf
provisioner: ebs.csi.aws.com
parameters:
type: io2
iopsPerGB: "50"
fsType: ext4
encrypted: "true"
kmsKeyId: arn:aws:kms:us-east-1:123456789:key/abc-def-123
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
Throughput-Optimized (st1)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: st1-throughput
provisioner: ebs.csi.aws.com
parameters:
type: st1
fsType: ext4
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
EFS Dynamic
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: efs-dynamic
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-0123456789abcdef0
directoryPerms: "700"
mountOptions:
- tls
Reclaim Policies
| Policy | Behavior | Use Case |
|---|---|---|
| Delete | PV and backing EBS/EFS deleted when PVC is deleted | Dev/test environments |
| Retain | PV and backing storage preserved after PVC deletion | Production databases |
Production rule: Use
Retainfor any data you can’t afford to lose. You’ll need to manually clean up retained volumes.
Volume Binding Modes
| Mode | When PV is Created | Use Case |
|---|---|---|
| Immediate | When PVC is created | Pre-provisioned storage needs |
| WaitForFirstConsumer | When a pod using the PVC is scheduled | EBS (AZ-aware provisioning) |
Always use WaitForFirstConsumer for EBS — prevents AZ mismatch:
volumeBindingMode: WaitForFirstConsumer
Parameters Reference
EBS CSI Parameters
| Parameter | Description | Example |
|---|---|---|
type |
EBS volume type | gp3, gp2, io2, st1, sc1 |
iopsPerGB |
Provisioned IOPS per GB (io2 only) | "50" |
iops |
Total provisioned IOPS | "10000" |
throughput |
Throughput in MiB/s (gp3 only) | "250" |
encrypted |
Enable encryption | "true" |
kmsKeyId |
CMK ARN for encryption | "arn:aws:kms:..." |
fsType |
Filesystem type | ext4, xfs |
EFS CSI Parameters
| Parameter | Description | Example |
|---|---|---|
provisioningMode |
Provisioning mode | efs-ap |
fileSystemId |
EFS file system ID | fs-0123456789abcdef0 |
directoryPerms |
Access point directory permissions | "700" |
gidRangeStart |
Starting GID for access points | "1000" |
gidRangeEnd |
Ending GID for access points | "2000" |
basePath |
Base directory in EFS | "/dynamic_provisioning" |
Terraform: StorageClass Creation
resource "kubernetes_storage_class_v1" "gp3" {
metadata {
name = "gp3"
annotations = {
"storageclass.kubernetes.io/is-default-class" = "true"
}
}
storage_provisioner = "ebs.csi.aws.com"
reclaim_policy = "Delete"
volume_binding_mode = "WaitForFirstConsumer"
allow_volume_expansion = true
parameters = {
type = "gp3"
fsType = "ext4"
encrypted = "true"
}
}
Best Practices
- Make gp3 the default StorageClass — better and cheaper than gp2
- Always use
WaitForFirstConsumerfor EBS — prevents cross-AZ scheduling issues - Enable encryption by default — set
encrypted: "true"in all StorageClasses - Use
Retainfor production data — prevents accidental deletion of critical volumes - Define purpose-specific StorageClasses — databases get io2, logs get st1, apps get gp3
- Allow volume expansion — saves you from recreating PVCs when you need more space