This is the generic Kubernetes path — static access key/secret key credentials, any supported vendor (S3/AWS, GCS, Azure, Wasabi, Aliyun, Cloudflare, Oracle, S3-compatible), on any Kubernetes cluster.
On AWS EKS and you want an AWS Marketplace-compliant install (IRSA, no static AWS credentials), use the EKS quick start instead — EKS is just another Kubernetes cluster from the driver's point of view unless you specifically opt into IRSA.
Prerequisites
- A Kubernetes cluster with
kubectlandhelm(v3+) configured against it - A valid mapfs license (email + token, from https://mapfs.cloud/)
- Access credentials for your target cloud storage (access_key/secret_key, or an Azure connectionString)
- An existing bucket / container — this guide uses
my-mapfs-bucketinus-east-1as the running example; substitute your own
Step 1: Download the Manifests
$ wget https://mapfs.cloud/dist/mapfs-csi-manifests-kubernetes-1.1.3.tar.gz
$ tar xzf mapfs-csi-manifests-kubernetes-1.1.3.tar.gz && cd mapfs-csi-manifests-kubernetes-1.1.3
Step 2: Deploy the ChannelServer
One per cluster, not per-volume. Every mount-daemon pod connects to this for cache-invalidation fan-out.
$ kubectl apply -f channel-server.yaml
$ kubectl -n kube-system get pod -l app=mapfs-channel # wait for Running
Step 3: Install the mapfs-csi Driver
This is a one-time, per-cluster step. It doesn't need any cloud/bucket information — just your mapfs license.
$ helm install mapfs-csi oci://registry-1.docker.io/wyflow/mapfs-csi-chart --version 1.1.3 \
--set license.email=you@example.com \
--set license.token=<token-from-mapfs-portal> \
--set channelServer.address=mapfs-channel.kube-system.svc.cluster.local:7777
channelServer.address is required — without it, mount-daemon pods fail to start.
On Graviton (arm64) nodes, also add:
--set nodeSelector."kubernetes\.io/arch"=arm64
— though this only matters for a cluster with a mix of amd64 and arm64 nodes; a homogeneous arm64 cluster works fine without it (images are published multi-arch, so kubelet already pulls the matching-arch image per node with no scheduling hint needed).
Check status — both should reach Running before continuing:
$ kubectl -n kube-system get pod -l app=mapfs-csi-controller
$ kubectl -n kube-system get pod -l app=mapfs-csi-node
To upgrade to a newer version later:
$ helm upgrade mapfs-csi oci://registry-1.docker.io/wyflow/mapfs-csi-chart --version <new-version>
Step 4: Configure a Storage Backend
Repeat this step for every bucket/cloud you want to expose to the cluster — it's independent of Step 3, and doesn't require reinstalling or upgrading the driver.
Create the credentials Secret (real access_key/secret_key for your bucket, not the
placeholders below):
$ kubectl create secret generic mapfs-cloud-creds -n kube-system \
--from-literal=access_key=<AccessKeyID> \
--from-literal=secret_key=<SecretAccessKey>
For Azure Blob Storage, use connectionString instead of the two fields above — see the
comments in cloud-secret.yaml.
Edit storageclass.yaml: fill in at least cloud and bucket (some clouds also need
region/endpoint/accountId/ociNamespace — see the comments in the file for
details). Continuing the running example:
--- storageclass.yaml (template)
+++ storageclass.yaml
@@
cloud: "s3"
- bucket: "my-bucket-name"
+ bucket: "my-mapfs-bucket"
@@
- # region: "us-east-1" # required: s3/aws, gcs, wasabi, aliyun, oracle
+ region: "us-east-1"
Then apply it:
$ kubectl apply -f storageclass.yaml
$ kubectl get storageclass mapfs-s3
Step 5: Create a PVC and Mount a Volume
With storageclass.yaml applied, csi-provisioner creates the PV automatically the moment a
matching PVC is created — no manual PV needed. Save this as my-pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 1Ti
storageClassName: mapfs-s3 # must match metadata.name in storageclass.yaml
And this as my-app.yaml:
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: ubuntu:22.04
command: ["sleep", "infinity"]
volumeMounts:
- name: cloud-data
mountPath: /data
volumes:
- name: cloud-data
persistentVolumeClaim:
claimName: my-pvc
Apply both and wait for the pod to start:
$ kubectl apply -f my-pvc.yaml -f my-app.yaml
$ kubectl get pv,pvc
$ kubectl get pod my-app -w # wait for Running
Verify from inside the application pod — a non-local filesystem type (not overlay/tmpfs)
confirms the mount succeeded:
$ kubectl exec my-app -- df -h /data
For independent confirmation straight from the mount-daemon pod itself (proves the CSI node plugin →
mapfs mount → ChannelServer path end to end, not just that something got mounted into
/data), look it up by the PV's volume handle, which the driver labels the pod with:
$ VOLNAME=$(kubectl get pv -o jsonpath='{.items[?(@.spec.claimRef.name=="my-pvc")].spec.csi.volumeHandle}')
$ kubectl -n kube-system get pod -l mapfs.cloud/volume=$VOLNAME
NAME READY STATUS RESTARTS AGE
ip-10-0-1-23.ec2.internal-pvc-a1b2c3d4 1/1 Running 0 2m
$ kubectl -n kube-system exec ip-10-0-1-23.ec2.internal-pvc-a1b2c3d4 -- sh -c "ls /var/lib/mapfs/mounts/$VOLNAME"
$ kubectl -n kube-system exec ip-10-0-1-23.ec2.internal-pvc-a1b2c3d4 -- mapfs stat
/var/lib/mapfs/mounts/<volume-name> is where the daemon actually mounts the bucket inside its
own pod — listing it should show the same files as /data did from my-app. stat
(with no arguments) prints the daemon's own request/cache counters for this mount; see
/docs/k8s/commands.html for the other helper commands it accepts.
Static provisioning (pointing at an existing bucket/volume by hand instead of a PVC-driven one), Azure Blob Storage, and self-hosted MinIO all follow the same shape but start from different template files — see the concrete before/after examples for each in /docs/k8s/k8s_deploy.html.
Uninstalling
Removing what this guide created, in order:
$ kubectl delete -f my-app.yaml -f my-pvc.yaml
$ kubectl delete -f storageclass.yaml
$ kubectl -n kube-system delete secret mapfs-cloud-creds
A StorageClass/PV with reclaimPolicy: Retain never deletes data in the cloud bucket — deleting
the PV only removes the Kubernetes record.
Uninstalling the driver (only once no storage backend depends on it anymore):
$ helm uninstall mapfs-csi
$ kubectl delete -f channel-server.yaml
Full Documentation
For directory layout, upgrades, and concrete examples for every YAML template (static provisioning,
Azure, MinIO), see:
/docs/k8s/k8s_deploy.html
On AWS EKS and want IRSA / AWS Marketplace compliance instead of static credentials, see:
/docs/k8s/eks_quickstart.html
For helper commands (load/stat/help/version), see:
/docs/k8s/commands.html