In this example we create an EKS cluster consisting of 1 `g6e.48xlarge` compute node, each with 8 NVIDIA L40S GPUs and 1 `c5.2xlarge` CPU node as control plane. We also setup EFA between the compute nodes.
### a. Configure AWS CLI
```
aws configure
```
### b. Create a config file for EKS cluster creation
```
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: <CLUSTER_NAME>
version: "1.32"
region: <REGION_NAME>
iam:
withOIDC: true
managedNodeGroups:
- name: sys-ng
instanceType: c5.2xlarge
minSize: 1
desiredCapacity: 1
maxSize: 1
iam:
withAddonPolicies:
imageBuilder: true
autoScaler: true
ebs: true
efs: true
awsLoadBalancerController: true
cloudWatch: true
albIngress: true
- name: efa-compute-ng
instanceType: g6e.48xlarge
minSize: 1
desiredCapacity: 1
maxSize: 1
volumeSize: 300
efaEnabled: true
privateNetworking: true
iam:
withAddonPolicies:
imageBuilder: true
autoScaler: true
ebs: true
efs: true
awsLoadBalancerController: true
cloudWatch: true
albIngress: true
```
> [!NOTE]
> We set `minSize` and `desiredCapacity` to be 1 because AWS does not create your cluster successfully if no nodes are available. For example, if you specify `desiredCapacity` to be 2 but there are no available 2 nodes, your cluster creation will fail due to timeout even though there are no errors. The easiest way to avoid this is to create the cluster with 1 node and increase the number of nodes later in the EKS console. After you increase number of nodes in your node groups, make sure GPU nodes are in the same subnet. This is required for EFA to work.
### c. Create the EKS cluster
```
eksctl create cluster -f eks_cluster_config.yaml
```
## 3. Create an EFS file system
We'll need a common, shared storage location to enable pods deployed to multiple nodes to load shards of the same model. This way, they can be used in coordination to serve inference requests for models too large to loaded by GPUs on a single node. In Kubernetes, these common, shared storage locations are referred to as persistent volumes. Persistent volumes can be volume mapped in to any number of pods and then accessed by processes running inside of said pods as if they were part of the pod's file system. We will be using EFS as persistent volume.
Additionally, we will need to create a persistent-volume claim which can use to assign the persistent volume to a pod.
### a. Create an IAM role
Follow the steps to create an IAM role for your EFS file system: https://docs.aws.amazon.com/eks/latest/userguide/efs-csi.html#efs-create-iam-resources. This role will be used later when you install the EFS CSI Driver.
### b. Install EFS CSI driver
Install the EFS CSI Driver through the Amazon EKS add-on in AWS console: https://docs.aws.amazon.com/eks/latest/userguide/efs-csi.html#efs-install-driver. Once it's done, check the Add-ons section in EKS console, you should see the driver is showing `Active` under Status.
### c. Create EFS file system
Follow the steps to create an EFS file system: https://github.com/kubernetes-sigs/aws-efs-csi-driver/blob/master/docs/efs-create-filesystem.md. Make sure you mount subnets in the last step correctly. This will affect whether your nodes are able to access the created EFS file system.
## 4. Test
Follow the steps to check if your EFS file system is working properly with your nodes: https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/examples/kubernetes/multiple_pods. This test is going to mount your EFS file system on all of your available nodes and write a text file to the file system.
## 5. Create StorageClass
You can find your `fileSystemId` from AWS EFS. It usually start with `fs-`.
"content": "In the heart of Eldoria, an ancient land of boundless magic and mysterious creatures, lies the long-forgotten city of Aeloria. Once a beacon of knowledge and power, Aeloria was buried beneath the shifting sands of time, lost to the world for centuries. You are an intrepid explorer, known for your unparalleled curiosity and courage, who has stumbled upon an ancient map hinting at ests that Aeloria holds a secret so profound that it has the potential to reshape the very fabric of reality. Your journey will take you through treacherous deserts, enchanted forests, and across perilous mountain ranges. Your Task: Character Background: Develop a detailed background for your character. Describe their motivations for seeking out Aeloria, their skills and weaknesses, and any personal connections to the ancient city or its legends. Are they driven by a quest for knowledge, a search for lost familt clue is hidden."
}
],
"stream": false,
"max_tokens": 30
}'
```
You should output something similar to below
```
{"id":"chatcmpl-bbe52b36-90ed-4479-9872-89e1aa412aa7","choices":[{"index":0,"message":{"content":"<think>\nOkay, so the user wants me to develop a character background for an explorer named someone in Eldoria. The character is part of the","refusal":null,"tool_calls":null,"role":"assistant","function_call":null,"audio":null},"finish_reason":"stop","logprobs":null}],"created":1753417848,"model":"Qwen/Qwen3-0.6B","service_tier":null,"system_fingerprint":null,"object":"chat.completion","usage":{"prompt_tokens":196,"completion_tokens":29,"total_tokens":225,"prompt_tokens_details":null,"completion_tokens_details":null}}
SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->
# Create an Amazon EFS File System for Amazon EKS
This guide walks through creating an Amazon EFS file system and connecting it to your EKS cluster. The EFS CSI Driver was already installed as an addon via `eksctl.yaml` during cluster creation. Now we need to create the actual file system and make it available to Kubernetes workloads.
This filesystem will be used by Dynamo to store shared model weights and compilation cache across nodes.
## Prerequisites
- EKS cluster created following the [README](README.md)
- Environment variables set:
```bash
export AWS_REGION="us-east-1"
export CLUSTER_NAME="ai-dynamo"
export DYNAMO_NAMESPACE="dynamo-system"
```
## Retrieve VPC and Subnet Information
Get the VPC ID associated with your EKS cluster:
```bash
export VPC_ID=$(aws eks describe-cluster \
--name$CLUSTER_NAME\
--region$AWS_REGION\
--query"cluster.resourcesVpcConfig.vpcId"\
--output text)
```
Get the CIDR range for the VPC (used for the security group rule):
```bash
export VPC_CIDR=$(aws ec2 describe-vpcs \
--vpc-ids$VPC_ID\
--query"Vpcs[0].CidrBlock"\
--output text)
```
## Create a Security Group for EFS
Create a security group that allows NFS traffic (port 2049) from within the VPC:
--description"Security group for EFS access from EKS"\
--vpc-id$VPC_ID\
--region$AWS_REGION\
--query"GroupId"\
--output text)
```
Add an inbound rule to allow NFS traffic from the VPC CIDR:
```bash
aws ec2 authorize-security-group-ingress \
--group-id$EFS_SG_ID\
--protocol tcp \
--port 2049 \
--cidr$VPC_CIDR\
--region$AWS_REGION
```
## Create the EFS File System
```bash
export EFS_FS_ID=$(aws efs create-file-system \
--performance-mode generalPurpose \
--throughput-mode elastic \
--encrypted\
--region$AWS_REGION\
--tagsKey=Name,Value=dynamo-efs \
--query"FileSystemId"\
--output text)
```
Wait for the file system to become available:
```bash
aws efs describe-file-systems \
--file-system-id$EFS_FS_ID\
--region$AWS_REGION\
--query"FileSystems[0].LifeCycleState"\
--output text
```
You should see `available` before proceeding.
## Create Mount Targets
Mount targets allow your EKS nodes to access the EFS file system. You need one mount target per subnet where your nodes run.
Get the subnet IDs used by your EKS cluster:
```bash
export SUBNET_IDS=$(aws eks describe-cluster \
--name$CLUSTER_NAME\
--region$AWS_REGION\
--query"cluster.resourcesVpcConfig.subnetIds[]"\
--output text)
echo"Subnet IDs: $SUBNET_IDS"
```
Create a mount target in each subnet:
```bash
for SUBNET_ID in$(echo"$SUBNET_IDS" | tr'\t''\n');do
echo"Creating mount target in subnet: $SUBNET_ID"
aws efs create-mount-target \
--file-system-id$EFS_FS_ID\
--subnet-id$SUBNET_ID\
--security-groups$EFS_SG_ID\
--region$AWS_REGION 2>/dev/null ||echo" Mount target already exists or subnet is in a duplicate AZ (this is OK)"
done
```
> **Note:** EFS allows only one mount target per Availability Zone. If multiple subnets are in the same AZ, the command will fail for the duplicates, which is expected and safe to ignore.
*`perf-cache` stores benchmark traces and performance results.
```bash
# Create the namespace we will use for Dynamo if not already exists
kubectl create namespace ${DYNAMO_NAMESPACE}
# Create PVCs
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: model-cache
namespace: ${DYNAMO_NAMESPACE}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: "efs-sc-dynamic"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: compilation-cache
namespace: ${DYNAMO_NAMESPACE}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: "efs-sc-dynamic"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: perf-cache
namespace: ${DYNAMO_NAMESPACE}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: "efs-sc-dynamic"
EOF
```
> **Note:** EFS is elastic, the `storage` value in the PVC is required by Kubernetes but does not limit the actual storage. EFS will grow and shrink automatically.
## Verify
Confirm the PVC is bound:
```bash
kubectl get pvc -n${DYNAMO_NAMESPACE}
```
You should see output similar to:
```
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE