Skip to main content

Rob Pankow Rob Pankow

The Double-Replication Tax: Rethinking Storage Redundancy for Databases After the VMware Exit

Aug 5, 2026  |  8 min read

The Double-Replication Tax: Rethinking Storage Redundancy for Databases After the VMware Exit

A vSAN storage policy made redundancy an explicit, per-VM decision. Failures To Tolerate (FTT), stripe width, and erasure coding versus mirroring were dials a platform team turned deliberately, often lower for a VM whose application already replicated itself and higher for one that didn’t. That dial rarely survives the move to Kubernetes. Most clusters ship with a single, fixed StorageClass replication factor applied to every PVC regardless of what is running on top of it, and a database that already replicates at the application layer, Postgres with Patroni, MySQL with Galera, Cassandra with its own replication factor, quietly gets a second, redundant copy of every write it makes. Nobody chose that. It’s just what the default StorageClass does.

What the vSAN Policy Habit Actually Taught

vSAN’s Storage Policy-Based Management forced a conversation that Kubernetes StorageClasses often skip: how much redundancy does this specific workload need, given what it already provides for itself? A stateless web tier VM with no self-replication got FTT=1 or higher. A database VM running its own synchronous replication to a standby sometimes got a lower FTT, because the storage team knew the application layer already covered node failure. That per-VM tuning was tedious to administer, but it made the redundancy cost visible and adjustable.

On Kubernetes, that visibility mostly disappears. A cluster typically has one or two StorageClasses, backed by one storage system with one configured replication factor, and every workload inherits it whether or not the workload needs it. The why CSI alone isn’t enough for stateful Kubernetes problem of a flat, one-size-fits-all connection model shows up again here in the redundancy dimension: CSI gives you a PVC, not a policy conversation about whether this specific PVC’s data is already protected somewhere else.

Where the Double Replication Actually Comes From

Several widely deployed databases already replicate at the application layer, independent of whatever the storage underneath is doing:

  • PostgreSQL with Patroni streams every write to two or three standby replicas over the Postgres replication protocol, with automatic failover if the primary dies.
  • MySQL InnoDB Cluster and Galera use certification-based or group replication so every write commits across a quorum of nodes before it’s considered durable.
  • Cassandra and CockroachDB replicate at the data layer by design, Cassandra with a configurable replication factor per keyspace, CockroachDB with Raft consensus across a range’s replicas.
  • etcd, underneath Kubernetes’ own control plane, commits every write through Raft to a majority of cluster members before acknowledging it.

Put any of these on a Kubernetes storage backend that also triple-replicates by default, a common configuration for Ceph- or ODF-backed pools, and the data now exists nine times: three application-layer copies, each itself stored as three storage-layer copies. Every write pays for both layers: the database’s own replication traffic across the cluster network, then a second round of replication traffic inside the storage layer for each of those copies. That’s write amplification and network I/O that vSAN’s per-VM policy would have made an explicit trade-off; on Kubernetes it’s usually just the default nobody looked at.

Not sure how much of your storage redundancy budget is doing real work? Talk to us about mapping your database’s own replication model onto a per-volume storage policy instead of a fixed platform-wide default. Talk to a storage architect

The double-replication stack: app-layer database replication and storage-layer replication compounding without anyone choosing that

Mapping Database Replication to a Storage Redundancy Budget

The right storage replication factor depends entirely on what the database above it already guarantees. A single-instance database with no replication of its own still needs the storage layer to be the only line of defense. A database that already tolerates node loss on its own doesn’t need the storage layer to redo that work at full strength.

DatabaseApp-layer replication mechanismStorage-layer replication to addWhy
Single-instance PostgreSQL or MySQL (no standby)None2-3x (storage is the only redundancy layer)No application-layer copy exists; storage failure is data loss
PostgreSQL + Patroni, MySQL Galera/InnoDB ClusterSynchronous replication to 2-3 nodes1-2xThe application already tolerates a node failure; storage mainly needs to survive a single-disk failure inside a node
Cassandra (RF=3), CockroachDB, etcdQuorum-based replication across the cluster1x, snapshots for recoveryLosing one replica’s underlying volume is exactly the failure mode the application layer is designed to absorb
Any of the above, disaster-recovery tierCross-region app replication or noneMatch to RPO/RTO requirements, not a blanket defaultDR posture is a separate decision from local storage redundancy

Table: matching storage-layer replication to what the database already provides, instead of applying one platform-wide default.

This isn’t an argument for turning off storage redundancy on databases that don’t have it. A single-instance database with no application-layer replication still needs the storage layer to carry that risk. It’s an argument for treating the replication factor as a per-workload decision again, the way vSAN forced it to be, rather than a fixed property of whichever StorageClass happens to be the cluster default.

Making the Dial Real: Per-Volume Replication on NVMe/TCP

Turning that table into an actual configuration requires two things a lot of Kubernetes storage layers don’t give you: a replication factor that can be set per volume instead of per cluster, and a fabric fast and consistent enough that a lower replication factor doesn’t introduce a new latency problem to replace the one you just removed.

Simplyblock’s NVMe/TCP storage exposes replication factor as a StorageClass parameter, so a platform team can publish one tier for databases that already self-replicate (lower storage RF, tuned for write throughput) and a separate tier for everything else (full RF, the safe default), the same way a vSAN admin published separate policies for different workload classes. Because the fabric delivers consistent, low p99 write latency rather than the variable performance of local disks stitched together over a slower network, a database running at storage RF=1 still gets fast, predictable commits, the storage layer isn’t quietly relying on extra replicas to paper over an inconsistent fabric.

That per-volume tuning also changes the economics directly. Simplyblock licenses per usable TB provisioned per year, not per core and not a flat per-node fee, so a lower replication factor on a self-replicating database’s volumes is a visible, immediate reduction in provisioned capacity and license cost, not just a latency argument. A platform team right-sizing redundancy the way vSAN encouraged sees that saving on the bill, the same way over-provisioning a vSAN policy showed up as wasted capacity on the datastore.

Snapshots Still Matter, Regardless of the Replication Dial

Dialing down storage-layer replication for a self-replicating database doesn’t remove the need for point-in-time recovery. Replication protects against a node or disk failure; it does nothing for a bad migration, a dropped table, or an application bug that corrupts data consistently across every replica. That’s what CSI snapshots are for, and they work the same way regardless of which replication factor a volume is configured with: a fast, storage-side complement to the database’s own backup tooling (pg_basebackup, mysqldump, nodetool snapshot) rather than a substitute for either replication or backup. Getting the redundancy budget right and keeping snapshot-based recovery in place are two separate decisions, and the double-replication tax conversation is really about not conflating them.

Questions and Answers

What is the “double-replication tax” in Kubernetes storage? It’s the extra write amplification, network I/O, and provisioned capacity that results from running a database with its own application-layer replication (Postgres/Patroni, MySQL Galera, Cassandra) on top of a Kubernetes storage layer that also applies its own fixed replication factor to every volume by default. The data ends up replicated twice, once by the application and once by the storage system, without anyone deciding that trade-off deliberately.

Did vSAN actually solve this problem? Not automatically, but it made the trade-off visible. vSAN’s Storage Policy-Based Management required an explicit FTT (Failures To Tolerate) and stripe-width choice per VM, so a platform team could, and often did, choose a lower redundancy policy for a VM whose application already replicated itself. Most Kubernetes clusters skip that conversation and apply one StorageClass replication factor to everything.

Which databases are the best candidates for a lower storage replication factor? Databases with strong, quorum-based application-layer replication: Cassandra (configurable replication factor per keyspace), CockroachDB and etcd (Raft consensus across replicas), and clustered MySQL (Galera or InnoDB Cluster with group replication). A single-instance database with no standby or replica is the wrong candidate; for that workload, storage replication is the only redundancy layer that exists.

Does lowering storage replication factor remove the need for backups or snapshots? No. Replication protects against a node or disk failure. It does not protect against a bad migration, a dropped table, or an application bug that replicates the same corruption to every copy. CSI snapshots and the database’s own backup tooling remain necessary regardless of the replication factor chosen at the storage layer.

Can simplyblock set a different replication factor per database on the same cluster? Yes. Replication factor is a StorageClass parameter, so a platform team can publish one StorageClass tuned for workloads with their own application-layer replication and a separate, fully-replicated StorageClass for everything else, all backed by the same underlying NVMe/TCP pool. That mirrors how a vSAN admin assigned different storage policies to different VMs from one datastore.

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.