Spindle

Open-source distributed block storage · MIT

Every spindle, working for every client.

Spindle turns the hard drives already in your servers into replicated block volumes. A client knows where every chunk lives and talks straight to the drive that holds it, so throughput adds up across every disk and node. It is one small Rust binary, built to share its hosts with the workloads it serves.

# on any client host
$ sudo spindle attach pgdata
/dev/spindle/pgdata
$ sudo mkfs.xfs /dev/spindle/pgdata
Spindle data path managers (Raft) table, health, leases client: /dev/spindle/vol ublk + io_uring, table in RAM table updates only server, node 1 server, node 2 server, node 3 direct I/O, one hop
No metadata server on the I/O path. Managers hand out the table; data goes straight from client to drive.
0metadata lookups per I/O
1static binary: manager, server, client, CSI
2copies; a write is acknowledged only when both land
2 + 1two storage nodes and a storageless witness is a valid cluster

Why Spindle

Most open-source block storage is designed around flash, or around being everything at once. Spindle is designed around one job: make a rack of hard drives fast, safe block storage without taking the rack's CPU and memory away from the workloads on it.

Built for hard drives

On HDD clusters the limits are seeks and the network, not CPU. Spindle spends its design budget there: reads go to either copy to double random-read IOPS, queues stay deep so drives can reorder, a data I/O never adds a metadata I/O, and unwritten space reads back as a flag instead of zeros on the wire.

Client-direct data path

The client computes where every chunk lives from a table it already holds, the model ScaleIO proved on spinning disks. No lookup, no gateway, no extra hop. Each request carries one 32-40 byte header; payloads land straight in their final buffer.

Hyper-converged by design

Storage that runs beside your VMs and pods has to stay out of their way. Spindle is Rust with no async runtime: its event loop is written directly on io_uring. A placement row costs 16 bytes of memory. Raft runs on the control plane only, so the consensus log never sees data I/O.

Data safety first

Losing data is never an acceptable trade for speed. Writes acknowledge only after every copy succeeds, checksums are on by default, a write-intent log keeps copies identical across a whole-rack power cut, and a failed drive's missed chunks are recorded before the error is returned. The details.

systemd, Kubernetes, or both

The same binary runs as systemd units on bare metal (tarball, install.sh or .deb) or from a Helm chart with a CSI driver, managers as a StatefulSet and servers and clients as DaemonSets. Mix them: Kubernetes nodes and plain Linux hosts can be clients of one cluster.

Small clusters welcome

You do not need five nodes to start. Two storage nodes plus a storageless witness, a cloud VM or a small box running only the manager, keep quorum without split brain. Each node picks which network carries client, replication and control traffic.

How it works

  1. Managers (three, or two plus a witness) run Raft and own the placement table, drive health, leases and fencing. They are never asked anything on the data path.
  2. Servers serve a node's drives. Each volume is cut into chunks spread over every drive in the pool, with copies in different failure domains, so a single volume can use all the spindles in the cluster.
  3. Clients expose volumes as kernel block devices through ublk, the fastest path from user space to a block device. The client holds the table and sends each request to the drive that owns the chunk.
  4. An SSD read cache on a node can absorb random reads; streaming reads skip it because the HDDs already stream faster than the cache.

The full design explains each choice, the performance rule behind it, and the measurement that decided it.

Performance rules, in order

  • 1No metadata on the I/O path. Placement is computed, not looked up.
  • 2Fewest bytes on the wire. Writes leave the client once; no separate acks or heartbeats on busy links.
  • 3Fewest copies in memory. Receive into the final buffer, send from where data already is.
  • 4Build it when it is faster. The custom protocol replaced iSCSI, NBD and NVMe/TCP because none of them carry placement, row versions or queue depth.

Data safety, then availability

The project's first rule is that data loss is unacceptable; its second is that data stays available. Every mechanism below is built and covered by tests or a recorded lab run.

No silent partial writes

A write is acknowledged only when every copy succeeded. When a drive fails mid-write, the chunks it may have missed are fsynced to a record before the error reply is sent, and reads of those chunks go to the good copy.

Power-cut consistency

Designed for a rack that loses power with no UPS. Flushed data survives, and a write-intent log makes every copy of an in-flight chunk converge on the same contents, so a filesystem replaying its journal never sees reads flip between copies.

Restart without unmounting

Client devices use ublk user recovery: the kernel keeps the device and holds its I/O while the daemon restarts. In a lab run, clientd was SIGKILLed during fio write+verify and restarted 5 s later; both runs finished with 0 errors.

Hung and dead peers

Per-request deadlines catch a stalled server whose kernel still ACKs TCP. Reads move to the other copy; writes to a down disk's rows fail fast before any copy changes. A 30 s network partition recovers within 1 s of healing.

Format safety

Drives carry a superblock tied to disk, node and cluster. A drive reformatted in place is treated as empty, so reads never return zeros where data should be. Stable /dev/disk/by-id paths and a global LVM filter keep the host from grabbing spindle drives.

Quorum without split brain

Leases and fencing come from the Raft managers. A storageless witness supplies the third vote for two-node sites. Manager state has scheduled backups for recovering from a lost majority.

How it compares

There is excellent open-source storage already. Here is where Spindle fits, and where you should pick something else.

SpindleCeph RBD (and Rook)LonghornLINSTOR / DRBD (Piraeus)OpenEBS Mayastor
InterfacesBlockBlock, file, S3 objectBlockBlockBlock
Where it runssystemd hosts, Kubernetes, or bothBare metal (cephadm) or Kubernetes (Rook)Kubernetes onlyBare metal or KubernetesKubernetes only
Data pathClient computes placement, talks to drives directly (ublk)Client computes placement with CRUSH (librbd / krbd)Per-volume engine fronting replicasIn-kernel replication of a whole device to N nodesSPDK engine, NVMe-oF to the app node
One volume spreads overEvery drive in the poolEvery OSD in the poolIts replica nodes' disksIts replica nodes' disksIts replica nodes' disks
Designed aroundHDDs, with an optional SSD read cacheAny media, any scaleSimplicity on KubernetesMature, local-read replicationNVMe at low latency
Host footprintSmall: no async runtime, 16 B per placement rowLarger: about 4 GiB memory target per OSD by default, plus MONs and MGRsModerate: engine and replica processes per volumeSmall: kernel module plus controllerDedicated polling cores and hugepages per engine
Smallest HA cluster2 storage nodes + storageless witness3 MONs recommended3 nodes recommended2 nodes + diskless tiebreaker3 nodes recommended
MaturityYoung: running in one lab, no outside production users yetVery mature, huge production baseMature; CNCF incubating projectVery mature; DRBD has run in production for two decadesProduction releases; part of OpenEBS (CNCF Sandbox)

Pick Spindle when

  • Your capacity is hard drives and you want every one of them working for each volume.
  • Storage must share hosts with VMs and pods without claiming their CPU and memory.
  • You run plain Linux hosts and Kubernetes side by side, or a two-node site with a witness.

Pick something else when

  • You need file or S3 object storage: Ceph, or MinIO and SeaweedFS for objects.
  • You need years of production history and a support ecosystem today: Ceph, DRBD.
  • Your cluster is all-NVMe and latency is everything: Mayastor or Vitastor are built for it.

Fair-comparison notes

Peer figures are from each project's public documentation and defaults, which are tunable. Spindle's own numbers come from its repository's lab runs. We have not published a head-to-head benchmark yet; when we do, the method and raw results will be in the repo.

Honest status

Spindle is young and moving fast. This is what exists today, what is landing, and what is only designed. When in doubt, the code and the design documents in the repo are the source of truth.

Built and tested

  • Two-copy replicated block volumes over HDD pools; discard and write-zeroes
  • ublk client with restart recovery and stable /dev/spindle/NAME names
  • Raft managers, failure detection, leases, fencing and degraded writes; storageless witness
  • Resync and rebuild, riskiest rows first; a background scrub that compares every copy
  • Write-intent log for power-cut consistency; missed-chunk records
  • Named networks per traffic role; SSD read cache
  • systemd units, packages and spindle install; Helm chart and CSI driver (csi-sanity: 68 passed, 0 failed)
  • Admin page, Prometheus metrics, alert runbook, Headlamp plugin
  • Static binaries for x86_64 and aarch64

In the code, rolling out

  • Server-side snapshots
  • SSD write-back cache
  • Graceful maintenance and drive-fill balance
  • Rolling upgrades with preflight checks
  • One-command host join

Designed, not built yet

  • Encryption at rest and a FIPS 140-3 crypto boundary
  • Boot from SAN
  • Asynchronous replication to a second cluster
  • Changed-block export for backups

Get started

Pick the shape that matches your hosts. Every path uses the same spindle binary.

# every host: binary, units, udev rules, LVM filter, kernel modules
$ sudo spindle install
# storage hosts, once: format the data drives
$ sudo spindle format -c /etc/spindle/cluster.conf -n 1 --dry-run
# client hosts: a volume is a block device
$ sudo spindle attach VOLUME