AWS EFS

By Vishvesh Patel · DevOps EngineerUpdated September 16, 2026

Shared NFS storage across instances and containers. Mount targets per AZ, why Bursting throughput stalls, access points and EFS versus EBS.

Amazon EFS

EFS is a managed NFS filesystem that many instances, containers or Lambda functions can mount at the same time, all reading and writing the same files. It grows and shrinks automatically and spans availability zones.

That concurrent access is the whole reason to choose it. An EBS volume attaches to one instance and lives in one availability zone; EFS is reachable from every instance in the VPC.

Create and mount

aws efs create-file-system \
  --creation-token orders-shared \
  --encrypted --kms-key-id alias/aws/elasticfilesystem \
  --throughput-mode elastic \
  --tags Key=Name,Value=orders-shared

A mount target is needed in every availability zone you will mount from. This is not optional, and an instance in an AZ without one simply cannot connect:

for subnet in subnet-0a1b2c3d subnet-4e5f6a7b subnet-8c9d0e1f; do
  aws efs create-mount-target --file-system-id fs-0123456789abcdef0 \
    --subnet-id "$subnet" --security-groups sg-0123456789abcdef0
done
sudo dnf install -y amazon-efs-utils
sudo mkdir -p /mnt/shared
sudo mount -t efs -o tls,iam fs-0123456789abcdef0:/ /mnt/shared

Use amazon-efs-utils rather than mounting NFS directly. It handles TLS in transit, IAM authorisation, and retries. A plain mount -t nfs4 works and gives you none of that.

For /etc/fstab:

fs-0123456789abcdef0:/ /mnt/shared efs _netdev,tls,iam,noresvport 0 0

_netdev waits for networking before mounting, without which boot can hang. noresvport lets the client reconnect after a network interruption rather than holding a dead session.

The security group rule everyone forgets

An EFS mount that hangs and eventually times out is, almost every time, the security group:

aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef0 \
  --protocol tcp --port 2049 --source-group sg-0fedcba9876543210

The mount target's security group must allow inbound TCP 2049 (NFS) from the client's security group. Nothing reports this, mount simply sits there for a minute or two and then fails with a timeout, which is a slow way to discover a firewall rule.

aws efs describe-mount-targets --file-system-id fs-0123456789abcdef0 \
  --query 'MountTargets[].[AvailabilityZoneName,LifeCycleState,IpAddress]' --output table

Throughput modes matter more than you expect

This is where EFS goes wrong in production, and the default is the problem.

ModeBehaviour
BurstingThroughput scales with filesystem size, using burst credits
ElasticScales automatically, pay per use
ProvisionedFixed throughput you pay for regardless of size

Bursting was the historical default and is a trap for small filesystems. Baseline throughput is about 50 KiB/s per GiB stored, so a 10 GiB filesystem gets roughly 0.5 MiB/s sustained. It accumulates burst credits while idle and spends them under load, so everything is fast for a while, then abruptly is not.

aws cloudwatch get-metric-statistics \
  --namespace AWS/EFS --metric-name BurstCreditBalance \
  --dimensions Name=FileSystemId,Value=fs-0123456789abcdef0 \
  --start-time 2026-09-15T00:00:00Z --end-time 2026-09-16T00:00:00Z \
  --period 3600 --statistics Minimum

A BurstCreditBalance trending toward zero is the warning, and exhausting it produces a filesystem that has become mysteriously slow with no change on your side. Alarm on it, or use Elastic:

aws efs update-file-system --file-system-id fs-0123456789abcdef0 \
  --throughput-mode elastic

Elastic is the right default now. It scales up to gigabytes per second with no credits to track, and you pay for what you read and write. For a spiky or small workload it is usually cheaper than provisioning, and it removes an entire failure mode.

EFS is not fast for small files

Worth being blunt, because it disappoints people. EFS is a network filesystem, so every operation crosses the network. Latency per operation is orders of magnitude higher than a local EBS volume.

That makes it poor for anything metadata-heavy: node_modules, a Git working tree, a compiler's intermediate files, an application's local cache. npm install on EFS is painfully slow, and no throughput setting fixes it, because the constraint is operation latency rather than bandwidth.

General Purpose performance mode gives the lowest latency and is right for almost everything. Max I/O raises throughput at the cost of higher per-operation latency, and only pays off for genuinely parallel workloads across many instances.

Use EFS for shared state that must be shared. Keep build artefacts and caches on local storage.

Access points

An access point enforces a directory and a POSIX identity, so a client cannot reach outside its own subtree:

aws efs create-access-point --file-system-id fs-0123456789abcdef0 \
  --posix-user 'Uid=1000,Gid=1000' \
  --root-directory 'Path=/tenant-a,CreationInfo={OwnerUid=1000,OwnerGid=1000,Permissions=0755}' \
  --tags Key=Name,Value=tenant-a
sudo mount -t efs -o tls,iam,accesspoint=fsap-0123456789abcdef0 \
  fs-0123456789abcdef0:/ /mnt/tenant-a

Two reasons this matters. It makes multi-tenant use safe, since every client is confined to its path regardless of what it asks for. And it creates the root directory with the right ownership automatically, which removes the usual permission fight when several containers share a filesystem.

Access points are also required for Lambda, and the standard approach on EKS via the EFS CSI driver.

Cost control with lifecycle policies

EFS Standard storage is considerably more expensive per GiB than EBS or S3, so lifecycle management is not optional on any growing filesystem:

aws efs put-lifecycle-configuration --file-system-id fs-0123456789abcdef0 \
  --lifecycle-policies \
    'TransitionToIA=AFTER_30_DAYS' \
    'TransitionToArchive=AFTER_90_DAYS' \
    'TransitionToPrimaryStorageClass=AFTER_1_ACCESS'

Infrequent Access costs a fraction of Standard with a per-access read charge; Archive is cheaper still. TransitionToPrimaryStorageClass=AFTER_1_ACCESS moves a file back on first read, so genuinely active data does not stay in a class that charges per access.

For a filesystem holding years of documents that are rarely re-read, this is frequently the difference between a sensible bill and an alarming one.

IAM authorisation

aws efs put-file-system-policy --file-system-id fs-0123456789abcdef0 \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Deny",
      "Principal": {"AWS": "*"},
      "Action": "*",
      "Condition": {"Bool": {"elasticfilesystem:AccessedViaMountTarget": "true"},
                    "Null": {"aws:PrincipalArn": "true"}}
    }]
  }'

Mounting with -o iam uses the instance role rather than relying purely on network reachability, so a filesystem policy can require authenticated access and deny anonymous mounts. Combined with tls, that gives encryption in transit and identity on every mount.

Common errors

mount.nfs4: Connection timed out

Security group missing inbound 2049, or no mount target in this availability zone.

mount.nfs4: access denied by server while mounting

The filesystem policy or access point is refusing. Check -o iam and the instance role.

mount: /mnt/shared: bad option; for several filesystems ... a helper program

amazon-efs-utils is not installed, so -t efs is unrecognised.

Everything suddenly slow. Burst credits exhausted. Check BurstCreditBalance and switch to Elastic.

Permission denied writing to a new filesystem. The root is owned by root with mode 755. Use an access point with CreationInfo, or chown it once from a client.

Quick reference

TaskCommand
Createaws efs create-file-system --throughput-mode elastic --encrypted
Mount target per AZaws efs create-mount-target --subnet-id <s>
Check mount targetsaws efs describe-mount-targets
Mountmount -t efs -o tls,iam <fs-id>:/ /mnt/x
Access pointaws efs create-access-point --posix-user … --root-directory …
Lifecycleaws efs put-lifecycle-configuration
Switch throughputaws efs update-file-system --throughput-mode elastic
Watch creditsBurstCreditBalance in AWS/EFS

Key takeaways

  • EFS is for storage many clients share at once. EBS is one instance; S3 is objects, not a filesystem.
  • A mount target is required in every availability zone you mount from.
  • Inbound TCP 2049 from the client security group: the cause of almost every mount timeout.
  • Use Elastic throughput. Bursting starves small filesystems once credits run out.
  • Alarm on BurstCreditBalance if you stay on Bursting.
  • EFS is slow for small-file workloads. Never put node_modules or a build cache on it.
  • Use amazon-efs-utils for TLS, IAM and retries.
  • Access points confine clients to a subtree and fix the ownership problem.
  • Set lifecycle policies. Standard storage is expensive per GiB.

Check your understanding

Exercise

A filesystem that became slow overnight

A 20 GiB EFS filesystem performed well for weeks, then throughput collapsed with no configuration or code change. It uses Bursting throughput mode. What happened?

Exercise

A mount that hangs then times out

mount -t efs on an instance in the same VPC hangs for about two minutes and fails with "Connection timed out". A mount target exists in the instance's availability zone. What should you check?


Frequently Asked Questions

When should I use EFS instead of EBS or S3?

EFS when multiple instances or containers must read and write the same files simultaneously. Shared uploads, a CMS content directory, a workspace several workers process. EBS when a single instance needs a fast block device: databases, boot volumes, anything latency-sensitive. S3 when you want objects rather than a filesystem, at the lowest cost and highest durability, accessed over HTTP. Choosing EFS for single-instance storage means paying considerably more per GiB for network latency you did not need.

Why does my EFS mount time out?

Almost always the security group. The mount target needs an inbound rule allowing TCP 2049 from the client's security group, and without it packets are dropped silently while mount waits out its timeout. Nothing in the error mentions a firewall. The other cause is no mount target in the instance's availability zone, since mount targets are per-AZ and an instance cannot use one elsewhere. describe-mount-targets shows which zones are covered.

What is the difference between Bursting and Elastic throughput?

Bursting ties baseline throughput to how much data is stored, around 50 KiB/s per GiB, and funds short bursts from credits that accrue while idle. That works for large filesystems and starves small ones, which fail suddenly when credits reach zero with nothing having changed. Elastic scales automatically to gigabytes per second and charges for what you actually read and write. Elastic is the sensible default; if you remain on Bursting, alarm on BurstCreditBalance.

Why is EFS slow for my application?

Because it is a network filesystem, so every file operation incurs network latency that a local EBS volume does not. Workloads dominated by many small operations, node_modules, a Git working tree, compiler intermediates, an application cache, are badly affected, and no throughput setting fixes it since the constraint is per-operation latency rather than bandwidth. Keep build artefacts and caches on local storage and use EFS only for state that genuinely needs sharing.

What are EFS access points for?

An access point pins a mount to a specific directory and POSIX user and group, so a client is confined to its own subtree no matter what path it requests, which is what makes multi-tenant use safe. It also creates that directory with the ownership and permissions you specify, removing the usual permission struggle when several containers share a filesystem. They are required for Lambda and are the standard approach for Kubernetes through the EFS CSI driver.

How do I stop EFS costs growing?

Set a lifecycle configuration. EFS Standard is expensive per GiB relative to EBS or S3, and Infrequent Access costs a fraction of it with a small per-read charge, while Archive is cheaper again. Transitioning after 30 and 90 days respectively, with TransitionToPrimaryStorageClass=AFTER_1_ACCESS so genuinely active files move back, usually cuts the bill substantially on any filesystem accumulating data that is rarely re-read.

Continue Learning

Explore Related Topics

When it breaks

Related Resources