# TopoLVM: a capacity-aware CSI storage driver for Kubernetes

> TopoLVM is a Container Storage Interface plugin that provisions local persistent volumes from LVM on a Kubernetes node, and it extends the scheduler so that pods land on nodes with room for the volume they asked for. It ships as a Helm chart, a single container image with four entry points, and a controller plus scheduler pair alongside a node driver.

**topolvm/topolvm** — Capacity-aware CSI plugin for Kubernetes

- Repository: https://github.com/topolvm/topolvm
- Stars: 1,197 · Forks: 203
- Language: Go
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/topolvm-topolvm

## What a capacity-aware CSI driver actually is

TopoLVM is a CSI plugin using LVM for Kubernetes, and the README offers the shortest possible description of what that means: it can be considered as a specific implementation of local persistent volumes using CSI and LVM.

That framing matters because local persistent volumes are a Kubernetes feature with a well-known limitation. A local PV is bound to a specific node, so a pod using one can only run on that node, and the scheduler has to place the pod there, which can leave a pod stuck. TopoLVM's contribution is to make that placement decision informed by capacity rather than by a simple node affinity rule.

The extended scheduler is listed in the scope section as: TopoLVM extends the general Pod scheduler to prioritize Nodes having larger storage capacity. So when a pod asks for a volume, the scheduler places it where there is room, rather than where the volume happens to be.

The CSI conformance is explicit. The conformed CSI version is 1.5.0, and the supported platform is stated as Kubernetes 1.36, 1.35 and 1.34, with a node OS of Linux with LVM2, specifically tested on Ubuntu, and filesystems ext4, xfs and btrfs, and lvm version 2.02.163 or later, which the README notes adds JSON output support.

The kernel constraint is a footnote in the README that deserves more attention than a footnote. The host's Linux Kernel must be v4.9 or later, which supports `rmapbt` and `reflink`, if you use xfs filesystem with an official docker image. That is a hard floor for the xfs path specifically, and it is the kind of requirement that only bites after deployment.

The primary language is Go, the licence is Apache 2.0, and the release history is regular: v0.42.0 and topolvm-chart-v17.3.0 both on 2026-10-08, and topolvm-chart-v17.2.0 on 2026-09-02. The last push was on 2026-10-14 and the repository is not archived. Note that the chart has its own version line, which means the operator-facing artefact and the driver are released separately and have to be kept in step.

## The scope list, and the one feature still on the roadmap

The scope section is a list of what the project does, and it reads like a CSI feature checklist with a couple of additions that are not standard.

Dynamic provisioning: volumes are created dynamically when PersistentVolumeClaim objects are created. Raw block volume: volumes are available as block devices inside containers, without a filesystem mounted on them. Topology: TopoLVM uses the CSI topology feature to schedule a Pod to a Node where the LVM volume exists. Volume expansion: volumes can be expanded by editing PersistentVolumeClaim objects. Snapshot: snapshots can be taken when using thin provisioning.

Then the two that are TopoLVM's own.

The extended scheduler, described above, prioritises nodes with larger storage capacity. And volume metrics: usage stats are exported as Prometheus metrics from `kubelet`, which is a deliberate choice of the source, since kubelet is where the actual per-volume usage is known rather than the driver.

And there is storage capacity tracking, which is the alternative to the extended scheduler. You can enable Storage Capacity Tracking mode instead of using topolvm-scheduler. That is the more standard Kubernetes path, since CSI capacity tracking is a mechanism the ecosystem already has, and the README offers it as a distinct mode rather than an afterthought.

The planned features list has one entry: prioritize based on volume capacity without using the extended scheduler. That is the same capability as the extended scheduler, delivered through a different mechanism, and it is on the roadmap rather than shipped. Read the two together and the picture is clear: the extended scheduler is the current way to do capacity-aware placement, storage capacity tracking is the alternative, and the plan is for the standard mechanism to be able to express the same prioritisation directly.

So for an adopter, the decision is which of the two you configure today, and the knowledge that the standard path is the direction of travel.

## One binary, four entry points, and an optional sidecar image

The Dockerfile is where the deployment shape becomes concrete, and it is unusual in a way that is worth reading.

The build stage is golang:1.26-bookworm, and it runs a make target called build-topolvm with the version and target architecture passed in as build arguments. The resulting artefact is a single binary named hypertopolvm.

Then the runtime stage, ubuntu:22.04, installs btrfs-progs, file and xfsprogs, which are the tools needed to create and inspect the supported filesystems on the host. It copies the binary in as /hypertopolvm, and then creates four symbolic links to it:

```dockerfile
RUN ln -s hypertopolvm /lvmd \
    && ln -s hypertopolvm /topolvm-scheduler \
    && ln -s hypertopolvm /topolvm-node \
    && ln -s hypertopolvm /topolvm-controller
```

So one executable is four different programs, and the symlink name selects the role. That is a deliberate way to keep the deployment small: one image, one binary, and the container's command name decides which component the node runs.

The sidecar variant is a separate stage on top. The build-sidecars stage installs patch and runs make csi-sidecars, and the resulting image copies in the standard CSI sidecar binaries: csi-provisioner, csi-node-driver-registrar, csi-resizer, csi-snapshotter and livenessprobe. Those are the upstream external sidecars, and the fact that the project builds them rather than pulling them from a registry is a supply chain decision worth noting. The repository carries container-structure-test.yaml and container-structure-test-with-sidecar.yaml, so the image layout is checked rather than assumed.

The build orchestration is docker-bake, driven by docker-bake.hcl, and the Makefile handles the distinction between the normal image and the with-sidecar one through a STRUCTURE_TEST_TARGET variable. The Makefile also pins the tools it downloads into a local bin directory, with controller-gen, container-structure-test, actionlint, ghalint, zizmor and golangci-lint, and it lists the host packages the test environment needs, including unzip, lvm2, xfsprogs and thin-provisioning-tools, the last being what makes the thin provisioning snapshot support work.

## Architecture tiers, and what the CI coverage actually tells you

The support matrix in the README is unusually explicit about confidence levels, and it is the most useful paragraph in the document for an adopter.

The architectures are x86, arm64, ppc64le and s390x, and each is assigned a tier with a stated meaning. x86 is Tier1 support: the official docker images are provided and all functionalities are tested by CI. arm64 and ppc64le are Tier2: the official docker images are provided, but no tests run by CI. s390x is Tier3: no docker images are provided, and no tests run by CI, but binaries are built.

That last distinction is the important one. A tier tells you how much of the project's own verification applies to your platform. On x86 you are on the platform where the CI actually runs, which is where a regression is most likely to be caught. On ppc64le you get images but not the test coverage. On s390x you get binaries you have to run yourself.

The storage and scheduling design is documented separately. The docs/ directory contains the user manual, designs and specifications, and there is a diagram of components in docs/design.md. The deploy/ directory holds the Kubernetes manifests, and the charts/ directory holds the Helm chart, which is where the version line topolvm-chart-v17.x comes from. The api/ directory holds the custom resource definitions, and the Makefile contains templates for generating them, with a useLegacy value deciding between two CRD templates and an awk step that injects Helm values into the controller-gen annotations.

The example/ directory is the practical entry point for evaluation. If you want to try TopoLVM on your local machine, the example directory provides scripts to run TopoLVM on kind, Kubernetes IN Docker, and it contains a Makefile, a podpvc.yaml and a values.yaml. For a real cluster, the README points at the documents in docs/README.md.

One deprecation is worth flagging because it is the kind of thing that wastes an afternoon. There is a separate page for Rancher and RKE users, at docs/deprecated/rancher/README.md, so that path is supported but on its way out.

## Conclusion

Adopt TopoLVM if you run Kubernetes on bare metal or on nodes where local LVM storage is available and you want per-node persistent volumes with capacity-aware scheduling. Do not adopt it if your cluster is on a managed cloud where block storage is provided differently, or if you need a filesystem the supported list does not cover. Verify first that your nodes meet the stated kernel and lvm version requirements, that your architecture falls in a tier with CI coverage, and that the storage class you write matches the class and node affinity the extended scheduler expects.

## FAQ

### What is TopoLVM and what problem does it solve?

TopoLVM is a Container Storage Interface plugin that provisions local persistent volumes from LVM for Kubernetes. It can be considered a specific implementation of local persistent volumes using CSI and LVM, and it extends the Pod scheduler to prioritise nodes with larger storage capacity.

### What are the system requirements for TopoLVM?

The README lists Kubernetes 1.36, 1.35 and 1.34, a node OS of Linux with LVM2 specifically tested on Ubuntu, the filesystems ext4, xfs and btrfs, lvm version 2.02.163 or later, and a host kernel of v4.9 or later if you use xfs with an official docker image.

### Which CPU architectures does TopoLVM support?

x86, arm64, ppc64le and s390x, each at a stated support tier. x86 is Tier1 with official images and all functionalities tested by CI, arm64 and ppc64le are Tier2 with images but no CI tests, and s390x is Tier3 with binaries built but no images and no CI tests.

### How do I try TopoLVM locally?

The README points at the example directory, which provides scripts to run TopoLVM on kind, Kubernetes IN Docker, and includes a Makefile, a podpvc.yaml and a values.yaml. For a real cluster, the documents in docs/README.md are the reference, and Rancher and RKE have a separate deprecated page.

## Sources

- [Issues](https://github.com/topolvm/topolvm/issues)
- [License: Apache-2.0](https://github.com/topolvm/topolvm/blob/main/LICENSE)
- [README](https://github.com/topolvm/topolvm/blob/main/README.md)
- [Releases](https://github.com/topolvm/topolvm/releases)
- [topolvm/topolvm on GitHub](https://github.com/topolvm/topolvm)

---

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