EFS vs EBS vs S3: Choosing AWS Storage
Open the AWS storage docs and EBS, EFS, and S3 appear side by side, but they are not competing for the same spot. The disk attached to a single instance is EBS; the shared file system mounted by many instances at once is EFS; and the object store your applications upload files to and download them from is S3. Because the access models differ, most systems use all three together. The hard questions live at the boundaries, and this post maps those boundaries along with the pricing. Prices are for us-east-1.
Side-by-side comparison #
| EBS (gp3) | EFS (Standard) | S3 (Standard) | |
|---|---|---|---|
| Type | Block storage | File storage (NFS) | Object storage (HTTP API) |
| Access | Attached to one instance | Hundreds to thousands mount concurrently | API calls from anywhere |
| Scope | Single AZ | Regional (multi-AZ) | Regional (multi-AZ) |
| Latency | Sub-millisecond | Milliseconds | Tens of milliseconds (first byte) |
| Capacity | Provisioned (up to 64TiB) | Grows automatically | Effectively unlimited |
| Price per GB-month | ~$0.08 | ~$0.30 | ~$0.023 |
Access models: a disk, a shared folder, or an API #
EBS is the disk you attach to an instance. The OS sees a block device, so you put a file system on it and use it as a boot volume or a database data directory. Latency is sub-millisecond — the lowest of the three — and IOPS can be guaranteed as a spec, so for databases with heavy random I/O there is effectively no alternative. The constraint is attachment scope: a volume lives in a single AZ and attaches to one instance (Multi-Attach on io1/io2 is the exception, and it demands a cluster file system). Choosing a volume type is covered in gp3 vs io2.
EFS is a shared file system mounted over NFS. Hundreds to thousands of instances, containers, and Lambda functions mount the same path concurrently, and capacity grows automatically as you write. The Standard class is multi-AZ, so it survives an AZ failure. Its typical homes: an upload directory shared by several web servers, ML serving where many tasks read the same model files, and NFS-dependent legacy systems brought over in a lift-and-shift.
S3 is an object store you access with PUT and GET over HTTP. You don’t mount it — you call the API. Modifying part of a file is impossible (you re-upload the whole object), and the first byte takes tens of milliseconds. In exchange you get effectively unlimited capacity, 99.999999999% (eleven nines) durability, static web hosting, versioning, and lifecycle policies. It is the default for write-once-read-whole data: backups, logs, images and video, data lakes.
Pricing: unit price alone misleads — look at how each one bills #
Per GB-month, the order is S3 $0.023, EBS gp3 $0.08, EFS Standard $0.30 — EFS is about 4x gp3 and 13x S3. But the billing models differ, so comparing unit prices settles nothing.
- EBS bills what you provision. Allocate 500GB and use 100GB, and you pay for 500GB. gp3 includes 3,000 IOPS and 125MB/s in the base price, so unless you buy extra performance, that rate is the whole story.
- EFS bills what you store — plus throughput. Under Elastic Throughput, reads cost about $0.03 per GB and writes about $0.06 per GB as a separate line item. For a file system with busy I/O, throughput charges can outgrow storage charges. On the flip side, lifecycle policies move idle files down to IA (~$0.016/GB) and Archive (~$0.008/GB), pushing the storage rate below gp3, and One Zone (~$0.16) exists when a single AZ suffices.
- S3 bills what you store plus your requests. PUTs run about $0.005 per 1,000 and GETs about $0.0004 per 1,000. It is overwhelmingly cheap for bulk storage, but a pattern that constantly touches millions of small files can see request charges overtake storage. Choosing a class by access frequency is covered in S3 storage class comparison.
Common overengineering and traps #
- EFS without a sharing requirement: putting single-instance data on EFS means paying 4x the gp3 rate plus throughput charges. Migrating when a sharing requirement actually materializes is not too late.
- Treating S3 as a file system: Mountpoint for S3 lets you mount a bucket, but it is a tool for sequential reads and writes — partial file edits and locking, the POSIX behaviors, are unsupported. If you need file-system semantics, EFS is the right home.
- Databases on EFS: millisecond latency and NFS semantics pair badly with a database’s random I/O. Data directories belong on EBS.
- Snapshots and orphaned volumes: EBS snapshots accumulate in S3-backed storage (~$0.05 per GB-month), and deleting an instance leaves its volumes and snapshots behind, still billing. This is a signature leak from the standing checklist.
Selection order #
- Settle the access model first: if the OS must see a disk, EBS; if multiple compute nodes must mount one file tree, EFS; if the data just needs to go in and come out via API, S3. This ends most debates.
- Validate the sharing requirement: before choosing EFS, ask whether multiple machines genuinely need the same path at the same time. If not, step down to EBS or S3.
- Check latency requirements: sub-millisecond means EBS, full stop. If tens of milliseconds are acceptable, S3 enters the picture.
- Cost it the way each service bills: EBS by provisioned capacity, EFS by storage plus throughput, S3 by storage plus requests — only then does the real bill appear.
- Design the lifecycle: EFS steps down via IA and Archive, S3 via class transitions. For EBS, lifecycle management amounts to cleaning up unused volumes and snapshots.
Summary #
- One instance’s disk is EBS, a file system shared across machines is EFS, and API-accessed objects are S3. They divide the work; they don’t compete.
- Unit prices spread up to 13x ($0.023 / $0.08 / $0.30), but the billing models differ — provisioned, storage-plus-throughput, storage-plus-requests — so unit price alone decides nothing.
- EFS throughput charges (reads $0.03/GB, writes $0.06/GB) are the most-forgotten line item. Busy I/O pushes them past the storage bill.
- The most common overengineering is parking unshared data on EFS. Verify the sharing requirement actually exists first.
- Start from the default placement — databases on EBS, uploads and shared models on EFS, backups, logs, and static files on S3 — and then argue the exceptions. It’s faster.