EBS Volumes & Volume Types
Most EC2 instances need a disk that survives reboots and lives independently of the instance itself. That disk is an EBS volume. EBS (Elastic Block Store) is network-attached, durable block storage that you attach to an EC2 instance like a virtual hard drive. Because it lives on its own and is replicated automatically within a data center, your data stays safe even if you stop, start, or replace the instance.
What EBS is and why it matters
Block storage means the disk hands raw blocks to the operating system, and the OS puts a file system (like ext4 or XFS) on top. To Linux or Windows, an EBS volume looks and behaves exactly like a locally attached drive. The difference is that it is not physically inside the server. It lives on the network and connects to your instance over a high-speed AWS internal link.
That network attachment gives you two big wins. First, durability: AWS keeps multiple copies of your data inside one Availability Zone (an AZ, which is an isolated data center within a region), so a single disk failure does not lose your data. Second, persistence: when you stop or terminate an instance, the EBS volume can keep existing on its own, and you can re-attach it to a different instance later.
Key gotcha: An EBS volume is locked to a single Availability Zone. A volume created in
us-east-1acan only attach to instances inus-east-1a. To move data to another AZ, you take a snapshot (a backup stored in S3) and create a new volume from it in the target AZ. See the snapshots page for how.
When to use EBS: any time you need persistent storage that outlives the instance, such as boot volumes, databases, or application data. When NOT to use it: for cheap, scratch data that you can lose on reboot, where the free local instance store is faster, or for shared file access across many instances, where EFS (Elastic File System) or FSx fit better.
Volume types — when to use which
EBS offers several volume types tuned for different price and performance needs. The two main families are SSD (solid-state, good for random reads and writes and low latency) and HDD (spinning disks, good for cheap, large, sequential throughput).
| Type | Family | Best for | Max IOPS | Max throughput | Notes |
|---|---|---|---|---|---|
| gp3 | SSD | Default choice for most workloads, boot volumes, dev/test, web apps | 16,000 | 1,000 MB/s | IOPS and throughput priced separately from size |
| gp2 | SSD | Legacy general-purpose; superseded by gp3 | 16,000 | 250 MB/s | IOPS tied to size; usually worth migrating off |
| io2 Block Express | SSD | Mission-critical databases needing high, guaranteed IOPS | 256,000 | 4,000 MB/s | Highest durability and performance, highest cost |
| io1 | SSD | Older provisioned-IOPS workloads | 64,000 | 1,000 MB/s | Largely replaced by io2 |
| st1 | HDD | Big sequential workloads: log processing, data warehouses, streaming | 500 | 500 MB/s | Throughput-optimized, cannot be a boot volume |
| sc1 | HDD | Cold, rarely accessed data where cost is everything | 250 | 250 MB/s | Cheapest per GB, cannot be a boot volume |
IOPS stands for input/output operations per second (how many small reads/writes the disk handles). Throughput is how many megabytes per second it moves for large sequential transfers.
Migrate gp2 to gp3. gp3 is roughly 20% cheaper per GB than gp2, and it lets you provision IOPS and throughput independently of volume size. With gp2, you had to grow the disk just to get more speed. Most gp2 volumes should be moved to gp3. You can change the type live with no downtime using
modify-volume.
Creating and attaching a volume
A new volume must be created in the same AZ as the instance you plan to attach it to.
Console steps
- Open the EC2 console and choose Volumes under Elastic Block Store in the left menu.
- Click Create volume.
- Set Volume type to
gp3, Size to the GiB you need (for example100), and pick the Availability Zone that matches your instance (for exampleus-east-1a). - Optionally raise IOPS (default 3,000) and Throughput (default 125 MB/s).
- Add a Name tag, then click Create volume.
- Select the new volume, choose Actions -> Attach volume, pick your instance, and confirm the device name (for example
/dev/sdf).
CLI equivalent
Create the volume:
aws ec2 create-volume \
--availability-zone us-east-1a \
--volume-type gp3 \
--size 100 \
--iops 3000 \
--throughput 125 \
--tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=app-data}]'
Output:
{
"VolumeId": "vol-0a1b2c3d4e5f67890",
"AvailabilityZone": "us-east-1a",
"VolumeType": "gp3",
"Size": 100,
"Iops": 3000,
"Throughput": 125,
"State": "creating",
"Encrypted": true
}
Attach it to a running instance:
aws ec2 attach-volume \
--volume-id vol-0a1b2c3d4e5f67890 \
--instance-id i-0a1b2c3d4e5f \
--device /dev/sdf
Output:
{
"VolumeId": "vol-0a1b2c3d4e5f67890",
"InstanceId": "i-0a1b2c3d4e5f",
"Device": "/dev/sdf",
"State": "attaching"
}
After attaching, the OS still needs a file system. On the instance, format and mount it once:
sudo mkfs -t xfs /dev/xvdf
sudo mkdir /data
sudo mount /dev/xvdf /data
AWS often presents
/dev/sdfto the OS as/dev/xvdf(or/dev/nvme1n1on Nitro instances). Runlsblkto see the real device name before formatting.
Migrating gp2 to gp3 with no downtime
You do not need to detach or stop the instance. A single modify-volume call switches the type while the volume stays attached and online.
aws ec2 modify-volume \
--volume-id vol-0a1b2c3d4e5f67890 \
--volume-type gp3 \
--iops 3000 \
--throughput 125
Output:
{
"VolumeModification": {
"VolumeId": "vol-0a1b2c3d4e5f67890",
"TargetVolumeType": "gp3",
"ModificationState": "modifying",
"Progress": 0
}
}
In the console, the same change is Actions -> Modify volume on the selected volume.
Defining an EBS volume in infrastructure as code
When you launch instances with CloudFormation, attach storage in the same template so the disk is created and wired up automatically.
Resources:
AppVolume:
Type: AWS::EC2::Volume
Properties:
AvailabilityZone: us-east-1a
VolumeType: gp3
Size: 100
Iops: 3000
Throughput: 125
Encrypted: true
Tags:
- Key: Name
Value: app-data
VolumeAttachment:
Type: AWS::EC2::VolumeAttachment
Properties:
InstanceId: i-0a1b2c3d4e5f
VolumeId: !Ref AppVolume
Device: /dev/sdf
Cost note
gp3 storage is about $0.08 per GiB-month in us-east-1, so a 100 GiB volume costs roughly $8 per month. You get 3,000 IOPS and 125 MB/s free at that tier; extra IOPS and throughput are billed separately but cheaply. Crucially, a volume keeps billing even while the instance is stopped, because the data is still stored. Delete volumes you no longer need, and watch for orphaned volumes left behind when instances are terminated.
Best practices
- Use gp3 as your default volume type, and migrate existing gp2 volumes to cut cost and tune performance independently.
- Enable encryption on every volume; it is free, and you can set it as an account default so it is never forgotten.
- Set DeleteOnTermination correctly:
truefor throwaway boot volumes,falsefor data you must keep. - Tag volumes with their owner, environment, and purpose so you can find and clean up orphans later.
- Take regular snapshots for backups and for moving data across Availability Zones.
- Right-size IOPS and throughput from real CloudWatch metrics rather than over-provisioning by guesswork.
- Run
lsblkbefore formatting so you never overwrite the wrong device.