This is the AWS EKS path — IRSA (IAM Roles for Service Accounts), not static access key/secret key.

IRSA only supports cloud: s3 (AWS) — there is no equivalent mechanism for Azure/GCS/other vendors on this path.


Prerequisites

  • An EKS cluster with kubectl, helm (v3+), aws, and eksctl configured against it
  • A valid mapfs license (email + token, from https://mapfs.cloud/)
  • An existing S3 bucket

Step 1: One-Time Cluster Setup

Associate the cluster's OIDC provider (once per cluster; safe to re-run):

$ eksctl utils associate-iam-oidc-provider \
  --cluster <cluster-name> --region <region> --approve

Create an IAM policy scoped to your bucket:

$ cat > mapfs-irsa-policy.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::<YOUR_BUCKET>"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::<YOUR_BUCKET>/*"
    }
  ]
}
EOF

$ aws iam create-policy \
  --policy-name mapfs-irsa-test \
  --policy-document file://mapfs-irsa-policy.json
# note the returned Arn, needed below

Create the ServiceAccount + IAM Role + trust relationship in one step (eksctl sets the trust policy's subject to exactly system:serviceaccount:kube-system:mapfs-mount-sa, which a hand-rolled IAM Role easily gets wrong):

$ eksctl create iamserviceaccount \
  --cluster <cluster-name> --region <region> \
  --namespace kube-system \
  --name mapfs-mount-sa \
  --attach-policy-arn <arn from create-policy above> \
  --approve

Verify:

$ kubectl get sa mapfs-mount-sa -n kube-system -o yaml | grep role-arn

Step 2: Download the Manifests & Install the Driver

$ wget https://mapfs.cloud/dist/mapfs-csi-manifests-eks-1.1.3.tar.gz
$ tar xzf mapfs-csi-manifests-eks-1.1.3.tar.gz && cd mapfs-csi-manifests-eks-1.1.3

Deploy the ChannelServer (channeld) first. Every mount-daemon pod the node plugin creates connects to this for cache-invalidation fan-out; one per cluster, not per-volume:

$ kubectl apply -f channel-server.yaml

Log in to the AWS Marketplace ECR registry, then install the chart. Its values.yaml already defaults to the Marketplace ECR images. Point channelServer.address at the Service from the step above (<svc-name>.<namespace>.svc.cluster.local:<port>):

$ aws ecr get-login-password --region us-east-1 | \
  helm registry login --username AWS --password-stdin 709825985650.dkr.ecr.us-east-1.amazonaws.com

$ helm install mapfs-csi oci://709825985650.dkr.ecr.us-east-1.amazonaws.com/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 \
  --set irsa.acknowledgePreCreatedSA=true

irsa.acknowledgePreCreatedSA=true tells the chart's install-time validation "I already created mapfs-mount-sa myself" — the chart otherwise refuses to install rather than silently deploy mount pods that reference a ServiceAccount which doesn't exist. If you named the ServiceAccount something other than mapfs-mount-sa above, also add --set irsa.serviceAccountName=<that name>.

Unlike the generic chart, IRSA isn't a toggle here — this chart has no irsa.enabled field at all, since this whole chart is the IRSA path; you only ever choose roleArn vs. acknowledgePreCreatedSA.

On Graviton (arm64) nodes, also add:

--set nodeSelector."kubernetes\.io/arch"=arm64

— though this is only needed for mixed amd64/arm64 clusters; a homogeneous arm64 cluster works fine without it (images are multi-arch, kubelet pulls the matching one per node regardless).


Step 3: Configure a Storage Backend

No Secret to create — credentials come from IRSA. Repeat this step for every bucket you want to expose to the cluster; it's independent of Step 2. Edit storageclass.yaml: fill in at least bucket and region (both already point at IRSA — cloud: "aws", irsa: "true" — nothing to change there). Continuing with the example bucket from Step 1:

--- storageclass.yaml (template)
+++ storageclass.yaml
@@
-  bucket: "<YOUR_BUCKET>"
+  bucket: "my-mapfs-bucket"
-  region: "<YOUR_REGION>"    # e.g. us-east-1, must match the bucket's actual region
+  region: "us-east-1"

Then apply it:

$ kubectl apply -f storageclass.yaml
$ kubectl get storageclass mapfs-s3-eks

With it applied, csi-provisioner creates the PV automatically the moment a matching PVC is created. Save this as my-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 1Ti
  storageClassName: mapfs-s3-eks   # 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

Static provisioning (pointing at an existing bucket/volume by hand instead of a PVC-driven one) follows the same shape starting from pv.yaml + pvc.yaml instead — see /docs/k8s/eks_deploy.html for that example.


Step 4: Verify IRSA Is Actually in Effect

Success doesn't by itself prove IRSA is wired up — a Pod could happen to inherit node-level permissions instead. First, find the mount-daemon pod for this volume — the driver labels it with the PV's volume handle:

$ 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

Confirm it's really running under the IRSA ServiceAccount and picking up the webhook-injected credentials (not e.g. node-level permissions it happened to inherit):

$ kubectl -n kube-system get pod ip-10-0-1-23.ec2.internal-pvc-a1b2c3d4 -o jsonpath='{.spec.serviceAccountName}'
mapfs-mount-sa

$ kubectl -n kube-system exec ip-10-0-1-23.ec2.internal-pvc-a1b2c3d4 -- env | grep -E 'AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE'

Then confirm the bucket is actually mounted and readable, straight from the daemon's own pod — this proves the CSI node plugin → mapfs mount (via the injected IRSA credentials) → ChannelServer path is working end to end, not just from the application pod's point of view:

$ kubectl -n kube-system exec ip-10-0-1-23.ec2.internal-pvc-a1b2c3d4 -- sh -c "ls /var/lib/mapfs/mounts/$VOLNAME"
$ kubectl exec my-app -- df -h /data

/var/lib/mapfs/mounts/<volume-name> is where the daemon mounts the bucket inside its own pod; listing it should show the same files as /data does from my-app. A non-local filesystem type in the df -h output (not overlay/tmpfs) is the other half of that same confirmation.


Uninstalling

Removing what this guide created, in order:

$ kubectl delete -f my-app.yaml -f my-pvc.yaml
$ kubectl delete -f storageclass.yaml

No Secret to delete. 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

If you also want to tear down the IAM Role/ServiceAccount from Step 1:

$ eksctl delete iamserviceaccount \
  --cluster <cluster-name> --region <region> \
  --namespace kube-system --name mapfs-mount-sa

Full Documentation

For directory layout, upgrades, and a reference-style deploy/remove guide, see:
/docs/k8s/eks_deploy.html
For static credentials on Kubernetes (any vendor, not just AWS S3), see:
/docs/k8s/k8s_quickstart.html
For helper commands (load/stat/help/version), see:
/docs/k8s/commands.html


Support

support@mapfs.cloud