Whether youโre building a high-performance cloud-native app or running data-heavy workloads in your own infrastructure, persistent storage is necessary. In Kubernetes, this means having storage that survives pod restarts, failures, and rescheduling eventsโand thatโs precisely what simplyblock brings to the table: blazing-fast, scalable, and software-defined storage with cloud economics. A hyper-converged storage solution, like simplyblock enables Kubernetes storage par excellence.
In this guide, weโll show you how easy it is to install simplyblock in a hyper-converged Kubernetes cluster, where compute and storage live side-by-side. Weโll nerd out on the details, from CSI drivers to erasure coding, and even touch on data migration from existing persistent volumes.
๐ฆ A Quick Primer on Persistent Storage in Kubernetes
Kubernetes wasnโt built with persistent storage in mind. Containers are designed to be ephemeral by defaultโonce a pod is gone, its local data is gone, too. This is where PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs) come in.
Initially, PVs and PVCs were built around built-in storage drivers as part of the Kubernetes core. Over time, though, this proved challenging to maintain. Storage vendors had release timelines apart from Kubernetes, and the number of new storage vendors that wanted to join in on Kubernetes was unmanageable with the in-tree approach.
Thatโs when the Kubernetes Container Storage Interface, or CSI for short, project was born. CSI is a driver interface for out-of-tree storage plugins, enabling Kubernetes to โremote-controlโ different storage systems and implementations through a standardized interface. Sidenote: Kubernetes isnโt the only system using CSI anymore.
Anyhow, a Persistent Volume (PV) is a piece of storage in the cluster provided statically by an admin or dynamically through a Storage Class (SC).
On the other hand, a Persistent Volume Claim (PVC) is a request of a specific application or user to provide and mount a piece of storage.
Being Kubernetes-first, simplyblock provides a fully compliant CSI driver that makes it dead simple to attach ultra-fast NVMe-backed volumes to your workloads.
Deploy simplyblockโs CSI driver on Kubernetes using Helm in minutes.
๐งฑ Simplyblock Deployment Models
Simplyblock supports three deployment models: disaggregated, hyper-converged, and a hybrid of the two former. In this blog post, we will focus on the hyper-converged model. However, it is crucial to quickly understand the pros and cons of each model.

Disaggregated Deployment
In the disaggregated model, storage and compute are deployed on separate nodes. This is ideal when you want to scale storage independently from your application workloads. This model enables maximal storage density, throughput, and resource isolation.
When to use:
- Centralized storage serving multiple clusters or services.
- Large-scale environments where compute and storage lifecycles are managed independently.
- Use cases needing maximum performance, high-density storage, or where CPU/storage scaling diverges.
Hyper-Converged Deployment
In the hyper-converged model, each node runs both compute and storage workloads. Your application containers and storage services live side-by-side on the same physical infrastructure. This model offers access to features such as node affinity, which enables ultra-low latency access to local disks.
When to use:
- Edge environments or cloud-native clusters with tight coupling of compute and storage.
- Cost-sensitive deployments that aim to maximize hardware utilization.
- Fast, ultra-low-latency I/O paths due to local disk access.
Hybrid Deployment
The hybrid model combines the best of both worlds. Some nodes act purely as storage backends, while others are fully hyper-converged. Hybrid setups are great for mixed-type workloads. It combines the super low latency or the virtually native local disk access with the safety of a disaggregated setup and can quickly and seamlessly fail over to other workers.
When to use:
- Gradual migration from legacy to hyper-converged setups.
- Multi-tier storage strategies.
- Infrastructure with heterogeneous node capabilities (e.g., some nodes with lots of SSDs, others CPU-heavy).
๐ง Node Sizing and Architecture
Simplyblock is flexible โ you can run it disaggregated (storage separate from compute) or hyper-converged. In this guide, weโre going hyper-converged, meaning:
- The same nodes that run your pods also contribute local storage to the cluster.
- Data is spread across multiple nodes using erasure coding, protecting against failures and maximizing efficiency.
Recommended Storage Node Specs
From the node sizing guide:
- vCPUs: At least 8 (16+ recommended for heavy workloads) ARM64 or x86_64
- Memory: Minimum 32 GB RAM
- Storage Devices: High-performance SSDs or NVMe
- Network: 10 Gbit/s or better, ideally with RDMA support for advanced deployments
๐ ๏ธ Installing Simplyblock (Hyper-Converged)
Installing the Control Plane
Before deploying simplyblock inside your Kubernetes cluster, you must first install the control plane. This runs outside of Kubernetes as a dedicated management node on a standalone server or VM and is the central authority for managing the simplyblock cluster.
Management nodes of the control plane have three primary functions:
- Cluster Manager: Maintains cluster membership, volume metadata, and handles orchestration.
- API Gateway: Used by the CLI, CSI driver, and other clients.
- Telemetry and Monitoring: Tracks the health and status of all cluster components.
The resource requirements for management nodes arenโt excessive and can easily be provided via VMs. However, the control plane should be highly available, hence, three nodes are the recommended minimum.
- 2 vCPUs
- 4 GiB RAM
- 10 GiB of free disk space
- x86_64 Linux (Debian or RHEL-based)
- Outbound internet access to fetch packages and images. If you need an air-gapped installation, see the documentation; itโs totally possible.
Installing Python and the Command-Line Interface
Before we begin, we need to install Python and pip, as well as the simplyblock command-line interface.
sudo yum -y install python3-pipsudo pip install sbcli --upgradeInstalling the first Management Node
After that, you can run the quick check. While it doesnโt catch all issues, it quickly explains if the system should be compatible.
curl -L https://sblk.xyz/prerequisites | bashIf everything is green, we can go ahead with the installation. If there are checks that fail, see the complete documentation on how to configure firewalls and other components.
sbcli cluster create --ifname=<IF\_NAME> --ha-type=haThe parameter value for IF_NAME must be the ethernet interface for the control plane. Storage and control plane nodes have to have access to it.
A successful installation finishes with the cluster id. You can write this down as we need it later.
[demo@demo ~]# sbcli cluster create --ifname=eth0 --ha-type=ha2025-02-26 12:37:06,097: INFO: Installing dependencies...2025-02-26 12:37:13,338: INFO: Installing dependencies > Done2025-02-26 12:37:13,358: INFO: Node IP: 192.168.10.12025-02-26 12:37:13,510: INFO: Configuring docker swarm...2025-02-26 12:37:14,199: INFO: Configuring docker swarm > Done2025-02-26 12:37:14,200: INFO: Adding new cluster objectFile moved to /usr/local/lib/python3.9/site-packages/simplyblock\_core/scripts/alerting/alert\_resources.yaml successfully.2025-02-26 12:37:14,269: INFO: Deploying swarm stack ...2025-02-26 12:38:52,601: INFO: Deploying swarm stack > Done2025-02-26 12:38:52,604: INFO: deploying swarm stack succeeded2025-02-26 12:38:52,605: INFO: Configuring DB...2025-02-26 12:39:06,003: INFO: Configuring DB > Done2025-02-26 12:39:06,106: INFO: Settings updated for existing indices.2025-02-26 12:39:06,147: INFO: Template created for future indices.2025-02-26 12:39:06,505: INFO: {"cluster_id": "7bef076c-82b7-46a5-9f30-8c938b30e655", "event": "OBJ_CREATED", "object_name": "Cluster", "message": "Cluster created 7bef076c-82b7-46a5-9f30-8c938b30e655", "caused_by": "cli"}2025-02-26 12:39:06,529: INFO: {"cluster_id": "7bef076c-82b7-46a5-9f30-8c938b30e655", "event": "OBJ_CREATED", "object_name": "MgmtNode", "message": "Management node added vm11", "caused_by": "cli"}2025-02-26 12:39:06,533: INFO: Done2025-02-26 12:39:06,535: INFO: New Cluster has been created2025-02-26 12:39:06,535: INFO: 7bef076c-82b7-46a5-9f30-8c938b30e6557bef076c-82b7-46a5-9f30-8c938b30e655Installing Secondary Management Nodes
The process is very similar on secondary management nodes. First, we install Python, pip, and the command-line interface. Then, we add the management node to the control plane cluster.
However, before that, we need the cluster secret to enable secondary nodes to join.
[root@vm11 ~]# sbcli cluster get-secret 7bef076c-82b7-46a5-9f30-8c938b30e655e8SQ1ElMm8Y9XIwyn8O0Now, we install the secondary node.
sudo yum -y install python3-pippip install sbcli --upgradesbcli mgmt add <CP_PRIMARY_IP> <CLUSTER_ID> <CLUSTER_SECRET> <IF_NAME>The values for CP_PRIMARY_IP, CLUSTER_ID, CLUSTER_SECRET, and IF_NAME must be given to join the requested control plane cluster.
[demo@demo ~]# sbcli mgmt add 192.168.10.1 7bef076c-82b7-46a5-9f30-8c938b30e655 e8SQ1ElMm8Y9XIwyn8O0 eth02025-02-26 12:40:17,815: INFO: Cluster found, NQN:nqn.2023-02.io.simplyblock:7bef076c-82b7-46a5-9f30-8c938b30e6552025-02-26 12:40:17,816: INFO: Installing dependencies...2025-02-26 12:40:25,606: INFO: Installing dependencies > Done2025-02-26 12:40:25,626: INFO: Node IP: 192.168.10.22025-02-26 12:40:26,802: INFO: Joining docker swarm...2025-02-26 12:40:27,719: INFO: Joining docker swarm > Done2025-02-26 12:40:32,726: INFO: Adding management node object2025-02-26 12:40:32,745: INFO: {"cluster_id": "7bef076c-82b7-46a5-9f30-8c938b30e655", "event": "OBJ_CREATED", "object_name": "MgmtNode", "message": "Management node added vm12", "caused_by": "cli"}2025-02-26 12:40:32,752: INFO: Done2025-02-26 12:40:32,755: INFO: Node joined the clustercdde125a-0bf3-4841-a6ef-a0b2f41b8245Installing the Storage Plane
Itโs finally time to get the storage cluster running ๐
Preparing the Helm Repository
First, we need to register the simplyblock helm chart repository. Obviously, you need to have helm installed. If you donโt, follow the helm installation before you keep going here.
helm repo add simplyblock https://install.simplyblock.io/helmhelm repo updateLoad the NVMe/TCP Driver
Now letโs make sure that the NVMe over TCP (NVMe/TCP) driver is loaded on the Kubernetes workers. The NVMe over TCP driver itself is part of the standard Linux kernel, but not loaded by default.
To quickly test the driver, letโs load it temporarily.
modprobe nvme-tcpThat should have loaded the NVMe/TCP driver itself, as well as the NVMe over Fabrics (NVMe-oF) driver. We can easily check that by asking for Linux kernel for all loaded driver modules.
lsmod | grep 'nvme_'This should result in an output very similar to the following:
[demo@demo ~]# lsmod | grep 'nvme_'nvme_tcp 57344 0nvme_keyring 16384 1 nvme_tcpnvme_fabrics 45056 1 nvme_tcpnvme_core 237568 3 nvme_tcp,nvme,nvme_fabricsnvme_auth 28672 1 nvme_coret10_pi 20480 2 sd_mod,nvme_coreThe important ones are nvme_tcp and nvme_fabrics. Make sure those two show up in the result. If they do, yay!
But since itโs only a temporary load, letโs make that change persistent and load them automatically at boot time.
If youโre on a Debian-based system (yes, Iโm a Debian fan), just add it to /etc/modules.
echo "nvme-tcp" | sudo tee -a /etc/modulesOn Red Hat-based systems, itโs just as simple, though.
echo "nvme-tcp" | sudo tee -a /etc/modules-load.d/nvme-tcp.confAfter the change, you should reboot the system and run the lsmod command again to see that the drivers are really loaded.
Deploy the Helm Chart
Before we can kick off the helm chart installation, we quickly need to collect a few values that are required to connect the storage plane with the cluster definition in the control plane. If youโve written some of them down during the control plane installation, just skip the step.
The first value we need is the cluster id. Since a single control plane can control multiple storage clusters, the correct cluster id needs to be used.
The easiest way is to ask on the management node for all available clusters.
[demo@demo ~]# sbcli cluster list+--------------------------------------+-----------------------------------------------------------------+---------+-------+------------+---------------+-----+--------+| UUID | NQN | ha_type | tls | mgmt nodes | storage nodes | Mod | Status |+--------------------------------------+-----------------------------------------------------------------+---------+-------+------------+---------------+-----+--------+| 7bef076c-82b7-46a5-9f30-8c938b30e655 | nqn.2023-02.io.simplyblock:7bef076c-82b7-46a5-9f30-8c938b30e655 | ha | False | 1 | 4 | 1x1 | active |+--------------------------------------+-----------------------------------------------------------------+---------+-------+------------+---------------+-----+--------+So, for our example, 7bef076c-82b7-46a5-9f30-8c938b30e655 is the cluster id weโre looking for.
Next up, we need the cluster secret to prove that we are allowed to join the storage plane. Again, this can be done using the command-line tooling on any node of the control plane.
[demo@demo ~]# sbcli cluster get-secret 7bef076c-82b7-46a5-9f30-8c938b30e655e8SQ1ElMm8Y9XIwyn8O0We see that e8SQ1ElMm8Y9XIwyn8O0 is our secret.
Last, we need the storage pool name created in the previous section.
[demo@demo ~]# sbcli storage-pool list+--------------------------------------+-------+----------+----------+---------------+-------+-------+--------+| UUID | Name | Capacity | Max size | LVol Max Size | LVols | QOS | Status |+--------------------------------------+-------+----------+----------+---------------+-------+-------+--------+| 4357ea15-cdd1-41e2-b8f6-dfce2c02f6ff | pool1 | 0 B | 0 B | 0 B | 0 | False | active |+--------------------------------------+-------+----------+----------+---------------+-------+-------+--------+The storage pool name is pool1. With that, we have all the necessary values.
Installing the storage cluster is now as simple as giving the necessary values to the helm chart during installation.
CLUSTER_UUID="7bef076c-82b7-46a5-9f30-8c938b30e655"CLUSTER_SECRET="e8SQ1ElMm8Y9XIwyn8O0"CNTR_ADDR="http://192.168.10.1/"POOL_NAME="pool1"helm install -n simplyblock-csi \ --create-namespace simplyblock-csi \ simplyblock-csi/spdk-csi \ --set csiConfig.simplybk.uuid=<CLUSTER_UUID> \ --set csiConfig.simplybk.ip=<CNTR_ADDR> \ --set csiSecret.simplybk.secret=<CLUSTER_SECRET> \ --set logicalVolume.pool_name=<POOL_NAME> \ --set storagenode.create=trueThis will start the installation process. Eventually, helm should tell you that the installation was successfully run. At this point, we can list the pods and see the storage cluster coming up.
demo@demo ~> helm install -n simplyblock-csi \ --create-namespace simplyblock-csi simplyblock-csi/spdk-csi \ --set csiConfig.simplybk.uuid=${CLUSTER_UUID} \ --set csiConfig.simplybk.ip=${CNTR_ADDR} \ --set csiSecret.simplybk.secret=${CLUSTER_SECRET} \ --set logicalVolume.pool_name=${POOL_NAME}NAME: simplyblock-csiLAST DEPLOYED: Wed Mar 5 15:06:02 2025NAMESPACE: simplyblock-csiSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:The Simplyblock SPDK Driver is getting deployed to your cluster.
To check CSI SPDK Driver pods status, please run:
kubectl --namespace=simplyblock-csi get pods --selector="release=simplyblock-csi" --watchdemo@demo ~> kubectl --namespace=simplyblock-csi get pods --selector="release=simplyblock-csi" --watchNAME READY STATUS RESTARTS AGEspdkcsi-controller-0 6/6 Running 0 30sspdkcsi-node-tzclt 2/2 Running 0 30sAnd thatโs it ๐
The CSI driver is automatically registered during the installation, and your storage nodes are part of a robust, high-performance cluster.
โก Performance & Networking Tips
To ensure a smooth ride, there are some things you should keep in mind when building your storage infrastructure.
- Avoid CPU pinning for storage threads unless youโre tuning for edge performance.
- Prefer NUMA-aware deployments for large systems.
- Use MTU 9000 (Jumbo Frames) if your network supports it โ this reduces packet overhead.
- For public cloud deployments, you might need to disable automatic detection of link-local addresses.
- Network canโt be fast enough, get what you can ๐
Check out the network considerations for more deep-dive tuning.
๐ Migrating Existing Persistent Volumes
If youโre not starting from scratch but have existing volumes you want to migrate, the simplyblock documentation lists different options. The easiest way is to mount old and new into the same pod while running a rsync or dd. The latter is required for raw block devices, while the former is for files.
If you have the chance to create snapshots or full backups that can be replayed, or you run a database backup tool such as pgbackrest, just start a new instance and have it replay the backup or snapshot onto the new volume.
Last, there is a heavily involved, slightly more dangerous, but almost no-downtime method. To describe how it works would go beyond the scope of the blog post. Maybe itโs a good idea for a later one. See the documentation for more details.
๐งช Ready to Dive In?
Simplyblock on Kubernetes is built for modern, demanding workloads โ fast, cloud-native, and designed to scale with you. By deploying it hyper-converged, you get maximum performance with minimal complexity.
Need more control? You can explore hybrid and disaggregated models later. For now, kick the tires on this hyper-converged setup and see the speed for yourself. And remember, you can add disaggregated nodes later, making it a hybrid setup for extended fault tolerance.
๐ Check out the full Kubernetes deployment docs for all the gory details.
Happy hacking!