An EBS volume is more than a fixed-size disk. The type you choose sets its performance and cost, snapshots give you cheap point-in-time backups, and encryption protects the data at rest - all controlled from the SDK.
This page shows how to create the right kind of volume, automate snapshots, restore a volume from a snapshot (into any AZ or type), and encrypt with KMS. Every operation uses @aws-sdk/client-ec2, because EBS is part of EC2.
Create an encrypted io2 volume for a database, snapshot it with tags, wait for the snapshot to complete, then restore it as a gp3 volume in a different AZ.
# --- Python (boto3) ---import boto3ec2 = boto3.client("ec2")# 1. High-IOPS, encrypted io2 volume for a database.vol = ec2.create_volume( AvailabilityZone="us-east-1a", Size=200, VolumeType="io2", Iops=10000, Encrypted=True, # KmsKeyId optional; omit to use the default EBS key TagSpecifications=[{ "ResourceType": "volume", "Tags": [{"Key": "Name", "Value": "db-data"}], }],)ec2.get_waiter("volume_available").wait(VolumeIds=[vol["VolumeId"]])# 2. Point-in-time snapshot (incremental, stored in S3).snap = ec2.create_snapshot( VolumeId=vol["VolumeId"], Description="db-data nightly", TagSpecifications=[{ "ResourceType": "snapshot", "Tags": [{"Key": "Name", "Value": "db-data-snap"}], }],)ec2.get_waiter("snapshot_completed").wait(SnapshotIds=[snap["SnapshotId"]])# 3. Restore into a DIFFERENT AZ and type - the snapshot carries the encryption.restored = ec2.create_volume( AvailabilityZone="us-east-1b", SnapshotId=snap["SnapshotId"], VolumeType="gp3", # restore can change the type)print("restored", restored["VolumeId"], "encrypted:", restored["Encrypted"])
// --- TypeScript (AWS SDK v3) ---import { EC2Client, CreateVolumeCommand, CreateSnapshotCommand, waitUntilVolumeAvailable, waitUntilSnapshotCompleted,} from "@aws-sdk/client-ec2";const ec2 = new EC2Client({});// 1. High-IOPS, encrypted io2 volume for a database.const vol = await ec2.send(new CreateVolumeCommand({ AvailabilityZone: "us-east-1a", Size: 200, VolumeType: "io2", Iops: 10000, Encrypted: true, // KmsKeyId optional; omit to use the default EBS key TagSpecifications: [{ ResourceType: "volume", Tags: [{ Key: "Name", Value: "db-data" }] }],}));await waitUntilVolumeAvailable({ client: ec2, maxWaitTime: 180 }, { VolumeIds: [vol.VolumeId!] });// 2. Point-in-time snapshot (incremental, stored in S3).const snap = await ec2.send(new CreateSnapshotCommand({ VolumeId: vol.VolumeId, Description: "db-data nightly", TagSpecifications: [{ ResourceType: "snapshot", Tags: [{ Key: "Name", Value: "db-data-snap" }] }],}));await waitUntilSnapshotCompleted({ client: ec2, maxWaitTime: 600 }, { SnapshotIds: [snap.SnapshotId!] });// 3. Restore into a DIFFERENT AZ and type - the snapshot carries the encryption.const restored = await ec2.send(new CreateVolumeCommand({ AvailabilityZone: "us-east-1b", SnapshotId: snap.SnapshotId, VolumeType: "gp3", // restore can change the type}));console.log("restored", restored.VolumeId, "encrypted:", restored.Encrypted);
What this demonstrates:
io2 with Iops for a latency-sensitive database; Encrypted: true for at-rest protection.
TagSpecifications names both the volume and the snapshot at creation.
The snapshot_completed waiter blocks until the backup is durable before you rely on it.
A restore can change AZ and type, and an encrypted snapshot yields an encrypted volume automatically.
IOPS and throughput set independently of size - the modern default
gp2
SSD
Legacy general workloads
IOPS scale with size; prefer gp3 for new volumes
io2 / io2 Block Express
SSD
Databases needing high, sustained IOPS
Highest durability and IOPS; supports Multi-Attach
io1
SSD
Older high-IOPS workloads
Superseded by io2 for durability
st1
HDD
Big sequential throughput (logs, data lakes)
Throughput-optimized, not for small random I/O
sc1
HDD
Cold, infrequently accessed data
Cheapest, lowest throughput
The key gp3 advantage: Iops (up to 16,000) and Throughput (up to 1,000 MiB/s) are dials separate from Size, so you do not over-provision capacity just to buy performance the way gp2 forced you to.
A snapshot is a point-in-time copy of a volume stored in S3 (in an AWS-managed bucket you do not see).
Snapshots are incremental: the first captures every block, and each later snapshot stores only blocks that changed. You still restore a full volume from any single snapshot.
Deleting a snapshot only frees blocks not referenced by another snapshot, so the incremental chain stays consistent.
DescribeSnapshots with OwnerIds=["self"] and filters lists your snapshots for lifecycle or cleanup logic.
Set Encrypted: true at create time. Without KmsKeyId, EBS uses the AWS-managed default key (aws/ebs); pass a KmsKeyId for a customer-managed key with your own rotation and access policy.
Encryption is transparent - the instance reads and writes plaintext; AWS encrypts and decrypts blocks in the EBS layer.
A snapshot of an encrypted volume is encrypted with the same key, and any volume restored from it is encrypted. The chain preserves protection end to end.
You can enable EBS encryption by default at the account/region level so every new volume is encrypted whether or not the caller sets the flag.
You cannot encrypt an existing unencrypted volume in place. There is no "turn on encryption" call. Fix: snapshot it, copy the snapshot with CopySnapshot and Encrypted: true, then create a new volume from the encrypted snapshot.
Snapshots are not instantly usable.CreateSnapshot returns while the snapshot is still pending. Fix: wait on snapshot_completed / waitUntilSnapshotCompleted before restoring from or deleting it.
Restoring needs the AZ, not just the snapshot.CreateVolume from a snapshot still requires an AvailabilityZone. Fix: always pass the target AZ, which can differ from the source.
Iops/Throughput are not valid on every type. Setting Throughput on io2, or Iops on gp2, is rejected. Fix: only set Iops on gp3/io1/io2 and Throughput on gp3.
A crash-consistent snapshot may miss in-flight writes. Snapshotting a busy volume captures disk state, not application state. Fix: flush/quiesce the filesystem (or stop writes) before snapshotting data that must be transactionally consistent.
Deleting a volume does not delete its snapshots. Snapshots outlive the volume and keep billing. Fix: track and expire snapshots explicitly, or use Amazon Data Lifecycle Manager.
gp3. It is cheaper than gp2 and lets you set IOPS and throughput independently of size. Move to io2 only when you need sustained high IOPS or the highest durability, and to st1/sc1 for throughput-heavy or cold data.
Are snapshots full copies or incremental?
Incremental. The first snapshot copies all written blocks; later ones store only changed blocks. You can still restore a complete volume from any single snapshot - the incremental nature is a storage/cost detail, not a restore limitation.
Can I restore a snapshot into a different Availability Zone?
Yes. CreateVolume with a SnapshotId takes an AvailabilityZone, which can differ from the source. This is exactly how you move block data across AZs, since a volume itself cannot cross AZ boundaries.
Can I change the volume type when restoring?
Yes. The restored volume's VolumeType (and size, within limits) is independent of the source volume. You can snapshot an io2 and restore it as gp3, for example.
How do I encrypt a volume that already exists unencrypted?
You cannot toggle it in place. Snapshot the volume, CopySnapshot with Encrypted: true (and optionally a KmsKeyId), then create a new volume from that encrypted snapshot and swap it in.
Does encryption slow the volume down?
Not meaningfully. Encryption and decryption happen in the EBS layer and are designed to have negligible impact on IOPS and latency for supported instance types.
What is the default KMS key if I do not pass one?
The AWS-managed aws/ebs key. Pass a KmsKeyId to use a customer-managed key when you need your own key policy, grants, or rotation schedule.
Do snapshots get deleted with the volume?
No. Snapshots are independent objects that survive volume deletion and keep incurring storage cost. Expire them yourself or let Data Lifecycle Manager / AWS Backup manage retention.
How do I automate a nightly snapshot without writing a scheduler?
Use Amazon Data Lifecycle Manager (DLM) or AWS Backup. Both create tag-targeted schedules with retention rules, so you avoid hand-rolling CreateSnapshot on a cron.
Why did setting Throughput on my io2 volume fail?
Throughput is only valid on gp3. On io2/io1 you control performance with Iops. Match the parameter to the type, or the API rejects the request.