Kubelet checks volume mounts from the host namespace, so NodePublishVolume must mkdir and bind-mount the pod target path on the host, not only inside the CSI plugin container. Co-authored-by: Cursor <cursoragent@cursor.com>
kks-csi-plugin
Kloud CSI driver for Kubernetes user clusters. This plugin runs inside each user cluster and implements the Container Storage Interface (CSI) so workloads can use persistent volumes backed by Kloud storage.
Storage operations are forwarded to the kks management CSI server over HTTP. The driver does not talk to storage hardware directly; it translates Kubernetes CSI calls into REST API requests against the management plane.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ User Kubernetes cluster │
│ │
│ ┌──────────────────┐ ┌─────────────────────────────┐ │
│ │ CSI sidecars │ │ kks-csi-plugin │ │
│ │ (provisioner, │────▶│ controller / node modes │ │
│ │ attacher, etc.) │ └──────────────┬──────────────┘ │
│ └──────────────────┘ │ HTTP │
│ ▼ │
└────────────────────────────────────────────┼─────────────────┘
│
▼
┌──────────────────────────────┐
│ kks management CSI server │
│ (Kloud control plane) │
└──────────────────────────────┘
The Helm chart deploys two workloads:
| Component | Kind | Role |
|---|---|---|
| Controller | Deployment | Handles volume create/delete and publish/unpublish via CSI controller RPCs |
| Node | DaemonSet | Stages and publishes volumes on each node (format, mount, bind-mount) |
Standard CSI sidecars are bundled with each workload:
- csi-provisioner and csi-attacher on the controller
- csi-node-driver-registrar on each node
- liveness-probe on both
Features
- Dynamic provisioning via a
StorageClass(optional, enabled by default) - Controller publish/unpublish (attach/detach)
- Node stage/unstage and publish/unpublish (mount operations)
- Block volumes are not supported
- Volume expansion is not supported
Driver name: storage.csi.kloud.team
Prerequisites
- Kubernetes 1.28+
- Network access from the user cluster to the kks management CSI server
- A cluster CSI access token and server URL from your Kloud cluster details
Install with Helm
From the published chart repository
Charts are published as GitHub Releases and to GHCR as OCI artifacts.
GitHub Release (public):
helm install kloud-csi \
https://github.com/KubelanCloud/kks-csi-plugin/releases/download/kloud-csi-0.1.0/kloud-csi-0.1.0.tgz \
--namespace kube-system \
--create-namespace \
--set serverURL=https://csi.example.kloud.team \
--set accessToken="YOUR_CLUSTER_CSI_ACCESS_TOKEN"
OCI (requires GHCR access):
helm registry login ghcr.io
helm install kloud-csi oci://ghcr.io/kubelancloud/charts/kloud-csi \
--version 0.1.0 \
--namespace kube-system \
--create-namespace \
--set serverURL=https://csi.example.kloud.team \
--set accessToken="YOUR_CLUSTER_CSI_ACCESS_TOKEN"
To allow anonymous OCI pulls, set the charts/kloud-csi package visibility to public under GitHub → Packages.
From a local checkout
helm install kloud-csi ./charts/kloud-csi \
--namespace kube-system \
--create-namespace \
--set serverURL=https://csi.example.kloud.team \
--set accessToken="YOUR_CLUSTER_CSI_ACCESS_TOKEN"
Using an existing secret
If you already have a secret containing the access token:
kubectl create secret generic kloud-csi-credentials \
--namespace kube-system \
--from-literal=access-token="YOUR_CLUSTER_CSI_ACCESS_TOKEN"
helm install kloud-csi ./charts/kloud-csi \
--namespace kube-system \
--set serverURL=https://csi.example.kloud.team \
--set existingSecret=kloud-csi-credentials
Verify the install
kubectl get pods -n kube-system -l app.kubernetes.io/name=kloud-csi
kubectl get csidriver storage.csi.kloud.team
kubectl get storageclass kloud-csi
Helm chart
Chart path: charts/kloud-csi
Published releases are available at GitHub Releases and as OCI charts at oci://ghcr.io/kubelancloud/charts/kloud-csi. Bump version in Chart.yaml to publish a new chart release.
Required values
| Value | Description |
|---|---|
serverURL |
Base URL of the kks management CSI server |
accessToken |
Cluster CSI access token (required unless existingSecret is set) |
Helm fails at render time if serverURL or credentials are missing.
Common values
| Value | Default | Description |
|---|---|---|
image.repository |
ghcr.io/kubelancloud/kloud-csi-plugin |
Driver container image |
image.tag |
(chart appVersion) | Image tag (defaults to chart appVersion when empty) |
imagePullSecrets |
[] |
Pull secrets for private registries such as GHCR |
existingSecret |
"" |
Use an existing secret instead of creating one |
existingSecretAccessTokenKey |
access-token |
Key in the secret holding the token |
driver.name |
storage.csi.kloud.team |
CSI driver name |
storageClass.enabled |
true |
Create a StorageClass |
storageClass.name |
kloud-csi |
StorageClass name |
storageClass.isDefault |
false |
Mark as the default StorageClass |
storageClass.reclaimPolicy |
Delete |
Delete or Retain |
storageClass.volumeBindingMode |
WaitForFirstConsumer |
Volume binding mode |
controller.replicas |
1 |
Controller deployment replicas |
rbac.create |
true |
Create RBAC for controller and node |
See charts/kloud-csi/values.yaml for the full list, including sidecar image versions and resource limits.
Chart resources
The chart creates:
CSIDriver— registers the driver with KubernetesStorageClass— optional, for dynamic provisioningDeployment— controller + sidecarsDaemonSet— node plugin + registrar on every nodeConfigMap— minimal HCL stub (settings come from env vars)Secret— access token (unlessexistingSecretis used)- RBAC — service accounts and roles for controller and node
Upgrade and uninstall
helm upgrade kloud-csi ./charts/kloud-csi \
--namespace kube-system \
--reuse-values \
--set serverURL=https://csi.example.kloud.team
helm uninstall kloud-csi --namespace kube-system
Using persistent volumes
With the default StorageClass installed, create a PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: kloud-csi
resources:
requests:
storage: 10Gi
Then mount it in a pod as usual.
Configuration reference
The driver accepts configuration from an HCL file, environment variables, or both. Environment variables take precedence and are what the Helm chart uses.
HCL file
Example: examples/csi-client.hcl
driver {
name = "storage.csi.kloud.team"
endpoint = "unix:///var/lib/kubelet/plugins/storage.csi.kloud.team/csi.sock"
mode = "all" # all | controller | node
}
client {
server_url = "https://csi.example.kloud.team"
access_token = "YOUR_CLUSTER_CSI_ACCESS_TOKEN"
}
Environment variables
| Variable | Description |
|---|---|
KKS_CSI_SERVER_URL |
Management CSI server base URL |
KKS_CSI_ACCESS_TOKEN |
Cluster access token |
KKS_CSI_DRIVER_MODE |
controller, node, or all |
KKS_CSI_NODE_ID |
Node identifier (set automatically on node pods) |
KKS_CSI_DRIVER_NAME |
CSI driver name override |
KKS_CSI_DRIVER_ENDPOINT |
gRPC socket path |
KKS_CSI_CLIENT_TIMEOUT_SECONDS |
HTTP client timeout (default: 30) |
Standalone / development
Build and run locally:
go build -o kks-csi .
./kks-csi -c examples/csi-client.hcl
Or with Docker:
docker build -t kks-csi .
docker run --rm -v "$(pwd)/examples/csi-client.hcl:/csi.hcl:ro" kks-csi -c /csi.hcl
Container images are published to ghcr.io/kubelancloud/kloud-csi-plugin on pushes to main.
Development
go test ./...
License
See repository license terms.