The EBS CSI driver allows Kubernetes pods on EKS to dynamically provision and manage Amazon Elastic Block Store (EBS) volumes for persistent storage.

Here’s a practical look at how to get it set up and functioning:

First, let’s see it in action with a simple deployment that uses a persistent volume. We’ll create a Deployment with a single pod that writes a timestamp to a file in a persistent volume.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com
parameters:
  type: gp3 # Or gp2, io1, etc.
  fsType: ext4
reclaimPolicy: Delete # Or Retain
volumeBindingMode: Immediate # Or WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ebs-sc
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ebs-test-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ebs-test
  template:
    metadata:
      labels:
        app: ebs-test
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: ebs-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: ebs-storage
        persistentVolumeClaim:
          claimName: ebs-pvc

When you apply this YAML, the ebs.csi.aws.com provisioner in the StorageClass will automatically create an EBS volume in AWS and attach it to your EKS node. The PersistentVolumeClaim will then bind to this dynamically provisioned volume, and your pod will mount it.

The core problem the EBS CSI driver solves is bridging the gap between Kubernetes’ abstract PersistentVolume and PersistentVolumeClaim objects and the concrete, cloud-specific storage resources like EBS volumes. Before CSI, this was handled by in-tree volume plugins, which were tightly coupled to the Kubernetes codebase and harder to update independently. The Container Storage Interface (CSI) provides a standardized way for storage vendors to expose their systems to container orchestrators like Kubernetes without modifying the orchestrator’s core code.

Internally, the EBS CSI driver consists of two main components:

  1. Controller Plugin: This runs as a Deployment in your cluster (typically in the kube-system namespace). It handles volume lifecycle operations that don’t require direct access to a specific node, such as creating, attaching, detaching, and deleting volumes. It communicates with the AWS API to provision and manage EBS volumes.

  2. Node Plugin: This runs as a DaemonSet on each EKS node. It’s responsible for mounting and unmounting volumes on the node where the pod is scheduled. It interacts with the operating system’s block device management and file system utilities.

When a PersistentVolumeClaim is created requesting storage via the ebs.csi.aws.com provisioner, the Kubernetes control plane directs the request to the CSI controller plugin. The controller plugin then calls the AWS API to create an EBS volume. Once the volume is created, the controller plugin makes it available to the node where the pod will be scheduled. The CSI node plugin on that specific node is then responsible for attaching the EBS volume to the instance and mounting it into the pod’s filesystem path.

The StorageClass is your primary lever for controlling how EBS volumes are provisioned. Key parameters include:

  • provisioner: This must be ebs.csi.aws.com.
  • parameters:
    • type: This specifies the EBS volume type (e.g., gp3, gp2, io1, st1, sc1). gp3 is generally recommended for its balance of performance and cost.
    • fsType: The filesystem to format the volume with (e.g., ext4, xfs).
    • iops and throughput: For io1 and gp3 volumes, respectively, to control performance characteristics.
  • reclaimPolicy:
    • Delete: The EBS volume is deleted when the PersistentVolume (which is automatically created by the CSI driver when reclaimPolicy: Delete is set on the StorageClass) is deleted. This is the default and most common for dynamic provisioning.
    • Retain: The EBS volume is not deleted when the PersistentVolume is deleted. You’ll need to manually delete the EBS volume from AWS.
  • volumeBindingMode:
    • Immediate: Volumes are provisioned as soon as the PersistentVolumeClaim is created. This can lead to suboptimal placement if the node isn’t known yet.
    • WaitForFirstConsumer: Volumes are provisioned only when a pod that uses the PersistentVolumeClaim is scheduled. This allows the scheduler to consider topology constraints (like Availability Zones) when provisioning the volume, ensuring the volume is in the same AZ as the pod. This is generally preferred for EBS volumes.

To install the EBS CSI driver on EKS, you’ll typically use the AWS EKS add-on management system or apply the YAML manifests directly. The add-on is the recommended approach for EKS versions 1.21 and later.

Using kubectl:

# Enable the EBS CSI driver add-on (replace <cluster-name> and <region>)
aws eks update-cluster-config \
    --region <region> \
    --name <cluster-name> \
    --resources Addons=[{addonName=aws-ebs-csi-driver,resolveConflicts=OVERWRITE}]

# Verify the add-on status
aws eks describe-cluster \
    --region <region> \
    --name <cluster-name> | grep -A 5 "addonName.*aws-ebs-csi-driver"

Or, if managing manually (for older EKS versions or specific needs):

# Create the namespace if it doesn't exist
kubectl create namespace kube-system

# Apply the IAM policy to your worker nodes' IAM role
# (This is a crucial step for the driver to interact with AWS APIs)
# You can find the policy document in the AWS documentation for the EBS CSI driver.
# Example: Attach the AmazonEKS_EBS_CSI_DriverPolicy to your worker node IAM role.

# Apply the CSI driver manifests
kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=master"

The most surprising true thing about the EBS CSI driver is that volumeBindingMode: WaitForFirstConsumer is not just a convenience; it’s a fundamental requirement for ensuring your EBS volumes are provisioned in the correct AWS Availability Zone. If you use Immediate, the CSI controller might provision an EBS volume in an AZ where no pods are currently scheduled, leading to attachment failures when a pod is eventually scheduled in a different AZ and tries to use that volume. The driver relies on Kubernetes’ scheduler to provide topology information about the chosen node before it calls the AWS API to create the volume.

The next concept you’ll likely encounter is managing different EBS volume types and performance characteristics for various workloads, and understanding how io1, gp3, and gp2 map to different I/O and throughput needs.

Want structured learning?

Take the full Eks course →