Skip to content

Installation

MeshStor ships as a Helm chart. The chart installs the CRDs, RBAC, the controller StatefulSet, the node DaemonSet, the CSIDriver object, and a pair of default StorageClasses.

Before you start: complete the Prerequisites checklist.

Already have MeshStor running? Jump straight to Create Your First Volume below.

Deploy

The chart is published to GitHub Container Registry as an OCI artifact. Install it directly — no helm repo add is needed:

helm install meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --namespace meshstor \
  --create-namespace

To pin a specific chart version, pass --version:

helm install meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --version 0.1.0 \
  --namespace meshstor --create-namespace

Available versions are listed at https://github.com/meshstor/meshstor-csi/pkgs/container/charts%2Fmeshstor-csi. OCI installs require Helm 3.8 or newer.

This creates:

Resource Name (default release) Description
Namespace meshstor Created by --create-namespace
CRDs meshstorvolumes.csi.meshstor.io, meshstornodedevices.csi.meshstor.io Volume and device tracking. Chart defaults to crds.keep=true so helm uninstall will not delete them — see Uninstall below.
CSIDriver io.meshstor.csi.mesh Registers MeshStor with the kubelet
ServiceAccounts meshstor-csi-controller, meshstor-csi-node Identity for controller and node pods
ClusterRoles / Bindings meshstor-csi-controller, meshstor-csi-node Permissions for CRs, nodes, PVCs, events
StatefulSet meshstor-csi-controller Controller (1 replica)
DaemonSet meshstor-csi-node Node plugin (every Linux node)
StorageClasses meshstor-replicated (default), meshstor-local-storage Preconfigured — see StorageClass Examples for the full set

Resource names are derived from the release name (meshstor-csi above). Use --set fullnameOverride= or a different release name to run multiple instances side by side.

Customising the install

An operational walkthrough of what the chart deploys and the values most often tuned in production lives in the Helm Chart Reference. The full per-key value table is auto-generated in charts/meshstor-csi/README.md. The most common overrides:

# Pin a specific image tag
helm install meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --namespace meshstor --create-namespace \
  --set image.tag=v0.1.0

# Skip the bundled StorageClasses (install your own separately)
helm install meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --namespace meshstor --create-namespace \
  --set storageClasses=null

# Let GitOps manage CRDs
helm install meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --namespace meshstor --create-namespace \
  --set crds.enabled=false

A custom values.yaml is usually cleaner than many --set flags:

helm install meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --namespace meshstor --create-namespace \
  -f my-values.yaml

Verify the Deployment

It is critical for self-healing capability.

Disable udev MD auto-assembly on each node. (see Prerequisites)

sudo tee /etc/udev/rules.d/10-mdadm-no-udev-assemble.rules > /dev/null <<'EOF'
SUBSYSTEM!="block", GOTO="md_no_udev_end"
ACTION=="remove", GOTO="md_no_udev_end"
ENV{ID_FS_TYPE}=="", IMPORT{builtin}="blkid"
ENV{ID_FS_TYPE}=="linux_raid_member", ENV{ANACONDA}="1"
LABEL="md_no_udev_end"
EOF

sudo udevadm control --reload-rules
sudo udevadm trigger

Check that all pods are running:

kubectl -n meshstor get pods
NAME                          READY   STATUS    RESTARTS   AGE
meshstor-csi-controller-0     4/4     Running   0          30s
meshstor-csi-node-abc12       3/3     Running   0          30s
meshstor-csi-node-def34       3/3     Running   0          30s

Confirm the default StorageClasses exist:

kubectl get storageclass
NAME                              PROVISIONER              RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
meshstor-replicated (default)     io.meshstor.csi.mesh     Delete          Immediate           true                   30s
meshstor-local-storage            io.meshstor.csi.mesh     Delete          Immediate           true                   30s

Verify device discovery (each node reports its NVMe drives):

kubectl get msnd
NAME            MODEL                   SERIAL   LOCALPARTITIONS   REMOTEPARTITIONS   UNKNOWNPARTITIONS   MULTIQUEUE   BIGGESTUSABLEFREESPACE   SIZE      SECTORSIZE   UPDATEDAT
node1-nvme0n1   WD_BLACK SN7100 1TB     244...   0                 0                  0                   20           931.5Gi                  931.5Gi   4096         5s
node2-nvme1n1   WD_BLACK SN7100 500GB   260...   0                 0                  0                   16           465.8Gi                  465.8Gi   512          5s

No devices listed

If kubectl get msnd returns no resources, check that:

  • NVMe drives are present on the nodes (lsblk -d | grep nvme)
  • The node pods are running and not crash-looping (kubectl -n meshstor logs daemonset/meshstor-csi-node -c csi-plugin)
  • Drive selection labels are set on each node (see Prerequisites) — without the label, no drives are registered for that node

Create Your First Volume

The chart preinstalls meshstor-replicated as the default StorageClass, so a PVC with no storageClassName set will land on it. Create a PVC and a pod that writes to it.

1. PersistentVolumeClaim

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-first-volume
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: meshstor-replicated
  volumeMode: Filesystem
EOF

2. Workload

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: writer
spec:
  containers:
    - name: writer
      image: busybox
      command: ["sh", "-c", "while true; do date >> /data/log.txt; sleep 5; done"]
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: my-first-volume
EOF

3. Verify the Volume

Check that the PVC is bound:

kubectl get pvc my-first-volume
NAME              STATUS   VOLUME             CAPACITY   ACCESS MODES   STORAGECLASS          AGE
my-first-volume   Bound    pvc-cd1038a7-...   1Gi        RWO            meshstor-replicated   30s

Check the volume status — it should reach Synced with READY showing 2/2:

kubectl get msvol
NAME               PHASE    MDSTATE   READY   DEGRADED   SYNC   NODE       AGE
pvc-cd1038a7-...   Synced   active    2/2     0                 mf-01-02   1m

The NODE column is the consumer node — the one whose pod has the PVC mounted. Run kubectl get msvol <volume-name> -o jsonpath='{.status.partitions[*].nodeID}' to see all nodes hosting the partitions.

Check the partitions are on different nodes:

kubectl get msvol pvc-cd1038a7-... -o jsonpath='{range .status.partitions[*]}{.nodeID} {.state}{"\n"}{end}'
node1 Synced
node2 Synced

Verify the workload is writing data:

kubectl exec writer -- tail -3 /data/log.txt
Tue Apr  1 12:00:00 UTC 2026
Tue Apr  1 12:00:05 UTC 2026
Tue Apr  1 12:00:10 UTC 2026

Clean Up the Test Volume

kubectl delete pod writer
kubectl delete pvc my-first-volume

Upgrade MeshStor

helm upgrade meshstor-csi oci://ghcr.io/meshstor/charts/meshstor-csi \
  --namespace meshstor

Pin to a specific release with --version <x.y.z>; omit it to pick up the latest published chart.

CRDs bundled via crds.enabled=true are installed on first helm install only — Helm does not upgrade them on helm upgrade. If a release includes a CRD schema change, the release notes will say so and explain how to reapply the updated CRDs.

Uninstall MeshStor

helm uninstall meshstor-csi --namespace meshstor

helm uninstall removes the workloads, RBAC, CSIDriver, and StorageClasses but keeps the CRDs by default (crds.keep=true). Existing MeshStorVolume and MeshStorNodeDevice resources are preserved so you can reinstall without losing state.

Deleting CRDs is destructive

Removing the CRDs cascades to delete every MeshStorVolume in the cluster, which destroys the underlying data path state. Only do this after you have deleted all PVCs backed by MeshStor and confirmed the data is no longer needed.

To remove the CRDs anyway:

kubectl delete crd meshstorvolumes.csi.meshstor.io meshstornodedevices.csi.meshstor.io

Alternatively, pass --set crds.keep=false when installing — but note this removes the safety net on every subsequent uninstall.

What's Next