Amazon EKS — EBS CSI Driver
The Amazon EBS CSI Driver provisions and attaches EBS volumes to EKS pods. It replaces the legacy in-tree AWS cloud provider and supports gp3, io2, and other EBS volume types.
Installation as EKS Add-on
Step 1 — Create an IAM Role
The EBS CSI Driver needs permissions to create, attach, and delete EBS volumes.
Using eksctl:
eksctl create iamserviceaccount \
--cluster my-cluster \
--namespace kube-system \
--name ebs-csi-controller-sa \
--attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
--approve \
--role-name AmazonEKS_EBS_CSI_DriverRole
Or using Pod Identity:
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--namespace kube-system \
--service-account ebs-csi-controller-sa \
--role-arn arn:aws:iam::123456789:role/AmazonEKS_EBS_CSI_DriverRole
Step 2 — Install the Add-on
aws eks create-addon \
--cluster-name my-cluster \
--addon-name aws-ebs-csi-driver \
--service-account-role-arn arn:aws:iam::123456789:role/AmazonEKS_EBS_CSI_DriverRole
Verify Installation
kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver
kubectl get csidrivers
gp3 StorageClass
gp3 is the recommended default — better performance and lower cost than gp2:
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
gp3 vs gp2
| Feature | gp2 | gp3 |
|---|---|---|
| Baseline IOPS | 3 IOPS/GB (burst to 3000) | 3000 IOPS (always) |
| Baseline throughput | 128 MiB/s (250 GB+) | 125 MiB/s (always) |
| Max IOPS | 16,000 | 16,000 |
| Max throughput | 250 MiB/s | 1,000 MiB/s |
| Price (us-east-1) | $0.10/GB-month | $0.08/GB-month |
io2 for High-Performance Workloads
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"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
io2 delivers up to 256,000 IOPS — suitable for latency-sensitive databases.
Topology-Aware Provisioning
WaitForFirstConsumer ensures volumes are created in the same AZ as the pod:
volumeBindingMode: WaitForFirstConsumer # Recommended!
Why this matters:
- EBS volumes are AZ-bound (e.g., us-east-1a)
- If a volume is created in us-east-1a but the pod schedules to us-east-1b, the volume can’t attach
WaitForFirstConsumerdelays volume creation until a pod is scheduled, ensuring AZ alignment
Volume Expansion
Grow an existing PVC without recreating:
kubectl patch pvc data-postgres-0 -n production \
-p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'
Requirements:
- StorageClass must have
allowVolumeExpansion: true - EBS volumes can only be modified once every 6 hours
- Filesystem expansion happens automatically on the next mount
EBS Limitations on EKS
| Limitation | Impact |
|---|---|
| Single AZ | Volume bound to one AZ; pod must schedule in same AZ |
| Single attach | Only one node can mount the volume (ReadWriteOnce) |
| Modify cooldown | 6-hour cooldown between volume modifications |
| No Fargate | EBS volumes cannot be used with Fargate pods |
| Node instance limits | Each EC2 instance type has a max number of EBS volumes |
Terraform Example
module "ebs_csi_irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.0"
role_name = "ebs-csi-controller"
attach_ebs_csi_policy = true
oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"]
}
}
}
resource "aws_eks_addon" "ebs_csi" {
cluster_name = module.eks.cluster_name
addon_name = "aws-ebs-csi-driver"
addon_version = "v1.37.0-eksbuild.1"
service_account_role_arn = module.ebs_csi_irsa.iam_role_arn
resolve_conflicts_on_update = "OVERWRITE"
}
Best Practices
- Use gp3 as the default StorageClass — more IOPS and throughput at lower cost than gp2
- Always set
WaitForFirstConsumer— ensures AZ alignment between pod and volume - Enable encryption —
encrypted: "true"in StorageClass parameters - Set
allowVolumeExpansion: true— allows PVC resizing without recreation - Use
Retainreclaim policy for production databases — prevents accidental data loss - Monitor volume IOPS — CloudWatch metrics help identify throttled volumes