RAPID INCIDENT TRIAGE & ACTION PLAN•Performance
01. Error Signature
EBS CSI External Provisioner PVC Debugger
02. Root Cause Vector

EBS CSI external provisioner fails to bind a PVC, leaving pods stuck in Pending indefinitely due to misconfigured StorageClass, missing I...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Performance

EBS CSI External Provisioner PVC Debugger

EBS CSI external provisioner fails to bind a PVC, leaving pods stuck in Pending indefinitely due to misconfigured StorageClass, missing IAM permissions, or driver installation failure.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(66L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: legacy-pci-app-pvc
  namespace: pci-secure-zone
  annotations:
    # TODO: This annotation is a hack from the AWS migration, but the new CSI driver ignores it? Check with platform team.
    volume.beta.kubernetes.io/storage-class: "gp2"
    # Legacy annotation from the old in-tree provisioner, keep for rollback compatibility
    volume.beta.kubernetes.io/storage-provisioner: "kubernetes.io/aws-ebs"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  # VULNERABILITY: The storageClassName is set to a non-existent class, but the legacy annotation creates confusion, causing the external provisioner to ignore the claim silently.
  storageClassName: "ebs-csi-gp3-encrypted"
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp2
  annotations:
    # This is the old default class, but we're migrating to encrypted GP3. Don't delete yet.
    helm.sh/hook: "pre-install,pre-upgrade"
provisioner: ebs.csi.aws.com
parameters:
  type: gp2
  encrypted: "false"
  # TODO: We should enable encryption, but the KMS key ARN is missing due to compliance delays.
  # kmsKeyId: arn:aws:kms:us-east-1:123456789012:key/abcd1234-5678-90ef-ghij-klmnopqrstuv
reclaimPolicy: Retain
allowVolumeExpansion: true
---
# This is a messy ConfigMap with leftover secrets from a previous deployment script.
# It should have been moved to Vault, but the migration is pending.
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-bootstrap-config
  namespace: pci-secure-zone
data:
  AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE
  AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  STRIPE_API_KEY: sk_live_4eC39HqLyjWDarjtT1zdp7dc
  DATABASE_URL: "postgresql://app_user:SuperSecretPass123!@my-registry.internal:5432/legacy_pci_db?sslmode=require"
  # Old RSA key from a deprecated service, still referenced somewhere maybe?
  SSH_PRIVATE_KEY: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEArX8bBQnT8tBp6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7v
    L6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6v
    Kj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6
    pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7
    vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6
    vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj
    6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL
    7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6
    vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6
    pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7
    vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6
    vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6
    pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7v
    L6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj6pL7vL6vKj
    -----END RSA PRIVATE KEY-----
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix EBS CSI PVC Stuck in Pending: 'Waiting for a Volume to Be Created by External Provisioner'

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 10–30 mins depending on root cause

TL;DR

  • What broke: Your PVC is stuck in Pending because the EBS CSI external provisioner cannot fulfill the volume request — caused by a missing/misconfigured CSI driver, wrong provisioner field in the StorageClass, IAM role missing ec2:CreateVolume permissions, or an AZ topology mismatch.
  • How to fix it: Verify the ebs.csi.aws.com driver is running, confirm IRSA/node IAM permissions, and ensure your StorageClass references the correct provisioner and AZ topology constraints.
  • Shortcut: Use our Client-Side Sandbox above to auto-refactor this — paste your kubectl describe pvc output and StorageClass YAML and get a corrected manifest instantly.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

You run kubectl describe pvc <your-pvc> and see:

Events:
  Type     Reason                Age                From                         Message
  ----     ------                ----               ----                         -------
  Warning  ProvisioningFailed    2m (x12 over 10m)  persistentvolume-controller  waiting for a volume to be created, either by external provisioner "ebs.csi.aws.com" or manually created

Immediate consequence: Every pod with a volumeMount referencing this PVC is stuck in Pending. Deployments, StatefulSets, and Jobs are fully blocked. If this is a database StatefulSet (Postgres, MySQL, Kafka), your application is down. The control plane is not broken — the provisioner simply never received a valid, actionable request or lacked the authority to execute it.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This is a silent availability failure. The cluster appears healthy — nodes are Ready, the API server responds — but workloads never start. The blast radius depends on what was deploying:

  • StatefulSets: All replicas stuck. No leader elected. Quorum-based systems (etcd, Kafka, ZooKeeper) lose write availability entirely.
  • Rollouts: A deployment rollout stalls. The old ReplicaSet keeps serving, but if it was already scaled down or this is a fresh deploy, you have zero running pods.
  • Cascading HPA failure: HPA cannot scale what never started. Traffic spikes hit nothing.

The five root causes, in order of frequency:

  1. CSI driver not installed or pods are CrashLooping — ebs-csi-controller pods in kube-system are not Running.
  2. Wrong provisioner name in StorageClass — legacy kubernetes.io/aws-ebs instead of ebs.csi.aws.com.
  3. IRSA not configured / IAM policy missing — the CSI controller pod cannot call ec2:CreateVolume, ec2:DescribeVolumes, ec2:AttachVolume.
  4. Availability Zone topology mismatch — PVC requests a volume in us-east-1a but no schedulable node exists there.
  5. StorageClass does not exist or is named incorrectly in the PVC spec.

How to Fix It (The Solution)

Step 0 — Triage in 60 Seconds

# Is the CSI driver running?
kubectl get pods -n kube-system -l app=ebs-csi-controller

# What is the provisioner on your StorageClass?
kubectl get sc <your-storage-class> -o jsonpath='{.provisioner}'

# Full event log on the stuck PVC
kubectl describe pvc <pvc-name> -n <namespace>

# CSI controller logs
kubectl logs -n kube-system -l app=ebs-csi-controller -c csi-provisioner --tail=50

Fix 1 — Wrong Provisioner in StorageClass (Most Common)

 apiVersion: storage.k8s.io/v1
 kind: StorageClass
 metadata:
   name: gp3-ebs
- provisioner: kubernetes.io/aws-ebs
+ provisioner: ebs.csi.aws.com
 parameters:
-  type: gp2
+  type: gp3
+  encrypted: "true"
 reclaimPolicy: Retain
 volumeBindingMode: WaitForFirstConsumer
 allowVolumeExpansion: true

Critical: volumeBindingMode: WaitForFirstConsumer is mandatory for EBS. Without it, the provisioner tries to create the volume before knowing which AZ the pod will schedule into, causing topology failures.


Fix 2 — Missing IAM Permissions (IRSA)

The CSI controller pod must assume a role with at minimum:

 # IAM Policy attached to the CSI controller IRSA role
 {
   "Version": "2012-10-17",
   "Statement": [
     {
       "Effect": "Allow",
       "Action": [
-        "ec2:*"
+        "ec2:CreateVolume",
+        "ec2:DeleteVolume",
+        "ec2:AttachVolume",
+        "ec2:DetachVolume",
+        "ec2:DescribeVolumes",
+        "ec2:DescribeVolumeStatus",
+        "ec2:DescribeInstances",
+        "ec2:DescribeAvailabilityZones",
+        "ec2:CreateTags",
+        "ec2:ModifyVolume",
+        "ec2:DescribeVolumesModifications"
       ],
       "Resource": "*"
     }
   ]
 }

Annotate the CSI service account:

kubectl annotate serviceaccount ebs-csi-controller-sa \
  -n kube-system \
  eks.amazonaws.com/role-arn=arn:aws:iam::<ACCOUNT_ID>:role/AmazonEKS_EBS_CSI_DriverRole

Then restart the controller:

kubectl rollout restart deployment ebs-csi-controller -n kube-system

Fix 3 — Install the EBS CSI Driver (If Missing)

Enterprise Best Practice — EKS Add-on (not Helm):

 # Terraform
 resource "aws_eks_addon" "ebs_csi" {
   cluster_name             = aws_eks_cluster.main.name
   addon_name               = "aws-ebs-csi-driver"
-  # Not configured — driver missing entirely
+  addon_version            = "v1.30.0-eksbuild.1"
+  service_account_role_arn = aws_iam_role.ebs_csi_irsa.arn
+  resolve_conflicts_on_update = "OVERWRITE"
 }

Fix 4 — PVC Referencing Non-Existent StorageClass

 apiVersion: v1
 kind: PersistentVolumeClaim
 metadata:
   name: postgres-data
 spec:
   accessModes:
     - ReadWriteOnce
   resources:
     requests:
       storage: 20Gi
-  storageClassName: standard
+  storageClassName: gp3-ebs

💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

OPA/Gatekeeper Policy — Enforce Correct Provisioner

package k8s.storageclass

violation[{"msg": msg}] {
  input.review.object.kind == "StorageClass"
  input.review.object.provisioner != "ebs.csi.aws.com"
  msg := sprintf("StorageClass '%v' must use provisioner 'ebs.csi.aws.com', got '%v'",
    [input.review.object.metadata.name, input.review.object.provisioner])
}

violation[{"msg": msg}] {
  input.review.object.kind == "StorageClass"
  input.review.object.volumeBindingMode != "WaitForFirstConsumer"
  msg := "StorageClass must set volumeBindingMode: WaitForFirstConsumer for EBS"
}

Checkov in CI Pipeline

# .github/workflows/checkov.yml
- name: Scan Kubernetes manifests
  uses: bridgecrewio/checkov-action@master
  with:
    directory: k8s/
    framework: kubernetes
    check: CKV_K8S_28  # Ensure StorageClass uses approved provisioner
    soft_fail: false

Terraform Pre-Commit

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    hooks:
      - id: terraform_validate
      - id: terraform_tflint

Add to your tflint rules: assert aws_eks_addon for aws-ebs-csi-driver exists before any StatefulSet workload module is applied.

Smoke Test Post-Deploy

#!/bin/bash
# ebs-smoke-test.sh — run this in your CD pipeline after cluster provisioning
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-smoke-pvc
  namespace: default
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: gp3-ebs
  resources:
    requests:
      storage: 1Gi
EOF

sleep 30
STATUS=$(kubectl get pvc ebs-smoke-pvc -o jsonpath='{.status.phase}')
if [ "$STATUS" != "Bound" ]; then
  echo "FATAL: EBS CSI smoke test failed. PVC status: $STATUS"
  kubectl describe pvc ebs-smoke-pvc
  exit 1
fi
kubectl delete pvc ebs-smoke-pvc
echo "EBS CSI provisioner: OK"

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why is my EBS CSI PVC stuck in Pending even though the StorageClass exists?
The root cause is a misconfigured or broken provisioning chain. The CSI controller pods may be failing due to missing IAM permissions (ec2:CreateVolume), or the StorageClass is referencing the legacy in-tree provisioner 'kubernetes.io/aws-ebs' instead of the CSI driver 'ebs.csi.aws.com'. This is a foundational control-plane failure that prevents any volume creation. To build authority, you must manually check pod logs, IAM roles, and provisioner strings—a tedious but necessary diagnostic process to understand the exact break.
Q2.What does 'volumeBindingMode: WaitForFirstConsumer' do and why is it required for EBS?
It delays volume provisioning until a pod is scheduled, ensuring the EBS volume is created in the same Availability Zone as the pod. Using 'Immediate' mode is a critical misconfiguration that leads to unrecoverable attach failures because the volume and pod end up in different AZs. Manually fixing this across hundreds of manifests is highly error-prone and introduces a significant blast radius—a single missed manifest can cause production outages. Do not attempt manual edits. Instead, drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that enforces correct binding mode without human error.
Q3.How do I verify the EBS CSI driver has the correct IAM permissions on EKS?
You must verify the IAM role attached to the CSI controller's service account has the necessary permissions (e.g., ec2:CreateVolume, ec2:AttachVolume). This involves checking IRSA annotations, IAM policies, and cross-referencing logs for 'AccessDenied' errors. Doing this manually across large mono-repos or at scale is a compliance nightmare—it's impossible to audit consistently and creates drift between environments. For reliable, automated pull request generation with Maker-Checker review, upgrade to the StackEngine CI/CD Automator (Team Edition). It provides on-prem analysis and enforces policy-as-code to eliminate IAM misconfigurations across your entire infrastructure, ensuring compliance and security at scale.