# SPDK: user-space storage drivers for NVMe, iSCSI and vhost

> SPDK moves NVMe, iSCSI and vhost storage paths into user space with polled I/O. This review covers what it solves, how to build it, and where the design costs you cores.

**spdk/spdk** — Storage Performance Development Kit

- Repository: https://github.com/spdk/spdk
- Website: https://spdk.io/
- Stars: 3,686 · Forks: 1,382
- Language: C
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/spdk-spdk

## What SPDK replaces, and who is expected to use it

The README describes SPDK as "a set of tools and libraries for writing high performance, scalable, user-mode storage applications." The mechanism it names is specific: drivers move into user space and run in a polled mode instead of relying on interrupts, which the README says avoids kernel context switches and eliminates interrupt handling overhead. That is the whole pitch, and it tells you the audience. If your storage path spends measurable time in the kernel block layer and in interrupt handling, SPDK offers a way to skip both. If it does not, SPDK offers nothing.

The kit ships an NVMe driver, an I/OAT DMA engine driver, an NVMe over Fabrics target, an iSCSI target, a vhost target and a Virtio-SCSI driver. Those are the pieces a storage appliance, a hypervisor host or a benchmarking rig would assemble. The project is written in C and the repository is dominated by lib/, module/, app/ and examples/, so the expectation is that you link against libraries or run the bundled applications rather than install a daemon and walk away. The licence file is not a standard SPDX identifier the tooling recognises, but the Makefile in the repository root carries an SPDX-License-Identifier of BSD-3-Clause in its header, and the repository has a licenses/ directory alongside it.

This is a framework for people who own the whole stack. It is a poor fit for anyone who wants a mounted filesystem with a POSIX API and no further thought.

## Polled queues, hugepages and the vfio-pci bind

The architecture follows from the polling decision. A thread running an SPDK application owns a queue pair and spins on it, so the data path never yields to the scheduler and never takes an interrupt. Memory for DMA is taken from hugepages, and devices are detached from their kernel driver and bound to a user-space driver such as vfio-pci. The README's AWS section spells this out for one environment: run modprobe vfio-pci before setup.sh, then DRIVER_OVERRIDE=vfio-pci ./setup.sh.

Two consequences fall out of that. First, core count is a budget, not a detail. A polled thread consumes a core whether or not work is arriving, so an idle SPDK target still burns CPU. Second, the device is no longer visible to the kernel once bound, which is why SPDK ships its own applications and examples rather than expecting you to mount something. The repository layout reflects this: examples/ contains separate trees for accel, bdev, blob, fsdev, nvme, nvmf, ioat and vhost, each a small program that exercises one layer.

Control plane work goes through JSON-RPC. The v26.05 release notes mention JSON-RPC schema generation and JSON-RPC client batching, and the repository carries go/rpc and python/ trees with a proto/ directory for protocol definitions. So the split is: data path in C with polled queues, configuration and management over JSON-RPC.

## Installing SPDK and running a first build

The README gives the source route first. Clone the repository, then pull submodules, which matters because the tree includes dpdk, isa-l, ocf, xnvme and libvfio-user as submodules or vendored directories.

```bash
git clone https://github.com/spdk/spdk
cd spdk
git submodule update --init
```

Dependencies are installed by a script rather than by hand. The README states that scripts/pkgdep.sh installs the bare minimum needed to build SPDK, and that --help lists dependencies for optional components.

```bash
./scripts/pkgdep.sh
```

On Linux the build is configure followed by make. The configure script reads the CONFIG file in the repository root and writes mk/config.mk with the final settings.

```bash
./configure
make
```

FreeBSD uses gmake instead, and the README notes that CONFIG_COVERAGE is not available for FreeBSD builds. If you want the optional RDMA support, the README shows two equivalent ways to turn it on: pass --with-rdma to configure, or write CONFIG_RDMA=y into mk/config.mk. A third form overrides the setting on the make command line.

```bash
./configure --with-rdma
make CONFIG_RDMA=y
```

To confirm the tree is sane before you write any code, the README points at the unit test script. It warns that error messages during the run are part of the suite and that the final message indicates success or failure.

```bash
./test/unit/unittest.sh
```

After that, the next real step is one of the programs under examples/, not a service. Pick the tree that matches the layer you care about, read its Makefile, and build it against the libraries you just produced.

## The cost of polling, and when SPDK is the wrong tool

The design that makes SPDK fast is also the reason it is unsuitable in several common situations. A polled thread does not sleep, so on a machine where storage work is bursty and the CPU is needed elsewhere, SPDK trades a scarce resource for latency it may not need to improve. On a laptop or a small VM with two cores, dedicating one to spinning is a bad bargain.

Hugepages and device binding are the second constraint. Binding an NVMe device to vfio-pci removes it from the kernel, so anything else on the host that expected to see that block device stops working. The README's AWS instructions are a reminder that this step is environment-specific: the documented sequence is Ubuntu 18.04 with modprobe vfio-pci before setup.sh and DRIVER_OVERRIDE=vfio-pci. Other environments are not covered in the README, and the documentation it links to is where that detail lives.

The third constraint is scope. SPDK is not a filesystem. It provides block devices, targets and drivers; the filesystem layer above them is your problem, and the examples/blob and examples/fsdev trees show that building one is a project in itself. If what you actually need is a POSIX filesystem with a page cache, SPDK is a detour.

Finally, the build is heavy. It pulls DPDK and several other components, and the configure step has enough optional surface that a mismatch between CONFIG, mk/config.mk and make command-line overrides is easy to create. The README is explicit that make command-line options take precedence over mk/config.mk, which is useful but also means a build can differ from the file you thought you were building.

## SPDK versus DPDK, and versus kernel storage stacks

The search results pair SPDK with DPDK often enough that the difference is worth stating plainly. DPDK is a packet processing framework: it takes network devices into user space and gives you polled rings for Ethernet frames. SPDK takes the same ideas and applies them to storage devices and storage protocols. They are not alternatives so much as neighbours, and SPDK's build reflects that: the root Makefile builds dpdkbuild when the environment points at the in-tree DPDK directory, and configure accepts --with-dpdk to point at an external DPDK installation.

```bash
./configure --with-dpdk=/path/to/dpdk/x86_64-native-linuxapp-gcc
make
```

That option exists so you can use a DPDK build you already maintain, or one installed from dpdk and dpdk-devel packages, instead of the submodule. If your project already standardises on a DPDK version, this is the knob that keeps you from carrying a second copy.

Against the kernel storage stack the difference is a single decision: interrupts versus polling. The kernel path handles interrupts, context switches and scheduling on your behalf and shares cores fairly. SPDK removes that layer and hands you the queue. You get lower per-I/O overhead and predictable latency; you lose the scheduler, the page cache and every kernel tool that expects to see the device. Neither is universally better. A database on a general-purpose server is usually well served by the kernel. A storage target appliance that terminates NVMe-oF or iSCSI and does nothing else is the case SPDK was built for.

## Maintenance, releases and what the licence file does not settle

The repository is not archived, and the last push was on 2026-09-23. Releases arrive on a regular cadence: v25.09 in September 2025, v26.01 in January 2026, and v26.05 in May 2026. The v26.05 notes list NVMe KV support, JSON-RPC schema generation and JSON-RPC client batching. v26.01 added NVMe 2.0 target support and NVMe target RDMA interrupt support; v25.09 added NVMe 1.4 support in the NVMe-oF target and NVMe-oF NSSR. That cadence means upgrade cost is a real line item rather than a one-off. Pinning to a tag and reading CHANGELOG.md and deprecation.md before moving is the practical approach, because the repository carries both files at the top level and a deprecation document implies interfaces do get retired.

On licensing, the situation needs care. The repository metadata reports the licence as NOASSERTION, which means automated tooling could not map the LICENSE file to a known identifier. The root Makefile header carries SPDX-License-Identifier: BSD-3-Clause, and there is a licenses/ directory in the tree alongside vendored components such as dpdk, isa-l, isa-l-crypto, ocf, xnvme and libvfio-user, each of which may carry its own terms. The README does not summarise the combined licensing position. If you are shipping SPDK inside a product, read LICENSE and the licenses/ directory yourself and get your own advice; nothing in the README answers that question.

## Conclusion

Adopt SPDK when you are building a storage target or a user-space data path and you can dedicate cores to polling, pin memory, and accept a build that pulls DPDK and several submodules. Do not adopt it for a general-purpose filesystem, for a laptop, or where interrupt-driven I/O already meets your latency target; the polled model spends CPU to remove kernel context switches. Before committing, verify three things on your own hardware: that scripts/pkgdep.sh covers your distribution, that your NIC or NVMe device binds cleanly with the vfio-pci driver, and that the example under examples/nvme or examples/nvmf you intend to copy still builds against the tag you plan to pin. If those three hold, the rest of the integration is mostly JSON-RPC plumbing.

## FAQ

### What does SPDK stand for?

SPDK stands for Storage Performance Development Kit. The README uses the full name as the project title and the abbreviation throughout the rest of the document.

### What is SPDK and DPDK?

SPDK is a set of tools and libraries for writing high performance, scalable, user-mode storage applications, covering an NVMe driver, an I/OAT DMA engine driver, an NVMe over Fabrics target, an iSCSI target, a vhost target and a Virtio-SCSI driver. DPDK appears in SPDK as a dependency: SPDK builds an in-tree DPDK by default, and configure accepts --with-dpdk to point at an external DPDK installation instead.

### How do I install SPDK on Linux?

Clone the repository, run git submodule update --init, then run ./scripts/pkgdep.sh to install the minimum build dependencies. After that, ./configure followed by make produces the libraries and applications.

### Does SPDK need hugepages and device binding?

The README has a section titled Hugepages and Device Binding, and its AWS instructions require running modprobe vfio-pci before setup.sh with DRIVER_OVERRIDE=vfio-pci. Binding a device this way takes it away from the kernel driver.

### How do I configure optional SPDK components such as RDMA?

Options live in the CONFIG file in the repository root, and the configure script writes the final settings to mk/config.mk. The README shows ./configure --with-rdma, editing CONFIG_RDMA to y in mk/config.mk, or passing make CONFIG_RDMA=y on the command line, with the command line taking precedence.

### Can I build SPDK with shared libraries instead of static ones?

Yes. The README states that configure option --with-shared produces SPDK shared libraries in addition to the default static ones, and links the executables against them. It then requires running ldconfig on the directory containing those libraries and setting LD_LIBRARY_PATH.

## Sources

- [Issues](https://github.com/spdk/spdk/issues)
- [Project website](https://spdk.io/)
- [README](https://github.com/spdk/spdk/blob/master/README.md)
- [Releases](https://github.com/spdk/spdk/releases)
- [spdk/spdk on GitHub](https://github.com/spdk/spdk)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/spdk-spdk
