⚠️ Pre-release — do not install in production. v0.3.0 is the first public candidate. Only kind and one k3s cluster have recorded evidence; every other distro and CNI row is untested, and data lives on one node with no replication or failover. Try it on a lab or staging cluster, runmake e2e, and test with us: bugs, distro/CNI results, and real-hardware findings are what move this to a release. Seedocs/support.mdfor what is and is not covered.
local-rwx provisions Kubernetes ReadWriteMany volumes from a node's local disk through NFS-Ganesha. It deliberately does not replicate data.
local-path local-rwx replicated storage
────────── ───────── ──────────────────
local disk local disk replicated disks
RWO RWX over NFSv4.1 RWX / RWO
no replication no replication survives node loss
lowest overhead one NFS network hop engine + replica fan-out
Use local-rwx when several pods need the same files and node-loss recovery can come from a backup or the source of truth. Use Longhorn, Ceph, or Mayastor when the storage layer must survive loss of the data node. Use local-rwx-hostpath for a single pod or for databases that replicate themselves; an NFS-backed class is not a database replication strategy.
local-rwx v0.3.0 targets Kubernetes 1.35, 1.36, and 1.37; the release support claim is made only after all three CI matrix jobs pass. Helm and the controller reject versions below 1.35. Every eligible node must have mount.nfs (nfs-common on Debian/Ubuntu or nfs-utils on RHEL/Fedora); the installed agent reports an actionable Warning when it is missing.
One command installs the pinned release:
kubectl apply -f https://raw.githubusercontent.com/p10node/local-rwx/v0.3.0/deploy/manifest/local-rwx.yamlThe same manifest is attached to every GitHub release, and a Helm chart is published to oci://ghcr.io/p10node/charts/local-rwx. Then create a volume:
kubectl apply -f - <<'YAML'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared
spec:
storageClassName: local-rwx
accessModes: [ReadWriteMany]
resources:
requests:
storage: 1Gi
YAML
kubectl wait pvc/shared --for=jsonpath='{.status.phase}'=Bound --timeout=120sThe default StorageClass uses shared mode, /var/lib/local-rwx, archiveOnDelete=true, rootSquash=true, and hard NFSv4.1 mounts. Capacity is advisory in this release. local-rwx-hostpath is a separate RWO class with WaitForFirstConsumer scheduling. Shared is the only supported RWX mode in v0.3.0; mode: dedicated is experimental and refused unless the controller runs with --enable-dedicated.
For local validation:
make verify
make e2e- One server and one NFSv4.1 export serve PVC subdirectories on each selected data node.
- The PV records the Service ClusterIP, not Service DNS. Service and Deployment finalizers protect this immutable endpoint.
- Ganesha's shared export FSID and NFSv4 recovery state persist under the backing hostPath, so server restarts do not change file handles.
- The server is non-privileged, but its required hostPath and two Linux capabilities require the dedicated
local-rwx-servernamespace to use Pod Securityprivilegedenforcement. - NFS
sec=sysis suitable for a trusted cluster, not hostile multi-tenancy. - Shared endpoints have no default NetworkPolicy because restrictive peers break legitimate cross-node mounts on tested Calico; dedicated endpoints get exact-selector policies allowing node IPs and pod CIDRs, but those are exposure reduction rather than workload authentication. Live placement changes have both blocked legitimate mounts and allowed outsider traffic, so dedicated isolation is not a portable release guarantee under the current architecture. Dedicated mode is therefore off by default in v0.3.0 and is not covered by release acceptance; enabling it never claims an isolation boundary.
- Draining or losing the selected data node makes every volume on it unavailable. There is no automatic failover or replication.
- Deleting a PVC archives its data by default. Set StorageClass parameter
archiveOnDelete: "false"only when permanent removal is intended. - Mount options are copied into each PV at provision time. Changing a StorageClass does not update existing volumes.
See docs/modes.md, docs/runbook.md, docs/migration.md, docs/troubleshooting.md, docs/security.md, docs/support.md, docs/risks.md, and docs/comparison.md before production use.
The normal finalizer prevents deletion while PVs reference a Service. If an administrator forcibly removed that protection from a shared endpoint and the Service is gone, --recover reconstructs missing shared-server resources at the ClusterIPs recorded in PV annotations. Dedicated endpoint recovery is not automated in v0.3.0; restore its resources from a captured manifest or migrate/restore the data. Stop application changes first and verify that the old IP has not been allocated elsewhere.
kubectl -n local-rwx-system patch deployment local-rwx-controller --type=json \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--recover"}]'
kubectl -n local-rwx-system rollout status deployment/local-rwx-controller
kubectl -n local-rwx-system logs deployment/local-rwx-controller | grep 'recovered immutable NFS endpoints'Remove the temporary --recover argument after success. Recovery fails closed if PV annotations conflict, are incomplete, or Kubernetes cannot reserve the recorded ClusterIP; do not edit PV.spec.nfs to work around that error.
Gate and implementation evidence are tracked in agents/PROGRESS.md. agents/PLAN.md remains the authoritative scope and design document.