AWS EBS volumes grow online. The Elastic Volumes feature (GA February 2017) added the control-plane support; modern Nitro instances expose the new size to Linux within seconds. The whole operation is four commands once you know the device path: aws ec2 modify-volume to enlarge the volume at the EBS layer, aws ec2 describe-volumes-modifications to confirm the modification reached the optimizing state, sudo growpart to expand the partition, and sudo resize2fs (ext4) or sudo xfs_growfs (XFS) to stretch the filesystem. No detach, no reboot, no maintenance window. Below is the exact procedure, the verification commands to run before and after, the two caveats that bite people once (the 6-hour modification cooldown and the AWS-vs-Linux device naming mismatch), and a Console vs CLI vs Terraform comparison.
How do I extend an AWS EBS volume without rebooting?
Run aws ec2 modify-volume --volume-id vol-XXX --size 200 to enlarge the volume at the EBS layer. The control plane acknowledges the request immediately; the volume moves through modifying to optimizing to completed over the next few minutes. The new size is visible from inside the instance the moment the volume reaches optimizing, which usually happens within seconds. SSH into the instance and confirm the kernel sees it with lsblk. Expand the partition with sudo growpart /dev/nvme1n1 1 (note the space between the device and the partition number). Finally, stretch the filesystem: sudo resize2fs /dev/nvme1n1p1 for ext4 or sudo xfs_growfs /mountpoint for XFS. Verify with df -h. The entire flow runs against a live EC2 instance with active connections, services, and I/O. The only constraint is one modification per six hours on the same volume.
Jump to:
- Before you start: prerequisites and snapshot
- Step 1: Modify the volume
- Step 2: Wait for the optimizing state
- Step 3: Expand the partition
- Step 4: Resize the filesystem
- Verify the new size
- Detect the filesystem before you resize it
- Console vs AWS CLI vs Terraform
- Caveats and gotchas
- What to do next
- FAQ
Before you start: prerequisites and snapshot
You need an IAM principal with ec2:ModifyVolume and ec2:DescribeVolumesModifications. The instance can be running. The volume must be attached.
Elastic Volumes (the feature that makes online modification possible) has been GA since February 2017 and works on every modern instance type. The one prerequisite caveat is the cutoff date: volumes attached to instances before November 3, 2016 23:40 UTC need to be "initialized" by either detaching and reattaching the volume or stopping and starting the instance. For anything launched in the last several years this is not a concern.
Take a snapshot first. Snapshots are incremental, cost cents, and are the only single-API-call rollback if the partition table or filesystem goes sideways:
aws ec2 create-snapshot \
--region :region \
--volume-id :volume_id \
--description "pre-resize-$(date +%Y%m%d-%H%M)"A 100 GiB volume snapshot completes in seconds for incremental snapshots after the first. Mistakes on a live disk are not cheap.
Capture the current state so you have a before-and-after to compare:
df -hT
lsblkdf -hT shows mounted filesystems with their type and used/free space. lsblk shows the block-device tree (disk to partitions to filesystems) and is where you confirm the new size lands.
Step 1: Modify the volume
The AWS CLI one-liner:
aws ec2 modify-volume \
--region :region \
--volume-id :volume_id \
--size :new_size--size is in GiB. The response shows the modification state:
{
"VolumeModification": {
"VolumeId": "vol-0abc123def456",
"ModificationState": "modifying",
"TargetSize": 200,
"OriginalSize": 100,
"Progress": 0,
"StartTime": "2026-04-10T13:22:01.000Z"
}
}The same command also changes volume type, IOPS, or throughput. For example, converting a gp2 volume to gp3 with provisioned performance:
aws ec2 modify-volume \
--region :region \
--volume-id :volume_id \
--volume-type gp3 \
--iops 6000 \
--throughput 250 \
--size :new_sizeAll four attributes can change in one call. The control plane handles each independently.
Console method: EC2 console, Volumes, select the volume, Actions, Modify volume, change Size, Modify. Same effect, slower for batches.
Step 2: Wait for the optimizing state
The modification moves through three states: modifying, optimizing, completed.
modifyingis the early phase where AWS is provisioning the resize at the EBS backend. New size is not yet visible to the instance.optimizingis when the resize is functionally complete. The new size is visible to the instance from inside the OS. Performance characteristics may still be settling (especially for volume type changes), but the size operation itself is done.completedis fully settled. Volume reports its target configuration on every metric.
The new size is visible at optimizing, not at completed. Waiting for completed is unnecessary if all you care about is the size.
Poll the modification state:
aws ec2 describe-volumes-modifications \
--region :region \
--volume-ids :volume_id \
--query "VolumesModifications[0].ModificationState" \
--output textOr wait in a Bash while loop until it reaches optimizing:
while true; do
state=$(aws ec2 describe-volumes-modifications \
--region :region \
--volume-ids :volume_id \
--query "VolumesModifications[0].ModificationState" \
--output text)
[[ "$state" == "optimizing" || "$state" == "completed" ]] && break
sleep 5
doneFor gp3 size-only modifications the wait is usually under a minute. For volume type conversions (gp2 to gp3, or anything to io2 Block Express) the optimizing phase can take much longer as data is rebalanced across the EBS backend, but the size is usable as soon as the state transitions.
From inside the instance, confirm the kernel sees the new geometry:
lsblkThe disk size should match the new value. If it does not, the volume is still in modifying (wait a moment) or, very rarely, the kernel needs a nudge to rescan. On modern Nitro instances this rescan happens automatically. On older Xen instances you may need:
echo 1 | sudo tee /sys/class/block/xvdf/device/rescanThis is rare in practice; the auto-detect path is reliable on every current AMI.
Step 3: Expand the partition
The kernel now sees the larger disk, but the partition table still says "this partition ends at the old boundary." Fix that with growpart from the cloud-utils-growpart package (pre-installed on every official AWS AMI).
For a Nitro instance with a partitioned data volume:
sudo growpart :device :partitionTwo arguments, space-separated: the device (/dev/nvme1n1) and the partition number (1). The space matters. A common typo is growpart /dev/nvme1n1p1 (no space, partition path concatenated) which fails because growpart needs the disk and the partition index separately.
For an older Xen instance:
sudo growpart /dev/xvdf 1For the root volume on a Nitro instance (this is the common case for boot disks):
sudo growpart /dev/nvme0n1 1Output looks like:
CHANGED: partition=1 start=2048 old: size=209713119 end=209715167 new: size=419430399 end=419432447
If growpart is not installed (rare, but happens on minimal or custom images):
# Amazon Linux / RHEL / Rocky / CentOS
sudo dnf install -y cloud-utils-growpart
# Debian / Ubuntu
sudo apt-get update && sudo apt-get install -y cloud-guest-utilsFor volumes formatted as a whole-disk filesystem (no partition table, common on additional data volumes formatted with mkfs.ext4 /dev/nvme1n1 directly), skip this step and go straight to the filesystem resize against the raw device. lsblk tells you which model your disk is on: if the disk appears with no partitions underneath it, you have a whole-disk filesystem.
Step 4: Resize the filesystem
The partition is now the full size of the volume, but the filesystem inside the partition still believes it is the old size. The command depends on the filesystem type.
ext4 (Amazon Linux 2, Ubuntu, Debian, most legacy custom AMIs):
sudo resize2fs /dev/nvme1n1p1For a whole-disk ext4 (no partition):
sudo resize2fs :deviceresize2fs reads the device geometry and grows the ext4 superblock to match. Online resize on a mounted filesystem is supported and is what runs here. Expected output:
resize2fs 1.45.5 (07-Jan-2020)
Filesystem at /dev/nvme1n1p1 is mounted on /; on-line resizing required
old_desc_blocks = 13, new_desc_blocks = 25
The filesystem on /dev/nvme1n1p1 is now 52428795 (4k) blocks long.
XFS (Amazon Linux 2023 root volumes, RHEL 8+, Rocky, AlmaLinux):
sudo xfs_growfs /For a data volume mounted at /mnt/data:
sudo xfs_growfs /mnt/dataXFS resizes by mount point, not by device. XFS can only grow, never shrink, which matches the EBS modification constraint anyway. The -d flag (sudo xfs_growfs -d /) explicitly grows to fill the device; current xfs_growfs does that by default but the flag is harmless and shows up in older AWS documentation.
btrfs (uncommon on AWS, but supported):
sudo btrfs filesystem resize max /If you do not know the filesystem type, the next section has the detection commands.
Verify the new size
df -hT
lsblkdf -hT should now show the new free space on the mount point. lsblk should show the partition matching the volume size.
A complete before-and-after on a Nitro instance's data volume grown from 100 GiB to 200 GiB:
# Before
$ df -hT /mnt/data
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme1n1p1 ext4 98G 42G 52G 45% /mnt/data
# After
$ df -hT /mnt/data
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme1n1p1 ext4 196G 42G 149G 22% /mnt/data
Same Used, larger Size. That is the green light. Total wall-clock for the four commands on a typical data volume: under a minute, no reboot, active SSH session never drops.
Detect the filesystem before you resize it
Run any of these to identify the filesystem type and partition layout. Useful when you inherited an instance and do not know what shape its disk is in.
# Filesystem type per partition
lsblk -f
# Filesystem type and mount info for the root mount
df -hT /
# Disk and partition geometry
sudo parted -l
# Quick filesystem type lookup by device
sudo blkid /dev/nvme1n1p1lsblk -f is the fastest single command and shows everything: device tree, filesystem type, label, UUID, and mountpoint.
Cheat sheet for picking the resize command:
| Filesystem | Resize command |
|---|---|
| ext2 / ext3 / ext4 | sudo resize2fs /dev/DEVICE |
| XFS | sudo xfs_growfs MOUNT_POINT |
| btrfs | sudo btrfs filesystem resize max MOUNT_POINT |
| ZFS | sudo zpool online -e POOL DEVICE |
| FAT32 / NTFS | not online-resizable on Linux |
The first two cover ~99% of EC2 Linux instances.
Console vs AWS CLI vs Terraform
The resize itself is identical at the API layer. The interface choice is about your workflow.
| Approach | Best for | Command / action |
|---|---|---|
| EC2 Console | One-off resizes, exploring | Volumes, Actions, Modify volume |
| AWS CLI | Scripted, one-shot, ad-hoc batches | aws ec2 modify-volume --volume-id vol-XXX --size N |
| Terraform | Infra-as-code, audit trail, multi-environment | aws_ebs_volume.size = 200 then terraform apply |
| AWS CDK | Programmatic, app-aligned | volume.size = cdk.Size.gibibytes(200) |
| REST API | Programmatic, custom tooling | POST /?Action=ModifyVolume against the EC2 endpoint |
For Terraform, the attribute that changes is size (plus optionally iops, throughput, or type):
resource "aws_ebs_volume" "data" {
availability_zone = "us-east-1a"
size = 200 # was 100
type = "gp3"
iops = 3000
throughput = 125
encrypted = true
tags = {
Name = "data-disk-01"
}
}
Terraform performs the resize in place. It does NOT trigger a replacement, which is exactly what you want. The growpart and resize2fs steps still have to happen inside the instance, typically via a remote-exec provisioner, a separate Ansible run, or an AWS Systems Manager Run Command document.
The CDK equivalent in TypeScript:
const dataVolume = new ec2.Volume(this, "DataVolume", {
availabilityZone: "us-east-1a",
size: cdk.Size.gibibytes(200), // was 100
volumeType: ec2.EbsDeviceVolumeType.GP3,
encrypted: true,
});To batch-resize many volumes via the CLI, a Bash for loop over a volume-ID list is the simplest pattern:
for vol in vol-aaa vol-bbb vol-ccc; do
aws ec2 modify-volume --region :region --volume-id "$vol" --size :new_size
donePair with a Bash while loop and aws ec2 describe-volumes-modifications if you need to wait for each volume to reach optimizing before triggering the next instance-side step.
Caveats and gotchas
You cannot shrink an EBS volume. Ever. The only path to a smaller volume is: create a new smaller volume, copy data over, swap the attachment, delete the old one. Plan capacity with this in mind; over-provisioning is reversible only the hard way.
One modification per six hours per volume. This is the most-cited Elastic Volumes constraint and the one that bites teams who plan resizes without reading the limits. The 6-hour cooldown applies to the same volume; modifications to other volumes are independent. If you need to retry a failed modification or follow up with a second one, the cooldown clock starts at the moment the first modification reached modifying.
The new size is visible at the optimizing state, not completed. A common mistake is waiting for completed before running growpart. optimizing is sufficient and usually arrives within seconds for size-only changes. For volume type conversions (gp2 to gp3, or anything to io2 Block Express) optimizing can take hours, but the size is already usable.
Nitro vs Xen device naming. On Nitro instances (C5 and later, every Graviton, T3, T3a, M5, R5, and modern types) EBS volumes appear as /dev/nvmeXn1. On older Xen instances they appear as /dev/xvdX. The growpart command differs accordingly. See How to Add an EBS Volume to an EC2 Instance for the full mapping table.
Whole-disk filesystems skip the growpart step. If lsblk shows your volume as a single disk with no partition rows underneath (no nvme1n1p1, just nvme1n1), the filesystem was created on the raw device. Run resize2fs /dev/nvme1n1 or xfs_growfs /mnt/data directly; growpart is not needed and will fail with "must supply partition-number".
io2 Block Express has a different optimization path. Conversions between standard io2 and io2 Block Express (the high-IOPS variant) involve more substantial backend work. The size change is fast but the throughput rebalancing during optimizing can run for hours. Plan accordingly.
Custom AMIs may not auto-detect the new size at boot. Official AWS AMIs (Amazon Linux 2, Amazon Linux 2023, Ubuntu) ship cloud-init configured to grow the root partition and filesystem on first boot. Custom AMIs built from a non-cloud-init base do not. If you are on a custom image, the four-step procedure above is mandatory and an actual reboot will not save you.
Snapshots before, not regrets after. A snapshot is faster than typing this sentence and saves the volume in its current state. Cost is incremental-only, measured in cents. There is no excuse to skip it on production volumes.
Performance scales with size on some types. gp2 IOPS scales at 3 IOPS per GiB. Going from 100 GiB to 200 GiB doubles your IOPS ceiling. gp3 decouples IOPS from size, so a resize alone does not change performance there; modify --iops and --throughput explicitly. io1 and io2 are pure provisioned IOPS and unaffected by size in performance terms.
Don't run fdisk just because an old guide tells you to. Some older blog posts advise running sudo fdisk /dev/nvme0n1 and pressing w to "fix the GPT partition table" before growpart. This is unnecessary on modern AWS AMIs and risks corruption if you press the wrong key in fdisk's interactive mode. growpart handles the partition table update on its own.
What to do next
- The companion AWS articles: How to SSH into an AWS EC2 Instance for the connection side, and How to Add an EBS Volume to an EC2 Instance for the initial-attach procedure.
- The GCP equivalent for cross-cloud sysadmin work: How to Increase Google Cloud VM Disk Size Without Rebooting. Same idea, different control plane.
- The canonical reference: AWS's Request Amazon EBS volume modifications covers the modification states and CLI flow.
- Extend the file system after resizing an Amazon EBS volume is the upstream version of the growpart and filesystem-resize steps, with the Nitro vs Xen examples.
- How to Export All MySQL Databases: the "snapshot before resize" hygiene applies double when a database lives on the volume. Run a logical dump first.
- Bash For Loops and Bash While Loops: the patterns I use to batch-resize a fleet of volumes and poll for completion.
- How to Export and Import PuTTY Sessions and Settings: if you SSH from Windows into EC2 to run the OS-side commands, save your sessions before moving machines.
- The
growpartsource lives in the cloud-utils repo if you want the implementation details.





