Amazon EKS — StorageClasses and Dynamic Provisioning

2 minute read

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 Retain for 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

  1. Make gp3 the default StorageClass — better and cheaper than gp2
  2. Always use WaitForFirstConsumer for EBS — prevents cross-AZ scheduling issues
  3. Enable encryption by default — set encrypted: "true" in all StorageClasses
  4. Use Retain for production data — prevents accidental deletion of critical volumes
  5. Define purpose-specific StorageClasses — databases get io2, logs get st1, apps get gp3
  6. Allow volume expansion — saves you from recreating PVCs when you need more space

Updated: