Skip to content

Helm Chart Reference

This page describes what the meshstor-csi Helm chart deploys and how it integrates with your cluster's policies. Pair it with the auto-generated charts/meshstor-csi/README.md, which lists every value key.

Chart Coordinates

Field Value
Name meshstor-csi
Source oci://ghcr.io/meshstor/charts/meshstor-csi
Repository https://github.com/meshstor/meshstor-csi (chart at charts/meshstor-csi/)
Minimum Kubernetes 1.27.0
appVersion Pins the default image tag — when image.tag is empty, the chart's appVersion is used.

OCI installs require Helm 3.8.0 or newer. See Installation for the install/upgrade commands.

What the Chart Installs

flowchart TB
  subgraph cluster[Cluster-scoped]
    direction LR
    CRDs[2 × CustomResourceDefinition<br/>meshstorvolumes · meshstornodedevices]
    CSI[CSIDriver<br/>io.meshstor.csi.mesh]
    SC[2 × StorageClass<br/>meshstor-replicated · meshstor-local-storage]
    CR[2 × ClusterRole + ClusterRoleBinding<br/>controller · node]
  end
  subgraph ns[Release namespace]
    direction LR
    SS[StatefulSet · controller<br/>1 replica · 4 containers]
    DS[DaemonSet · node<br/>1 per Linux node · 3 containers]
    SA[2 × ServiceAccount]
    Role[Role + RoleBinding<br/>csi-resizer leader-election lease]
  end

A default install (crds.enabled=true, rbac.create=true, serviceAccount.*.create=true, default storageClasses) creates:

Kind Name(s) Notes
CustomResourceDefinition meshstorvolumes.csi.meshstor.io, meshstornodedevices.csi.meshstor.io Annotated helm.sh/resource-policy: keep when crds.keep=true (default) — see CRD Management.
CSIDriver io.meshstor.csi.mesh attachRequired: false, podInfoOnMount: false.
StatefulSet <release>-controller 1 replica, system-cluster-critical. csi-provisioner has no leader election in the driver pipeline, so the StatefulSet is hardcoded to one replica.
DaemonSet <release>-node Every Linux node, system-node-critical, hostNetwork: true.
ServiceAccount <release>-controller, <release>-node Split per role so IRSA / Workload Identity can be attached to one.
ClusterRole + ClusterRoleBinding <release>-controller, <release>-node Cluster-scoped CR access, PV/PVC, events, storage classes — see RBAC.
Role + RoleBinding <release>-controller (release namespace) coordination.k8s.io/leases for csi-resizer leader election.
StorageClass meshstor-replicated (default), meshstor-local-storage Configurable via storageClasses — see Default StorageClasses.
(templated extraObjects) user-supplied Each entry is rendered through tpl against the release context.

Resource names are derived from the release name. Never run multiple instances side-by-side.

Cluster Integration

MeshStor's chart touches several cluster-wide policies.

RBAC

rbac.create=true (default) renders ClusterRoles for the controller and node, plus their bindings, plus a Role/RoleBinding pair in the release namespace for csi-resizer's leader-election lease. Set rbac.create=false if you manage RBAC out-of-band (sealed-secrets, vault, GitOps).

Subject Scope Grants Why
<release>-controller ClusterRole cluster MeshStor CRs, PV/PVC, events, storage classes, pods csi-provisioner + controller reconciliation
<release>-node ClusterRole cluster MeshStor CRs (verbs from _generated-rules-node.tpl) node plugin reads/updates CRs during stage/publish
<release>-controller Role release namespace coordination.k8s.io/leases csi-resizer leader election

Pod Security Admission

The controller pod fits PSA restricted. The node pod's csi-plugin container requires privileged and cannot be reduced — mdadm, nvme-cli, and mount(2) are not optional. If you label namespaces with PSA enforcement, the namespace hosting the DaemonSet must be enforce=privileged. Both pods set seccompProfile: RuntimeDefault, which the baseline policy requires for privileged pods.

Pod Container Privilege PSA fit
Controller (StatefulSet) csi-plugin, sidecars runAsNonRoot, readOnlyRootFilesystem, drop:[ALL] restricted
Node (DaemonSet) csi-plugin privileged: true privileged (required)
Node (DaemonSet) sidecars allowPrivilegeEscalation: false, readOnlyRootFilesystem, drop:[ALL] unprivileged inside privileged pod

Host Requirements

The node pod takes a privileged container, real node IPs, and several host paths. None of these are reducible without losing functionality. If your cluster enforces host access via Kyverno, OPA, or admission webhooks, pre-clear these.

Requirement Why
hostNetwork: true NVMe-oF target/host bind to node IPs (TCP and RDMA)
privileged: true (csi-plugin) runs mdadm, nvme-cli, mount(2)
Host path /dev create/delete/grow GPT partitions on nvme block devices
Host path /sys/kernel/config NVMe-oF target configfs
Host path /run/udev udev settle/notification
Kubelet dir (default /var/lib/kubelet) mount propagation; override with node.kubeletDir for k0s, microk8s, talos
Plugin / registration subdirs kubelet plugin registration socket
Mount propagation Bidirectional on kubelet dir, HostToContainer on /sys/kernel/config — kubelet sees mount(2) results from inside the container

The node pod runs a preStop that deletes its CSI socket so kubelet rebinds cleanly on restart.

CRD Management

Value Default Effect
crds.enabled true Chart renders the two CRDs into templates/crds/. Set to false to manage CRDs out-of-band (Argo CD pre-sync hook, kubectl apply, etc.).
crds.keep true Adds helm.sh/resource-policy: keep to each CRD. helm uninstall leaves the CRDs in place, so existing MeshStorVolume and MeshStorNodeDevice resources survive a reinstall.

Setting crds.keep=false is destructive

Without the keep annotation, helm uninstall deletes the CRDs, which cascades to delete every MeshStorVolume in the cluster — and that destroys the underlying data path state. Only use keep=false if you also keep your own out-of-band backups of the CRs and partitions.

Helm does not upgrade CRDs on helm upgrade — they are installed only on the first helm install. Releases that change CRD schema include reapply instructions in the release notes.

Default StorageClasses

The chart ships with two StorageClasses; storageClasses is a list, so you can add your own or set it to [] to skip creation entirely.

Name Default? Replication Use case
meshstor-replicated yes replicaCount: 2, stripeWidth: 1 (RAID1) General-purpose replicated block storage.
meshstor-local-storage no replicaCount: 1, stripeWidth: 1 (RAID1 with placeholder) Local-only with relocation support. See replicaCount=1 positioning.

Both default to reclaimPolicy: Delete, allowVolumeExpansion: true, and memberMissingTimeout: "900". Per-StorageClass tunables are in StorageClass Parameters.

Two fields are intentionally not exposed per StorageClass:

  • volumeBindingMode is hardcoded to WaitForFirstConsumer. The driver doesn't consume CSI topology hints today, but binding deferral still avoids zombie PVs from PVCs that are never claimed.
  • allowedTopologies passes through to the rendered StorageClass but is not honored by the driver — placement is driven by internal/nodeinfo's scoring algorithm, not by Kubernetes topology. Setting it does not constrain where MeshStor places replicas.

Container Images & Sidecars

The driver and four sidecars are configured separately under image.* and sidecars.* so a single sidecar bump is one focused PR.

Component Default registry Default image
Manager (driver) ghcr.io meshstor/meshstor-csi (tag falls back to chart appVersion)
csi-provisioner registry.k8s.io sig-storage/csi-provisioner
csi-resizer registry.k8s.io sig-storage/csi-resizer
node-driver-registrar registry.k8s.io sig-storage/csi-node-driver-registrar
livenessprobe registry.k8s.io sig-storage/livenessprobe

pullPolicy defaults to IfNotPresent for every image. imagePullSecrets accepts a string, a list of names, or a list of {name: …} dicts; the chart references existing secrets — it does not create them from username/password.

Where to Tune

The full list of values lives in the auto-generated charts/meshstor-csi/README.md. Two cluster-level defaults worth surfacing here:

  • defaultRelocationReservePercent (default 10) — per-node free-space reserve so a failed node's replicas can relocate to survivors. Set 0 cluster-wide, or override per node with the meshstor.io/relocation-reserve-percent label.
  • node.kubeletDir — change from /var/lib/kubelet if your distro uses a different path (k0s, microk8s, talos).

Common production knobs (image pinning, resources, tolerations, custom StorageClasses, extraObjects) are documented per-key in the chart README.

What's Next