S3, EBS & EFS

Object/block/file storage and S3 classes.

Beginner30 min · lesson 6 of 15

You already store things three different ways without thinking about it. Some things go in a parcel locker: you hand over a sealed box, take the claim ticket, and later swap the ticket back for the whole box. Nobody opens it while it sits there. That is Amazon S3 (Simple Storage Service), and the claim ticket is called the *key*. Other things live on the hard drive bolted inside one machine, raw and fast and invisible to every other machine. That is Amazon EBS (Elastic Block Store). And some things live on the shared network drive in the office, where everyone browses the same folders at the same time. That is Amazon EFS (Elastic File System). Three storage models: object, block, and file. Nearly every storage question you will ever face, on the exam or at 2am in production, comes down to matching the access pattern to the model first, then tuning cost inside it.

Why AWS sells you three storage services instead of one

What separates the three is what the interface lets you touch. Object storage holds whole blobs of data plus their metadata behind an HTTPS API (a web request, not a disk operation). You PUT and GET an entire object, up to 5 TB each, and you can never edit bytes in the middle of one. Changing a file means uploading the whole thing again. Block storage hands your operating system a raw virtual disk that it partitions and formats itself, so it can read or write any byte at any offset, but only for the single instance it is attached to. File storage gives you a POSIX directory tree (the ordinary Unix rules for paths, permissions and file locking) over NFS (Network File System, the standard protocol for mounting a remote folder), so many clients share one hierarchy at once. The plumbing follows from the interface. When you PUT an object, S3 copies it across at least three Availability Zones (separate data centre buildings inside one Region) and only then tells you the write succeeded. That is where the famous eleven nines comes from, a 99.999999999% chance that any given object is still there a year later. EBS copies your data only inside a single Availability Zone, which is exactly why a volume is pinned to that zone forever. EFS spreads across several zones in a Region. There is a fourth option people forget: the EC2 instance store, NVMe (a fast solid-state disk protocol) hardware physically inside the host machine. Fastest of the lot, and it disappears.

S3 hands-on: buckets, keys and the defaults that protect you

A *bucket* is the container your objects live in, and its name has to be unique across every AWS account on Earth, because that name becomes part of a public DNS (Domain Name System, the internet's address book) hostname. Pick something nobody else would think of. Inside the bucket there are no folders at all. The namespace is flat, so the key backups/backup.tar.gz is one long string that happens to contain slashes. The console draws you a folder tree because humans like folder trees, but no directory exists underneath. Two defaults now do real security work on your behalf. Since January 2023 every new object is encrypted at rest with SSE-S3 (server-side encryption using AES-256 keys that S3 manages for you). Since April 2023 Block Public Access is switched on for new buckets, so nothing is readable by the world unless you deliberately take the guardrails apart. Create a bucket and put something in it:

s3-first-bucket.sh
# bucket names are globally unique; outside us-east-1 you must state the Region twice
aws s3api create-bucket --bucket secopslog-artifacts-2026 \
--region eu-west-1 \
--create-bucket-configuration LocationConstraint=eu-west-1
# {
# "Location": "http://secopslog-artifacts-2026.s3.amazonaws.com/"
# }
aws s3 cp backup.tar.gz s3://secopslog-artifacts-2026/backups/backup.tar.gz
# upload: ./backup.tar.gz to s3://secopslog-artifacts-2026/backups/backup.tar.gz
aws s3api head-object --bucket secopslog-artifacts-2026 --key backups/backup.tar.gz
# {
# "AcceptRanges": "bytes",
# "LastModified": "2026-07-13T09:41:22+00:00",
# "ContentLength": 52428800,
# "ETag": "\"9c1185a5c5e9fc54612808977ee8f548-7\"",
# "ContentType": "application/x-tar",
# "ServerSideEncryption": "AES256",
# "Metadata": {}
# }

Read that output like a receipt. "ServerSideEncryption": "AES256" proves SSE-S3 ran without you passing a single flag. StorageClass is missing entirely, and that absence is a classic CLI quirk: no field means S3 Standard. The -7 on the end of the ETag is the interesting part. It says the file arrived as a *multipart upload*, in seven pieces. A single PUT tops out at 5 GB, so aws s3 cp quietly chops anything over 8 MiB into parts on your behalf. Splitting the file also lets the transfer run in parallel and pick up where it left off if the connection drops.

Storage classes, and letting lifecycle rules do the filing

Every storage class buys you the same eleven nines of durability. What changes is how warm the data is kept, and what you pay for that warmth. S3 Standard is the hot tier: reads come back in milliseconds, reading costs nothing extra, and storage runs about $0.023 per GB-month. Standard-IA (Infrequent Access) roughly halves the storage price, then charges you per GB every time you read, plus a 30-day minimum storage charge on anything you put there. It saves money only if you genuinely leave the data alone. One Zone-IA knocks another 20% off by keeping the data in a single Availability Zone, so save it for files you could rebuild if that one zone burned down. The Glacier family handles archives. Glacier Instant Retrieval still reads in milliseconds (90-day minimum). Glacier Flexible Retrieval takes minutes to hours (90-day minimum). Glacier Deep Archive costs roughly $0.001 per GB-month and makes you wait 12 to 48 hours (180-day minimum). Intelligent-Tiering watches each object and shuffles it between tiers for you for a small per-object monitoring fee, and it is the exam's answer every single time a question says the access pattern is unknown or changing. In real life you will rarely set a class by hand. *Lifecycle rules* on the bucket do the filing for you on a schedule:

lifecycle.sh
cat > lifecycle.json <<'EOF'
{
"Rules": [{
"ID": "archive-backups",
"Filter": { "Prefix": "backups/" },
"Status": "Enabled",
"Transitions": [
{ "Days": 30, "StorageClass": "STANDARD_IA" },
{ "Days": 90, "StorageClass": "GLACIER" }
],
"Expiration": { "Days": 365 }
}]
}
EOF
# note: "GLACIER" is the API name for Glacier Flexible Retrieval;
# GLACIER_IR and DEEP_ARCHIVE are also valid targets
aws s3api put-bucket-lifecycle-configuration \
--bucket secopslog-artifacts-2026 \
--lifecycle-configuration file://lifecycle.json
# {
# "TransitionDefaultMinimumObjectSize": "all_storage_classes_128K"
# }
Pick the storage model, then tune cost
What is the access pattern?
Match the workload to a storage model first, then optimize cost inside whichever service won
Objects served over HTTPS to many clients
Amazon S3
Object store, 11 nines across 3+ AZs; lifecycle rules when access is predictable, Intelligent-Tiering when it is not
Raw disk for exactly one instance
EBS gp3
Single-AZ block volume; capacity, IOPS and throughput are three separate dials
POSIX tree shared across instances and AZs
Amazon EFS
Managed NFS v4.1, Linux only, grows on its own; around 4x gp3 cost, so keep it for genuine sharing
Scratch you can afford to lose
Instance store
NVMe inside the host, fastest of all but ephemeral; gone the moment the instance stops or terminates
Name the access pattern before you look at price: object vs block vs file picks the service, and cost tuning happens after that.

EBS: a precise disk for exactly one instance

EBS gives one instance a durable virtual disk that survives a reboot. The current default type is gp3, and it is what the console picks for new volumes. Its trick is that the three things you pay for come apart: capacity (how many gigabytes), IOPS (input/output operations per second, meaning how many separate reads or writes it can handle each second) and throughput (how many megabytes per second flow through). Every gp3 volume starts with 3,000 IOPS and 125 MiB/s no matter how small it is, and you can raise each dial on its own, up to 80,000 IOPS and 2,000 MiB/s since a recent limit increase. Older study guides still print 16,000 and 1,000, so trust the newer numbers. Compare that with the older gp2, where speed grew only with size, so buying performance meant buying empty gigabytes you would never fill. Databases that need more than gp3 can deliver move up to io2 Block Express, which reaches 256,000 IOPS. A volume lives in exactly one Availability Zone and can only attach to an instance sitting in that same zone. Your escape hatch is a *snapshot*, an incremental block-level copy parked in S3. Only the blocks that changed since the last snapshot get stored, and from any snapshot you can build a fresh volume in a different zone or copy it to another Region entirely. One exam trap: io1 and io2 support Multi-Attach, which lets several instances in the same zone share one volume, but only with a cluster-aware file system that knows how to coordinate the writes. It is not a stand-in for EFS.

ebs-volume.sh
aws ec2 create-volume --availability-zone eu-west-1a \
--size 100 --volume-type gp3 --iops 3000 --throughput 125 --encrypted \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=app-data}]'
# {
# "AvailabilityZone": "eu-west-1a",
# "Encrypted": true,
# "Size": 100,
# "State": "creating",
# "VolumeId": "vol-0a1b2c3d4e5f67890",
# "Iops": 3000,
# "VolumeType": "gp3",
# "MultiAttachEnabled": false,
# "Throughput": 125
# }
aws ec2 attach-volume --volume-id vol-0a1b2c3d4e5f67890 \
--instance-id i-0fedcba9876543210 --device /dev/sdf
# { "State": "attaching", "Device": "/dev/sdf", "InstanceId": "i-0fedcba9876543210" }
# incremental backup to S3 — first one is full, the rest store changed blocks only
aws ec2 create-snapshot --volume-id vol-0a1b2c3d4e5f67890 \
--description "pre-upgrade backup"
# { "SnapshotId": "snap-0d9e8f7a6b5c4d3e2", "State": "pending", "VolumeSize": 100, "Encrypted": true }
Two kinds of data that die with the instance
Two kinds of data vanish quietly when an EC2 instance goes away. Instance-store data is gone the moment the instance stops, hibernates, terminates, or its host fails, so keep only caches and scratch files there, nothing you could not rebuild from scratch. The second one catches people out: the root EBS volume ships with DeleteOnTermination=true, which means terminating an instance wipes its boot disk too. Snapshot anything you care about, and run aws ec2 describe-instances and read the flag under BlockDeviceMappings before you press terminate.

EFS: one file system, every instance at once

Some workloads need every instance looking at the same files: shared web content, user home directories, a CI (continuous integration) workspace several build runners write into. EFS is a managed NFS v4.1 (Network File System version 4.1) file system that grows and shrinks on its own, so you never provision a size. Instances reach it through *mount targets*, one elastic network interface per Availability Zone, guarded by a security group exactly like any other endpoint in your network. Leave the throughput mode on Elastic, the modern default, so speed follows how hard you are hitting the file system rather than how many bytes you happen to have stored. EFS speaks Linux only. If someone asks for a Windows file share over SMB (Server Message Block, the Windows sharing protocol), the answer is FSx for Windows File Server, and for heavy HPC (high performance computing) work it is FSx for Lustre. EFS has its own lifecycle management that drifts files nobody has touched into Infrequent Access and Archive tiers, and you want that switched on, because EFS Standard costs around $0.30 per GB-month. That is roughly four times EBS gp3 and thirteen times S3 Standard. Sharing is not free.

efs-shared.sh
aws efs create-file-system --performance-mode generalPurpose \
--throughput-mode elastic --encrypted --tags Key=Name,Value=shared-content
# {
# "FileSystemId": "fs-0abc123def456789a",
# "LifeCycleState": "creating",
# "PerformanceMode": "generalPurpose",
# "ThroughputMode": "elastic",
# "Encrypted": true,
# "NumberOfMountTargets": 0
# }
# one mount target per AZ your instances live in
aws efs create-mount-target --file-system-id fs-0abc123def456789a \
--subnet-id subnet-0aa11bb22cc33dd44 --security-groups sg-0e1f2a3b4c5d6e7f8
# { "MountTargetId": "fsmt-0123456789abcdef0", "IpAddress": "10.0.1.87", "LifeCycleState": "creating" }
# on each instance (needs the amazon-efs-utils package); tls encrypts NFS in transit
sudo mount -t efs -o tls fs-0abc123def456789a:/ /mnt/shared
df -h /mnt/shared
# Filesystem Size Used Avail Use% Mounted on
# 127.0.0.1:/ 8.0E 0 8.0E 0% /mnt/shared
# (127.0.0.1 is the local proxy efs-utils runs for TLS; a plain NFS mount shows the fs-... DNS name)

Choosing well: cost, security and how the exam asks

That 8.0E in the df output is eight *exbibytes*, roughly a billion gigabytes, which is EFS quietly telling you that you will never have to resize it. So here is the procedure. Name the access pattern first. Optimize cost second, inside whichever service won. Objects served over HTTPS to lots of clients go to S3, with lifecycle rules when you know the access pattern and Intelligent-Tiering when you do not. A raw disk for exactly one instance is EBS gp3, with size and IOPS tuned on separate dials. A POSIX tree shared across instances and zones is EFS. Scratch you can afford to lose belongs on instance store. The exam pokes at exactly these joints. "Lowest-cost archive, retrieval within 12 hours" means Deep Archive. "Shared storage for a fleet of Linux instances" means EFS. "More IOPS without more capacity" means gp3. Notice how much networking kept creeping in along the way. The EFS mount target you created is a network interface sitting in a subnet, and a security group decides who is allowed to talk to it. Production S3 traffic usually leaves a private subnet through a gateway VPC endpoint so you are not paying NAT gateway charges to reach your own buckets. Subnets, route tables and security groups are where we go next.

S3 is your default durable object store. EBS is a disk bolted to one instance, with Multi-Attach only in narrow cluster-aware cases. EFS is a shared POSIX file system that many instances across many zones read at the same time. Mixing them up is how exam answers go wrong and how production bills quietly balloon.

Lifecycle rules push objects you rarely read into cheaper classes and delete the ones you no longer need at all. Reach for Intelligent-Tiering when nobody can tell you the access pattern, and for a Glacier class when everyone agrees retrieval can wait. Versioning plus MFA (multi-factor authentication) delete are the seatbelts for the day somebody overwrites the wrong key.

Encrypt by default. Block public access by default. And for fat S3 traffic out of private subnets, use a gateway endpoint, so you are not paying a NAT gateway every time your servers talk to your own buckets.

Try this

Make a private bucket with Block Public Access switched on, drop a small object into it, then ask S3 which storage class the object landed in. Tear the bucket down when you are finished so it stops showing up on the bill.

terminal
BUCKET=saa-lab-$(aws sts get-caller-identity --query Account --output text)-$RANDOM
aws s3api create-bucket --bucket $BUCKET --region us-east-1
aws s3api put-public-access-block --bucket $BUCKET \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
echo 'hello' | aws s3 cp - s3://$BUCKET/demo.txt
aws s3api head-object --bucket $BUCKET --key demo.txt --query '{Class:StorageClass,Size:ContentLength}' --output table
output
{
"Location": "/saa-lab-111122223333-18422"
}
---------------------------------
| HeadObject |
+----------+--------------------+
| Class | Size |
+----------+--------------------+
| STANDARD| 6 |
+----------+--------------------+
# Block Public Access settings applied; object is private by default

Takeaway

Object, block and file answer three different questions about how data gets touched. Pick S3, EBS or EFS on purpose, then sort out lifecycle and encryption before you congratulate yourself on the first upload.

Next: write a lifecycle rule that tiers your logs after 30 days and expires them after 90, then go and check that Block Public Access is on for every bucket that is not deliberately a website.

Quick check
01Your production database sits on a 100 GiB gp2 volume and needs a steady 10,000 IOPS. The data will never outgrow 100 GiB. What is the cheapest way to reach that IOPS number?
Correct — On gp3 the IOPS dial is separate from the capacity dial, so you buy the speed you need and none of the empty gigabytes. This is the "more IOPS without more capacity" case from the lesson.
Incorrect — No. On gp2 speed comes only with size, so you would rent more than 3 TiB you will never fill. That trap is the reason gp3 exists.
Incorrect — No. EFS is a shared file system for many instances, not a block device for one database, and it runs around four times the cost of EBS gp3.
Incorrect — No. Instance-store data disappears when the instance stops, hibernates or terminates, which no database can live with.
02Amazon S3 Standard advertises eleven nines of durability (99.999999999%). By the lesson's account, how does S3 actually reach that number for a stored object?
Correct — The write is not acknowledged until copies exist in three or more zones, and that is precisely where the eleven nines comes from.
Incorrect — No. That is closer to the weaker One Zone model. S3 Standard deliberately spreads its copies across separate zones.
Incorrect — No. Cross-Region Replication is something you switch on yourself, not the built-in mechanism behind the durability figure.
Incorrect — No. Versioning saves you from overwrites and deletes, which is a different problem from keeping the bytes alive.
03A hospital group has to keep diagnostic image files for seven years to satisfy regulators. Almost nobody ever reads them again, and when an auditor asks for one, waiting up to 12 hours is acceptable. Which S3 storage class is the cheapest place to keep them?
Incorrect — No. Standard-IA is built for the occasional millisecond read, and across seven years it costs far more per GB than the Glacier archive tiers.
Correct — About $0.001 per GB-month with a 12 to 48 hour restore, which is exactly the "lowest-cost archive, retrieval within 12 hours" shape the lesson describes.
Incorrect — No. You would be paying extra for millisecond reads that nobody in this scenario asked for.
Incorrect — No. One Zone-IA is for data you could rebuild, it costs far more than Deep Archive, and losing that single zone destroys the only copy of a compliance record.

Related