Skip to main content

Rob Pankow Rob Pankow

Why CSI Alone Isn't Enough for Stateful Kubernetes

Jun 12, 2026  |  9 min read

Last edited: Jun 16, 2026

Why CSI Alone Isn't Enough for Stateful Kubernetes

The Container Storage Interface (CSI) was one of the best things to happen to storage on Kubernetes. It gave every storage vendor a single, standard way to plug devices into the cluster, so a StorageClass and a PersistentVolumeClaim behave the same whether the volume lives on a cloud disk, a SAN, or a software-defined pool.

But CSI is a connection specification with a basic set of volume lifecycle features, not a data platform. To expose anything past the basics, vendors ship their own plugins that extend the spec, so capabilities like snapshots and backup vary from one driver to the next and you inherit a complex support matrix. More importantly, the way CSI connects storage to pods is built for simple cases. It is fine for small clusters and pinned workloads, and it quietly strains under the operations that define a real production platform. Put plainly: CSI tells you a volume will be there. It does not tell you the volume will stay fast, resilient, or cheap to operate at scale.

How CSI Actually Connects Storage to Pods

A CSI driver exposes block volumes from external storage directly to worker nodes, and it does so in a one-to-one relationship. Every time a pod makes a storage claim, the driver sets up a dedicated connection for it, and each connection is established sequentially.

Bringing a volume up on a new host, or moving it, is a four-step cycle. Two steps stand up the new connection, and two tear down the old one:

  1. Map: an API call to the array creates access for the new host and maps the volume.
  2. Attach: the host discovers the mapped device through bus scans and builds the multipath device.
  3. Detach: multipath devices and block devices are torn down on the original host.
  4. Unmap: another array call removes access from the original host.

For one volume on a quiet cluster, this is invisible. The problem is arithmetic: a single rolling upgrade can multiply that cycle across thousands of volumes, and each step is an array event. The control plane queues those events serially, so platform operations slow down exactly when you need them to be fast.

Where the 1:1 Connection Model Breaks at Scale

The connection overhead turns into real pain during the operations platform teams run constantly:

  • Maintenance windows and rolling upgrades. Draining and re-homing volumes node by node generates a flood of map, attach, detach, and unmap calls that the array has to absorb.
  • Failure recovery. Restoring service means re-mapping and re-attaching volumes on healthy hosts, and that cycle sits directly in your recovery time.
  • Boot storms and bursty traffic. A sudden wave of new pods, holiday e-commerce load or a fleet of LLM inference endpoints spinning up, means a new storage connection per pod. The host has to discover and mount each one individually, flooding the array with API requests.
  • Live migrations. Moving running workloads between nodes triggers the full teardown-and-rebuild cycle for every affected volume.
  • Hard host connection limits. A host can sustain only so many storage connections. Run out, and the only way to add more pods is to add more hosts purely for their connection budget, which wastes compute you did not need.

None of this is a CSI bug. The spec was designed for portable, standardized volume lifecycle, not for absorbing connection pressure across hundreds of thousands of volumes. Closing that gap is an architecture decision.

Diagram contrasting the 1:1 CSI connection model and its scaling failure points with a pooled, disaggregated NVMe fabric
Figure 1: A 1:1 connection per pod saturates arrays at scale; a pooled NVMe fabric lets pods claim from a shared layer instead.

What It Takes to Move Past the Bottleneck

The fix is to stop tying each pod to its own array mapping. Instead of one-to-one connections, you want a storage layer that aggregates devices into pools and lets pods claim capacity from the pool, so the layer absorbs the connection pressure that individual CSI drivers cannot. With data and its replicas distributed across nodes, a node failure means a volume is simply served from the nearest replica rather than re-mapped from scratch, which collapses recovery time. That same layer is where the data services CSI never standardized, thin provisioning, snapshots, encryption, and disaster recovery, naturally belong.

There is a catch worth naming. The most common way to build this pooled layer is to run it on the worker nodes themselves, as pods that replicate data and run data services using the same CPU, memory, and network as your applications. That solves the connection problem but trades it for a different tax:

  • Worker-node overhead. Replication and data services compete with your workloads on every node. You pay for storage twice: once in disks, once in stolen compute.
  • Write amplification. In-cluster replication writes every block two or three times across the cluster network, raising the latency floor for write-heavy databases.
  • Coupled scaling. When storage lives inside the compute cluster, you cannot scale capacity and compute independently.

The three ways teams answer this gap line up cleanly across the dimensions that actually decide a production platform:

DimensionStandard CSI (1:1 connections)In-cluster pooled layerDisaggregated NVMe (simplyblock)
Connection modelOne array mapping per pod, set up and torn down sequentiallyPods claim from a pool that runs on the worker nodesPods claim logical volumes from a shared NVMe fabric
Behavior at scaleArray API saturation on upgrades, boot storms, and migrationsAbsorbs the pressure, but on worker-node CPU and RAMAbsorbs the pressure off the worker nodes
Node-failure recoveryRe-map and re-attach cycle sits in the recovery pathRestart near a replica on another nodeVolume stays served from the pool; placement is distributed
Data servicesVary by driver; mostly out of scope for the specBundled, but running on application nodesThin provisioning, snapshots, encryption, and QoS in the storage layer

Table 1: How the three storage approaches handle connection scaling, recovery, and data services.

Scaling stateful Kubernetes and watching upgrades or failover drag because of storage connection overhead? Talk to us before you trade a 1:1 bottleneck for a worker-node tax that is hard to unwind later. Talk to a storage architect

So the real choice is not “minimal CSI” versus “feature-rich overlay.” It is whether the pooled layer lives on your application nodes or off them.

How simplyblock Solves It Without the Overlay Tax

Simplyblock keeps the CSI-native developer experience, then supplies the pooled fabric from a disaggregated storage layer instead of from the worker nodes.

The architecture is the point. Simplyblock pools NVMe devices into a shared, software-defined layer and serves logical volumes over NVMe/TCP (and NVMe/RoCE where the fabric supports it). Pods consume volumes through the standard CSI driver, but each pod claims a logical volume from the pool rather than triggering a fresh one-to-one array mapping. That removes the map, attach, detach, and unmap storm during upgrades, migrations, and recovery, and it sidesteps per-host connection limits, because the connection terminates at the simplyblock data plane, not at a finite array port budget.

Because data placement and replication are distributed across the storage layer, a lost node does not start a re-mapping cycle. The volume stays reachable from another copy, so recovery is fast and predictable. And because the layer is disaggregated rather than co-resident with your pods, you get the pooled behavior without the worker-node penalties:

  • Connection pressure absorbed off your application nodes, so upgrades and boot storms do not saturate an array or burn host connection slots.
  • Predictable, low latency with per-volume QoS, so a noisy neighbor cannot starve a latency-sensitive PostgreSQL primary. NVMe-first design keeps the latency floor low for write-heavy workloads.
  • Cluster-wide thin provisioning, snapshots, clones, and encryption as platform features rather than per-array integration projects.
  • Independent scaling of compute and storage, because storage is not stealing CPU and RAM from your workloads.

The result is the developer experience CSI promised, the connection-scaling behavior CSI never aimed for, and the data services it left out, without paying for them in worker-node compute or amplified writes. If you are choosing a storage standard for stateful workloads, see simplyblock for Kubernetes storage and the broader comparison of the best Kubernetes storage in 2026.

Questions and Answers

Why does standard CSI struggle at scale?

CSI connects storage to pods in a one-to-one relationship, and each connection is set up and torn down through a sequential map, attach, detach, and unmap cycle. For a few volumes this is invisible, but a single rolling upgrade can multiply that cycle across thousands of volumes, and the control plane queues the resulting array events serially. The overhead shows up as slow maintenance windows, slow recovery, and API saturation on the storage array, plus hard per-host connection limits that force you to add hosts just to gain more connections.

What happens to CSI storage connections during node failure or live migration?

Both trigger the full teardown-and-rebuild cycle. A failed or drained node requires re-mapping and re-attaching every affected volume on a healthy host, and that cycle sits directly inside your recovery time. Failover with plain CSI also tends to depend on custom scripting. A pooled storage layer avoids this by keeping replicas distributed across nodes, so a volume is served from the nearest copy instead of re-mapped from scratch.

How does a pooled storage layer avoid the connection bottleneck?

Instead of mapping each pod one-to-one to an array volume, a pooled layer aggregates storage devices into shared pools and lets pods claim capacity from the pool. The layer absorbs the connection pressure that individual CSI drivers cannot, parallelizes orchestration instead of queueing it serially, and keeps volumes immediately available through a software-defined fabric. That removes array API saturation during upgrades, boot storms, and migrations.

How does simplyblock go beyond CSI without taxing worker nodes?

Simplyblock stays CSI-native and supplies the pooled fabric from a disaggregated NVMe layer served over NVMe/TCP and NVMe/RoCE. Each pod claims a logical volume from the pool rather than a fresh array mapping, so the map, attach, detach, and unmap storm disappears and per-host connection limits stop being a ceiling. Replication, thin provisioning, snapshots, clones, encryption, and per-volume QoS run in the storage layer instead of on your worker nodes, so compute and storage scale independently and application nodes are not taxed by the data path.

You may also like:

NVMe/TCP vs NVMe/RoCE for Kubernetes Storage: Choosing the Right Fabric
NVMe/TCP vs NVMe/RoCE for Kubernetes Storage: Choosing the Right Fabric

NVMe over Fabrics gives Kubernetes clusters low-latency block storage over the network. The transport you pick, TCP or RoCE, determines your latency floor, infrastructure cost, and operational complexity. Here is how to choose.

We Break Our Storage So You Never Have To
We Break Our Storage So You Never Have To

Simplyblock runs 100+ hours of automated chaos engineering before every release: real NVMe hardware, real FIO workloads, four failure types injected under live load. This is what we test, why it is necessary, and what it means for your infrastructure.

NVMe Storage Cost Optimization in 2026: Erasure Coding, Thin Provisioning, and Compute Efficiency
NVMe Storage Cost Optimization in 2026: Erasure Coding, Thin Provisioning, and Compute Efficiency

NVMe drives deliver the performance Kubernetes stateful workloads need, but triple replication and thick provisioning multiply their cost fast. Here is a practical breakdown of erasure coding economics, thin provisioning, and how sub-millisecond latency reduces compute waste.