local-static-provisioner: the Kubernetes provisioner that will not create a disk for you
Static provisioner of local volumes
At a glance
- What is it?
- The local static provisioner manages PersistentVolume lifecycle for disks you have already partitioned on each node, detecting them and creating PV objects for them, and it explicitly does not support dynamic provisioning. It is GA since Kubernetes 1.14, it is a Kubernetes SIG storage project, and its most important behaviour is the one that leaves a PV pointing at a deleted node forever.
- Who is it for?
- The local static provisioner fits a cluster where you deliberately want node-local storage, for a database or a cache where replication is handled by the application rather than by the storage layer, and where you can accept that the volume dies with its node.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What it does and, more importantly, what it refuses to do
The local volume static provisioner manages PersistentVolume lifecycle for pre-allocated disks by detecting and creating PVs for each local disk on the host, and cleaning up the disks when released. The very next sentence in the README is the one that defines the tool: it does not support dynamic provisioning.
That distinction is the entire design. Most provisioners are dynamic, which means you write a PersistentVolumeClaim and the system finds or creates storage to satisfy it. This one is static, which means an administrator has already decided what storage exists on each node, has partitioned and formatted it, and the provisioner's job is to notice that and reflect it in the Kubernetes API so that the standard PVC interface can be used against it.
The project status is GA, since Kubernetes 1.14, and it lives in the kubernetes-sigs organisation as a SIG Storage project, which is the special interest group that owns storage-related Kubernetes work. The licence is Apache-2.0, the primary language is Go, and the module path is sigs.k8s.io/sig-storage-local-static-provisioner, which follows the SIG convention of owning a domain under sigs.k8s.io.
Why local storage at all? The overview gives the reason in one line. Local persistent volumes allow users to access local storage through the standard PVC interface in a simple and portable way. The PV contains node affinity information that the system uses to schedule pods to the correct nodes.
That last clause is the whole mechanism and the whole trade-off at once. The PV carries an affinity rule saying which node it lives on, and the scheduler honours it, so a pod that mounts the PVC lands on the node with the data. The result is that a workload using local storage cannot be rescheduled elsewhere, and that is a deliberate property of the design rather than a bug in it. It is also why a local volume is fast: there is no network storage layer in the path.
Discovery directories, and the two volume modes
The mechanism of static provisioning is a discovery directory, and understanding it tells you everything about the operational requirements.
The provisioner requires administrators to preconfigure the local volumes on each node, and then, depending on the volume mode, either mount them under discovery directories or create a symbolic link under the discovery directory to the block device on the node.
For filesystem volumeMode PVs, which are the default, the administrator mounts the prepared filesystem at a path under the discovery directory, and the provisioner sees a filesystem there and creates a PV pointing at it. For block volumeMode PVs, the administrator creates a symlink in the discovery directory that points at the raw block device, and the provisioner creates a PV that exposes the block device directly without a filesystem.
The provisioner then manages the volumes under the discovery directories by creating and cleaning up PersistentVolumes for each volume.
The two modes differ in a way that matters operationally. A filesystem PV has a filesystem on it, so the filesystem is mounted and the data is inside it. A block PV hands the raw device to the pod, which means the pod's application does the formatting and the mounting, and so the whole lifecycle of the disk belongs to the application rather than to the node. If you are running a database that wants a raw device, block mode is what you want, and it also means the node preparation is lighter because there is nothing to mount.
The node cleanup behaviour is the direct consequence. The README's note says that when the node hosting a local PV is deleted, the data is likely lost, but the PV object still exists, and the system will indefinitely try to schedule the Pod to the deleted node. The remedy is separate: the local volume node cleanup controller documentation explains how to make workloads automatically recover from node deletion.
So this is a data loss story with two parts. The data on the deleted node is gone, and the PV object is a tombstone that the scheduler keeps trying to satisfy. The cleanup controller exists to remove those objects, and there is a separate image for it in the Makefile, which means it is a second component you can run alongside the provisioner or not at all.
Deployment, the version matrix and the release shape
Deployment is documented through Helm first, with plain YAML as the alternative. The Helm documentation covers how to deploy and configure local-volume-provisioner in a Kubernetes cluster, and the example YAMLs are in the deployment/kubernetes/example directory, with Helm generated YAMLs in helm/generated_examples/ described as good sources of examples too.
The version compatibility table is short and worth reading before you pick a release. Provisioner 2.10.0, 2.9.0, 2.8.0 and 2.7.0 are all recommended with Kubernetes 1.21 and later.
That is a useful signal about the project's shape. Four recent minor versions all target the same wide Kubernetes range, which means the provisioner is not tied to a particular Kubernetes release and you are not forced to upgrade the cluster when you upgrade the provisioner. For a storage component that touches PersistentVolume objects, that stability is exactly what you want, because a provisioner change that forces a cluster upgrade is a much bigger deal than an application upgrade.
The release tags come in two formats. There are tags like v2.10.0 and local-static-provisioner-2.10.0, and the Makefile builds and pushes separate images for the provisioner and the cleanup controller. The recent tags are local-static-provisioner-2.10.0 on 2026-10-10, v2.10.0 on 2026-10-08 and local-static-provisioner-2.9.0 on 2026-07-25.
The images are pushed to registry.k8s.io/sig-storage, which is the Kubernetes project registry, and the Makefile defaults the registry and version to that registry and latest, with the environment variables documented as having default values set from hack/release.sh.
The build is cross-platform by design, not just for Linux. There are targets for building and pushing the container for Linux per architecture, the node cleanup controller for Linux per architecture, and both of them for Windows, with LINUX_ARCH defaulting to amd64 and WINDOWS_DISTROS empty by default. The Windows support is worth noting, because a local volume provisioner that runs on Windows nodes lets you use the same mechanism across a mixed cluster.
The verify target runs hack/verify-all.sh, the e2e target runs hack/e2e.sh, and the release target runs hack/release.sh. E2E results are published to a TestGrid dashboard, and there is a separate image build pipeline dashboard for the post-push image build.
The examples directory, and the decisions encoded in the file names
The examples directory is a small set of YAML files whose names describe the design decisions a local volume user has to make, and it is more informative than a paragraph of prose would be.
There is simple-pvc.yaml, which is the minimal request. There is manual-pv.yaml, which is what you would use without the provisioner at all, creating the PersistentVolume by hand, and having it in the same directory makes the difference between manual and provisioned explicit. There is local-reader.yaml, which is the read-only variant.
Then the scheduling decisions. local-statefulset.yaml is the base case, local-statefulset-affinity.yaml adds an affinity rule, and local-statefulset-anti-affinity.yaml adds the anti-affinity that spreads replicas. Those three files together encode the central constraint of local storage: a StatefulSet whose replicas each hold local data wants one replica per node, and that is what the anti-affinity expresses, and the provisioner supports it because the PVs carry node affinity information that the scheduler uses.
The GKE files are for a specific managed environment. gke-daemonset-raid-disks.yaml sets up local disks as a DaemonSet with RAID, gke-pod-nvme-ssd-block.yaml and gke-pvc-nvme-ssd-block.yaml are a pod and a PVC pair for block mode on NVMe SSDs, and gke-daemonset-raid-disks.yaml is the node preparation that makes local NVMe usable as local storage on that cloud.
The remaining repository layout is a standard Kubernetes project. There is a cmd/ directory for the binaries, pkg/ for the library code, a helm/ directory with the chart, deployment/ with the plain YAML, docs/ with the user guide, a test/ directory, a hack/ directory for the build and release scripts, a third_party/ directory and a vendor/ directory. There is also a Makefile, a cloudbuild.yaml for the Google Cloud build pipeline, an OWNERS file, a SECURITY_CONTACTS file, a CONTRIBUTING.md, a KNOWN_ISSUES.md, a CHANGELOG.md with a CHANGELOG/ directory, a RELEASE.md and a TODO.md.
The presence of a TODO.md and a KNOWN_ISSUES.md alongside the feature status list is a good sign about the project's honesty. The future features list is explicit about what is not there: local block devices as a volume source with partitioning and filesystem formatting, dynamic provisioning for shared local persistent storage, local PV health monitoring with taints and tolerations, and inline PV for using a dedicated local disk as ephemeral storage.
The dependency set, and what it says about the coupling to Kubernetes
The go.mod file is instructive because it shows how tightly this provisioner is coupled to a particular Kubernetes API version, and how carefully it tries to be.
The Kubernetes dependencies are pinned to a consistent set: k8s.io/api, apimachinery, apiserver, cli-runtime, client-go, component-base and kubernetes itself are all at 0.32.10, with k8s.io/kubernetes at v1.32.10. The uniformity is deliberate, since a provisioner that mixes Kubernetes library versions tends to produce subtle serialisation mismatches. The module requires Go 1.26.0.
The storage library dependency is sigs.k8s.io/sig-storage-lib-external-provisioner/v6 at v6.3.0, which is the shared library for external provisioners. That is the piece that implements the provisioner contract, and the fact that it is a separate SIG library rather than something in this repository is a sign of good structure, since the contract logic is shared with other provisioners.
The rest of the list is interesting. helm.sh/helm/v3 at v3.15.4 is a direct dependency, which is unusual for a controller and tells you the provisioner embeds Helm, presumably to render the local volume templates it writes to the node. github.com/kubernetes-csi/csi-proxy/client is there, which is a small client for the csi-proxy helper, used for a narrow purpose rather than for a full CSI driver. k8s.io/pod-security-admission and k8s.io/component-base are pulled in, and those are the pieces that let the provisioner evaluate whether a pod is permitted to use a given volume rather than relying on the node.
The observability and CLI side is thin and conventional: prometheus/client_golang for metrics, golang/glog and k8s.io/klog/v2 for logging, spf13/pflag for flags, and google/go-cmp plus ginkgo and gomega for the test suite.
One detail in the dependency list deserves a note. The presence of a vendor/ directory alongside a go.mod is unusual for a modern Go project, and in a Kubernetes SIG repository it usually means the build has to work in an environment without module proxy access, for example inside a build cluster that has no outbound network. It also means the dependency surface is large and updates are deliberate. The indirect requirements list in the file runs to dozens of entries, which is the normal cost of depending on k8s.io/kubernetes directly.
Editorial conclusion
The local static provisioner fits a cluster where you deliberately want node-local storage, for a database or a cache where replication is handled by the application rather than by the storage layer, and where you can accept that the volume dies with its node. It is the wrong choice for anything that must survive node loss, since the README states the data is likely lost and the PV object still exists, and the wrong choice if you want the system to allocate storage on demand, which it explicitly does not do. Before deploying, read the node cleanup controller documentation and decide whether you will run it, check the version compatibility matrix against your cluster version, and be deliberate about the local volume node affinity, because that affinity is the whole mechanism by which a pod is scheduled onto the node holding its data.
Frequently asked questions
What does the Kubernetes local static provisioner do?
It manages PersistentVolume lifecycle for pre-allocated disks by detecting and creating PVs for each local disk on the host, and cleaning up the disks when released. It does not support dynamic provisioning, so administrators must preconfigure the local volumes on each node under the discovery directories.
What happens to local PVs when their node is deleted?
The README states the data is likely lost, but the PV object still exists, and the system will indefinitely try to schedule the Pod to the deleted node. The local volume node cleanup controller documentation explains how to make workloads automatically recover from node deletion.
Which Kubernetes versions does local-static-provisioner support?
The version compatibility table recommends provisioner 2.10.0, 2.9.0, 2.8.0 and 2.7.0 with Kubernetes 1.21 and later, so the provisioner is not tied to a particular Kubernetes release and does not force a cluster upgrade when it is upgraded.
How are filesystem and block local volumes prepared?
For filesystem volumeMode PVs, which are the default, the volumes are mounted under the discovery directories. For block volumeMode PVs, a symbolic link is created under the discovery directory pointing at the block device on the node, and the provisioner creates a PV for it. The provisioner then creates and cleans up PVs for each volume it finds.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kubernetes-sigs-sig-storage-local-static-provisioner)Was this useful?