Skip to content

Helm Chart Reference

This page describes what the meshstor-csi Helm chart deploys and the operational shape of each component. Pair it with the charts/meshstor-csi/README.md, which is auto-generated from values.yaml annotations and lists every 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.

Resources Installed

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), so helm uninstall leaves them — see CRD Lifecycle.
CSIDriver io.meshstor.csi.mesh attachRequired: false, podInfoOnMount: false.
StatefulSet <release>-controller 1 replica, system-cluster-critical.
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.
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. Override with --set fullnameOverride=… to run multiple instances side-by-side.

Controller Pod

A single-replica StatefulSet with four containers sharing a socket via emptyDir.

Container Image Purpose
csi-plugin ghcr.io/meshstor/meshstor-csi The driver binary, started with --role=controller. Handles CreateVolume, DeleteVolume, ListVolumes, GetCapacity, and the controller-side reconciliation.
csi-provisioner registry.k8s.io/sig-storage/csi-provisioner Watches PVCs and calls CreateVolume. No leader election — duplicating the controller would issue duplicate provision calls, which is why the StatefulSet is hardcoded to one replica.
csi-resizer registry.k8s.io/sig-storage/csi-resizer Watches PVC capacity edits and calls ControllerExpandVolume. Uses leader election (--leader-election); the lease lives in the release namespace.
liveness-probe registry.k8s.io/sig-storage/livenessprobe Exposes /healthz for the kubelet probe on port 9909.
Property Default Why
replicas 1 csi-provisioner has no leader election in the driver pipeline.
priorityClassName system-cluster-critical Ensures controller stays scheduled during cluster pressure.
containerSecurityContext (csi-plugin) runAsNonRoot, runAsUser: 65532, readOnlyRootFilesystem, drop: [ALL] The controller only talks to the Kubernetes API. PSA restricted compatible.
tolerations key: CriticalAddonsOnly Lands on dedicated control-plane / addon nodes if you isolate them.
nodeSelector kubernetes.io/os: linux Prevents scheduling onto Windows nodes.
terminationGracePeriodSeconds 300 Must exceed worst-case in-flight DeleteVolume.

Node Pod (DaemonSet)

One pod per Linux node. hostNetwork: true — NVMe-oF connections bind to the node's real IPs, and the host paths the driver needs (/dev, /sys/kernel/config, kubelet dir) are mounted in.

Container Image Purpose
csi-plugin ghcr.io/meshstor/meshstor-csi The driver binary, started with --role=node. Handles NodeStageVolume, NodePublishVolume, partition lifecycle, MD RAID assembly, NVMe-oF target/host.
node-driver-registrar registry.k8s.io/sig-storage/csi-node-driver-registrar Registers the driver with the kubelet's plugin registration directory.
liveness-probe registry.k8s.io/sig-storage/livenessprobe Exposes /healthz for the kubelet probe on port 9909.
Property Default Why
containerSecurityContext (csi-plugin) privileged: true Runs mdadm, nvme-cli, mount(2); bind-mounts /dev and /sys/kernel/config. Cannot be reduced without losing functionality.
hostNetwork true NVMe-oF target/host need real node IPs; required for both TCP and RDMA transports.
Mount propagation Bidirectional on the kubelet dir, HostToContainer on /sys/kernel/config Bidirectional propagation lets the kubelet see mount(2) results from inside the container.
tolerations operator: Exists Lands on every node, including tainted ones.
updateStrategy.rollingUpdate.maxUnavailable 1 One node at a time during rollouts. Increase only if you're confident workloads can tolerate multiple node plugins down simultaneously.
priorityClassName system-node-critical The node plugin is on the data path; eviction would unmount volumes.
terminationGracePeriodSeconds 300 Must exceed worst-case in-flight mdadm --create + mkfs.xfs + NVMe-oF connect retries, otherwise a rolling upgrade can SIGKILL mid-orchestration.
Host paths /dev, /sys/kernel/config, /run/udev, kubelet dir, plugin/registration subdirs All required; do not remove.

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

Container Images

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

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

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.

CRD Lifecycle

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.

Cluster Defaults

Value Default Effect
defaultRelocationReservePercent 10 Each node keeps roughly this percent of its raw capacity free so a failed node's RAID-10 replicas can be relocated to survivors. Set to 0 to disable the gate cluster-wide.
Per-node override meshstor.io/relocation-reserve-percent label Overrides the cluster default for a single node.

The value is passed to both controller and node binaries via --default-relocation-reserve-percent.

RBAC

rbac.create=true (default) renders a ClusterRole for the controller and one for the node, plus their bindings, plus a Role/RoleBinding pair in the release namespace for csi-resizer's leader-election leases.

The controller ClusterRole holds the union of node-side rules plus PVC, PV, pod, event, and storage class verbs needed by csi-provisioner. The node ClusterRole is generated from _generated-rules-node.tpl and tracks the verbs the node plugin needs against the MeshStor CRDs.

Set rbac.create=false if you manage RBAC out-of-band (sealed-secrets, vault, GitOps).

Common Overrides

The full list lives in the chart README. The values most often tuned in production:

Override Purpose
image.tag Pin a specific manager image instead of tracking appVersion.
imagePullSecrets Reference an existing pull secret for private registries.
defaultRelocationReservePercent Adjust the cluster-wide reserve gate for relocation capacity.
controller.resources / node.resources Set CPU/memory requests and limits. Empty by default.
controller.tolerations / node.tolerations Re-target the controller off CriticalAddonsOnly nodes, or restrict the DaemonSet to a subset.
node.kubeletDir Change from /var/lib/kubelet if your distribution uses a different path (k0s, microk8s, talos).
storageClasses Replace the bundled StorageClasses with your own list, or set to [] to install none.
crds.enabled / crds.keep GitOps-managed CRDs, or accept CRD deletion on uninstall (destructive — see CRD Lifecycle).
extraObjects Add NetworkPolicies, ConfigMaps, alert rules, or extra StorageClasses without forking the chart. Each entry is tpl-rendered.

Security Posture

Component Privilege Pod Security Admission
Controller pod privileged: false, runAsNonRoot, readOnlyRootFilesystem, drop: [ALL] restricted compatible.
Node pod (csi-plugin) privileged: true privileged (required — mdadm/nvme-cli, host paths).
Node pod (sidecars) allowPrivilegeEscalation: false, readOnlyRootFilesystem, drop: [ALL] Unprivileged inside the privileged pod.

Both pods set seccompProfile: RuntimeDefault, which the PSA baseline policy requires for privileged pods. If you label the release namespace with PSA enforcement, use enforce=privileged for namespaces hosting the node DaemonSet — the controller alone fits restricted, but the node pod cannot.

What's Next