Skip to main content
emnode / learn
Cost

Delete unused EFS file systems

An idle EFS file system has zero IO and zero connections — but it bills every gigabyte stored, every month, at a rate that dwarfs S3 and EBS.

13 min·10 sections·AWS

Last reviewed

Unused EFS file systems: the basics

Why an idle file share is one of the priciest places to forget data

Amazon EFS is elastic NFS storage you mount across many instances, Lambda functions, or ECS tasks at once. Unlike EBS, there's no provisioning — you pay purely for what you store, and that's the trap. EFS Standard storage runs roughly $0.30 per GB-month in US-East, nearly four times the price of gp3 EBS and over thirteen times the price of S3 Standard. A file system you spun up for a shared workspace and forgot about keeps billing on every byte it holds whether anything ever reads it again.

The check flags a file system with no client activity over a 14-30 day window: ClientConnections sitting at zero or near-zero, and DataReadIOBytes / DataWriteIOBytes flat across the period. Often the giveaway is structural — no mount targets in any subnet, or mount targets that exist but no running instance, Lambda, or ECS task actually references them. The file system is mounted to nothing and serving nobody, but the storage meter never noticed.

It's flagged because EFS hides its cost behind its convenience. Nobody provisions it, so nobody feels they own a fixed cost; the number just appears under "EFS" on the bill and grows quietly as old data accumulates. A 2 TB file system left over from a migration that finished a year ago is $600 a month for data no process has touched since the cutover.

In this lesson you'll learn why EFS is priced so much higher than EBS or S3, how to tell a genuinely dead file system from one that's only rarely accessed (batch jobs and archival mounts look idle most of the time), and the safe retirement pattern — back up, delete mount targets, then delete the file system. You'll see the CloudWatch and EFS CLI commands to confirm a file system is truly cold, the trap of deleting something a Lambda or ECS task still references, and the lifecycle-management alternative — transitioning cold files to EFS IA or Archive — for the cases where the data must be kept but doesn't justify Standard pricing.

Fun fact

The file system that outlived its project by a year

A media company migrated a render pipeline off EFS to S3 in early 2025 and never deleted the old file system. It held about 3.1 TB of finished render assets nobody needed live anymore. With zero ClientConnections and flat IO for fourteen straight months, it quietly billed roughly $930 a month — over $11,000 — on EFS Standard before a FinOps audit caught it. The fix took twenty minutes: copy a manifest to S3 Glacier as insurance, delete two mount targets, delete the file system. The project that created it had been formally closed for thirteen months.

Retiring an idle EFS file system in action

Diego runs the FinOps cadence at a digital-agency platform team. The dashboard flags an EFS file system, fs-0a1b2c3d, holding 820 GB and costing about $246 a month, tagged Project=campaign-portal. Its CloudWatch metrics show ClientConnections flat at zero and TotalIOBytes essentially zero for the last 21 days.

He doesn't trust "looks idle" on its own — a quarterly batch job can make a perfectly live file system look dead for weeks. So he checks the mount targets and asks: is anything actually wired to this? describe-mount-targets returns two mount targets in two subnets, but a quick look at the security group shows no running instances in them, and the campaign-portal service was decommissioned last quarter. He pings the old project owner: "fs-0a1b2c3d still needed?" — "No, that portal's been dead since March, go ahead."

Diego retires it cleanly rather than nuking it blind. He starts an AWS Backup job of the file system as a 90-day rollback path, waits for it to complete, deletes both mount targets, then deletes the file system. Total hands-on time: about ten minutes. The 820 GB stops billing at $0.30/GB-month, and the backup sits in cold storage at a fraction of the cost in case the decommission turns out to have been premature.

First, confirm the file system is genuinely cold by pulling client connections over the last two weeks. A flat zero is a strong abandonment signal.

$ aws cloudwatch get-metric-statistics --namespace AWS/EFS --metric-name ClientConnections --dimensions Name=FileSystemId,Value=fs-0a1b2c3d --start-time 2026-05-12T00:00:00Z --end-time 2026-05-26T00:00:00Z --period 86400 --statistics Maximum --query 'sort_by(Datapoints,&Timestamp)[].{Day:Timestamp,Conns:Maximum}' --output table
-------------------------------------------
| GetMetricStatistics |
+----------+------------------------------+
| Conns | Day |
+----------+------------------------------+
| 0.0 | 2026-05-12T00:00:00Z |
| 0.0 | 2026-05-19T00:00:00Z |
| 0.0 | 2026-05-25T00:00:00Z |
+----------+------------------------------+
# Zero connections every day for 14 days. Now confirm IO is flat too before acting.

ClientConnections flat at zero across the window is the first signal — but pair it with IO before concluding.

Check whether the file system is even mountable anywhere — list its mount targets and the subnets they live in.

$ aws efs describe-mount-targets --file-system-id fs-0a1b2c3d --query 'MountTargets[].{Id:MountTargetId,Subnet:SubnetId,State:LifeCycleState,Ip:IpAddress}' --output table
-----------------------------------------------------------------------
| DescribeMountTargets |
+------------------+--------------+-----------+----------------------+
| Id | Subnet | State | Ip |
+------------------+--------------+-----------+----------------------+
| fsmt-0aa11bb22 | subnet-0d1e | available | 10.0.3.41 |
| fsmt-0cc33dd44 | subnet-0f2a | available | 10.0.4.88 |
+------------------+--------------+-----------+----------------------+
# Two targets exist, but zero connections means nothing is mounting them. Safe to retire.

Mount targets can exist with no consumers — the connections metric, not their presence, tells you if it's live.

EFS billing and lifecycle under the hooddeep dive

EFS bills purely on data stored per GB-month, by storage class — there is no provisioned capacity and (in Elastic/Bursting throughput mode) no fixed throughput charge when idle. US-East rough rates: Standard at $0.30/GB-mo, Standard-Infrequent Access (IA) at $0.016/GB-mo, and Archive at $0.008/GB-mo, plus small per-GB read/request charges on the colder tiers. Compare that to gp3 EBS at $0.08 and S3 Standard at $0.023 and the headline is clear: idle data left on EFS Standard is the single most expensive place in common AWS storage to forget something. A 2 TB Standard file system is ~$600/month for bytes nobody reads.

Lifecycle management is the lever that makes "keep it but make it cheap" possible. You set a policy — for example, transition files to IA after 30 days of no access, and to Archive after 90 — and EFS automatically moves cold files down the tiers. A file system that must be retained for compliance but is never read can drop from $0.30 to $0.008 per GB-month, a roughly 97% cut, without deleting anything. Files transition back to Standard on first access (with a small retrieval fee), so the data stays fully usable.

Deletion has an ordering dependency that trips people up: you cannot delete a file system while it still has mount targets. The correct sequence is delete every mount target first, wait for them to leave the deleting state, then delete the file system itself. Before any of that, confirm nothing references it — a Lambda function with an EFS access point, an ECS task definition with an EFS volume, or an Auto Scaling launch template that mounts it at boot can all look idle today and break loudly the next time they scale up. CloudTrail and the connection history over a longer window are your safety check.

# Confirm read AND write IO are both flat across the window before deciding.
FS=fs-0a1b2c3d
for METRIC in DataReadIOBytes DataWriteIOBytes; do
  echo "== $METRIC =="
  aws cloudwatch get-metric-statistics \
    --namespace AWS/EFS --metric-name $METRIC \
    --dimensions Name=FileSystemId,Value=$FS \
    --start-time 2026-04-26T00:00:00Z --end-time 2026-05-26T00:00:00Z \
    --period 86400 --statistics Sum \
    --query 'sort_by(Datapoints,&Timestamp)[-1].Sum'
done

# Check nothing references the file system before deleting it.
aws lambda list-functions \
  --query "Functions[?FileSystemConfigs[?contains(Arn, '$FS')]].FunctionName"
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=$FS \
  --max-results 5 --query 'Events[].[EventTime,EventName,Username]' --output table

What is the impact of unused EFS file systems?

The direct cost is the EFS Standard bill on data nothing reads. At $0.30/GB-month the math gets painful fast: a 200 GB leftover is $60 a month, an 820 GB project share is $246 a month, a 2 TB migration remnant is $600 a month. The same data on S3 Standard would be a thirteenth of that, and on EFS Archive a thirty-seventh. Every gigabyte left on the wrong tier is roughly an order of magnitude more expensive than it needs to be, and the meter runs every hour regardless of activity.

There's a compounding effect specific to EFS: it has no provisioned ceiling. An EBS volume is a fixed size you chose; an EFS file system grows without anyone re-approving it, so a forgotten one keeps getting larger if any stray automated process still writes to it — a log shipper, a backup target, a CI artifact dump — even after the primary workload is gone. The cost grows on its own, invisibly, with no provisioning event to flag it.

Lifecycle misconfiguration is a third, quieter cost. Many file systems are created with no lifecycle policy at all, so even legitimately-retained data that's read once a quarter sits on Standard at full price forever. The waste here isn't an abandoned file system — it's a live one paying 37x more than it should because nobody enabled IA/Archive transitions. This is often a larger pool of money than the truly-dead file systems, and it's reclaimed without deleting anything.

Finally, orphaned EFS makes data governance harder. Six months on, an auditor or a security review finds a file system full of data — possibly customer data — attached to no application, owned by no one, on premium storage. It's a cost finding and a posture finding at the same time. A short, well-owned list of file systems is far easier to reason about, secure, and tier correctly than a long one nobody can account for.

How do you retire unused EFS file systems safely?

Retirement is a four-step loop that runs at every FinOps cadence: confirm the file system is truly cold, decide retire-versus-retier, back up and delete safely, and set lifecycle and tagging so the survivors stay cheap and owned.

1. Confirm it's genuinely cold, not just quiet

Pull ClientConnections, DataReadIOBytes, and DataWriteIOBytes over at least 30 days — long enough to clear monthly batch jobs and quarterly archival reads. Cross-check that no Lambda access point, ECS task definition, or launch template still references the file system. A batch or archival mount can look identical to a dead one over a two-week window; the longer window and the reference check are what separate the two.

2. Decide: retire it, or keep it and re-tier it

Not every idle file system should be deleted. If the data must be retained but is rarely read, the right move is a lifecycle policy that transitions cold files to IA and then Archive — a ~90-97% cost cut with zero data loss and instant retrieval on access. Reserve deletion for file systems whose project is genuinely closed and whose data is reproducible or already copied elsewhere. Confirm intent with the owner via the cost-allocation tag before anything destructive.

3. Back up, delete mount targets, then delete the file system

Take an AWS Backup recovery point (or copy the contents to S3/Glacier) as a 30-90 day rollback path and wait for it to complete. Then delete every mount target and wait for them to leave the deleting state — you cannot delete a file system that still has mount targets. Only then delete the file system itself. The ordering is non-negotiable; skipping the backup or the wait is how a 'safe' cleanup becomes an incident.

4. Set lifecycle policies and ownership tags on the survivors

Every file system that stays should carry a lifecycle policy (transition to IA after 30 days, Archive after 90) so cold data never sits on Standard at full price, plus Owner and Project tags so the next audit is shorter. Enforce the lifecycle default with an AWS Config rule, and add storage closeout to the project decommission checklist so file systems don't outlive their projects in the first place.

# Option A — keep the data but cut the cost: enable lifecycle tiering.
aws efs put-lifecycle-configuration \
  --file-system-id fs-0a1b2c3d \
  --lifecycle-policies \
    '{"TransitionToIA":"AFTER_30_DAYS"}' \
    '{"TransitionToArchive":"AFTER_90_DAYS"}'

# Option B — retire it: back up, delete mount targets, then the file system.
FS=fs-0a1b2c3d
for MT in $(aws efs describe-mount-targets --file-system-id $FS \
             --query 'MountTargets[].MountTargetId' --output text); do
  aws efs delete-mount-target --mount-target-id $MT
done

# Wait until no mount targets remain, then delete the file system.
while [ "$(aws efs describe-mount-targets --file-system-id $FS \
           --query 'length(MountTargets)' --output text)" != "0" ]; do :; done
aws efs delete-file-system --file-system-id $FS

Quick quiz

Question 1 of 5

You find a 1.5 TB EFS file system on Standard with zero ClientConnections for 30 days, but CloudTrail shows a Lambda function with an EFS access point still references it and the data is a compliance archive you must retain for seven years. What's the right next move?

You've completed Delete unused EFS file systems. You now know why EFS Standard is the priciest place to forget data, how to tell a dead file system from a merely-quiet one using ClientConnections and IO over a long-enough window, the retire-versus-retier decision, and the mandatory mount-target-first deletion order. The next time the wastage report flags an idle file system, you'll have a defensible path from "flagged" to either "retired" or "re-tiered" in minutes.

Back to the library