Changing an EC2 instance type is one of those operations that looks trivial in the console (stop, edit, start) and yet bites people in three places: an instance-store volume that evaporates the moment you stop the instance, a Xen-era AMI that refuses to boot on a Nitro host, and a production fleet that takes the wrong kind of downtime because nobody set up an Auto Scaling Group with a rolling replace. This post is the full procedure for an EBS-backed instance, the CLI one-liners, the compatibility rules that decide whether the resize succeeds or fails, and the zero-downtime pattern I reach for on anything customer-facing.
How do I change an AWS EC2 instance type?
For an EBS-backed instance the procedure is three calls. Stop the instance: aws ec2 stop-instances --instance-ids i-0abc. Modify the type: aws ec2 modify-instance-attribute --instance-id i-0abc --instance-type '{"Value": "t3.large"}'. Start it again: aws ec2 start-instances --instance-ids i-0abc. The whole thing takes under a minute and the EBS root volume, attached EBS data volumes, security groups, IAM role, and Elastic IP all survive. The instance ID stays the same; the public IPv4 will change unless you use an Elastic IP. Instance-store volumes are wiped on stop and the resize requires migration to a fresh instance (snapshot the EBS root to an AMI, launch a new instance from the AMI on the new type). The destination type must be compatible with the AMI's architecture (x86_64 vs arm64), virtualization (HVM vs PV), network driver (ENA), and storage driver (NVMe). Modern Nitro families (C5/M5/R5/T3 and later) require ENA + NVMe; older Xen families do not. Moving from Xen to Nitro on an AMI that lacks those drivers fails at boot.
Type your AWS values once — every example below uses them.
Jump to:
- Before you start: the four pre-flight checks
- Method 1: Stop, modify, start (the standard path)
- Method 2: Console click-path
- Compatibility: what can move to what
- Method 3: Snapshot AMI + relaunch (Xen to Nitro, instance-store)
- Method 4: ASG rolling replace (zero downtime in production)
- Cost angle: vertical scale vs gp3 + smaller box
- Stop vs Hibernate vs Terminate
- Comparison: four ways to change an instance type
- Common pitfalls
- What to do next
- FAQ
Before you start: the four pre-flight checks
Four things to confirm before you touch the instance, in increasing order of how badly people get burned by missing them.
1. The root volume is EBS, not instance store. Instance store volumes are ephemeral local SSDs physically attached to the host. Stopping an instance-store-backed instance wipes the volume permanently. Check with:
aws ec2 describe-instances --region :region --instance-ids :instance_id \
--query 'Reservations[].Instances[].RootDeviceType'ebs is fine. instance-store means the standard stop-modify-start path is not available; see Method 3.
2. Anything important on the instance store is backed up. Even on EBS-rooted instances, larger families (m5d, r5d, i3, c5d) come with instance-store volumes attached as /dev/nvme1n1, /dev/nvme2n1, etc. Anything on those volumes is gone when you stop the instance. Snapshot or rsync the data first.
3. The destination instance type is in the same family architecture. x86_64 AMIs cannot boot on arm64 (Graviton) instances and vice versa. Check the AMI's architecture:
aws ec2 describe-images --region :region --image-ids ami-0abc \
--query 'Images[].Architecture'If you want to move from x86 to Graviton (or vice versa), you have to rebuild the image on the target architecture and migrate, not resize.
4. The AMI has the drivers the destination type requires. Nitro instance types (C5, M5, R5, T3, M6, C6, R6, M7, C7, and every newer family) require both ENA (network) and NVMe (block storage) drivers in the AMI. AMIs built before 2018 or based on legacy distributions may lack them. Check:
aws ec2 describe-instances --region :region --instance-ids :instance_id \
--query 'Reservations[].Instances[].EnaSupport'true means the network driver is loaded. For NVMe, run lsmod | grep nvme inside the instance before stopping it. If either is missing, the instance starts on the new type but fails to attach storage or come up on the network; you get a failed status check and have to revert.
Take a snapshot of the EBS root before you do anything destructive:
aws ec2 create-snapshot --region :region --volume-id vol-0abc \
--description "pre-resize-$(date +%Y%m%d-%H%M)"Snapshots are incremental, cost cents, and complete in seconds for a typical root volume. The only single-command rollback you have.
Method 1: Stop, modify, start (the standard path)
The three CLI calls, in order:
aws ec2 stop-instances --region :region --instance-ids :instance_idWait for the state to settle:
aws ec2 wait instance-stopped --region :region --instance-ids :instance_idModify the type:
aws ec2 modify-instance-attribute \
--region :region \
--instance-id :instance_id \
--instance-type '{"Value": ":new_type"}'The JSON quoting is awkward but exact. The CLI accepts the shorthand --instance-type t3.large on many recent versions too, but the documented form is the JSON Value wrapper and it works on every CLI v1 and v2 release since 2013.
Start the instance again:
aws ec2 start-instances --region :region --instance-ids :instance_id
aws ec2 wait instance-running --region :region --instance-ids :instance_idVerify the new type:
aws ec2 describe-instances --region :region --instance-ids :instance_id \
--query 'Reservations[].Instances[].InstanceType'Total wall-clock time on a typical EBS-rooted instance: 60 to 90 seconds. The instance ID is unchanged. The Elastic IP, if any, reattaches automatically. EBS volumes stay attached at the same device paths. Security groups, IAM instance profile, key pair, user data, and tags all persist.
If the destination type is not compatible (Xen-only AMI on a Nitro family, or a c5n that requires ENA when the AMI lacks it) start-instances returns the instance to running but the status check fails. Revert with the same sequence in reverse: stop, modify back to the original type, start.
Method 2: Console click-path
For the rare one-off where you don't want to type the CLI. EC2 → Instances → select the instance → Instance state → Stop instance. After it shows Stopped, Actions → Instance settings → Change instance type. Pick the new type from the dropdown. Save. Instance state → Start instance.
The console enforces the same compatibility rules as the CLI and refuses incompatible types with a slightly more readable error. Useful for exploring what types are available in the current region and AZ.
Compatibility: what can move to what
Not every type can move to every other type without rebuilding the AMI. The rules:
| Constraint | Rule | What happens if violated |
|---|---|---|
| AMI architecture | x86_64 → x86_64, arm64 → arm64 | Instance fails to boot; status check 1/2 fail |
| Virtualization | HVM → HVM only (PV is end-of-life) | New type rejects the AMI at launch |
| ENA support | Required for c5, m5, r5, t3, m6, c6, r6, and every newer family | Instance starts but has no network |
| NVMe support | Required for Nitro families (same list as ENA) | Instance starts but cannot mount root EBS |
| Tenancy | Dedicated host families cannot move to shared tenancy and vice versa | API error before stop completes |
| Placement group | Cluster placement requires compatible types (10 Gbps capable) | API error on start |
| Family-specific features | EFA (HPC), Local Zones, Wavelength | Validation error on modify |
The good cases: same family generation (t3.medium → t3.large), within a Nitro generation across families (m5.large → c5.large → r5.large), bumping a generation within an architecture (m5.large → m6i.large). The bad cases: old Xen family (m3, c3, m4, c4) to Nitro family on an AMI without ENA / NVMe; x86 to arm64 without rebuilding the image.
The safe play for any non-trivial jump is the snapshot-AMI-and-relaunch path in Method 3. It costs ten extra minutes but eliminates the "instance boots but has no network" failure mode.
Method 3: Snapshot AMI + relaunch (Xen to Nitro, instance-store)
When the destination type isn't compatible with the current AMI, the fix is to bake a new AMI from the running instance with ENA and NVMe enabled, then launch a fresh instance from that AMI on the new type. This is also the procedure for instance-store-rooted instances where stop-modify-start isn't available.
Step 1: Make sure ENA and NVMe modules are loaded inside the instance (Amazon Linux 2 / 2023, Ubuntu 18.04+, RHEL 7.4+ all ship them):
sudo modprobe ena
sudo modprobe nvme
lsmod | grep -E "ena|nvme"Step 2: Create an AMI from the running instance. EC2 quiesces I/O briefly to capture a consistent snapshot:
aws ec2 create-image \
--region :region \
--instance-id :instance_id \
--name "migration-$(date +%Y%m%d-%H%M)" \
--description "Pre-Nitro migration" \
--no-reboot--no-reboot skips the OS-level fsync that a clean shutdown gives you. For database-heavy instances drop the flag (or run sync manually first) so the snapshot is filesystem-consistent.
Step 3: Enable ENA on the new AMI explicitly. EC2 honors the source instance's ENA attribute, but for older AMIs the attribute may not propagate:
aws ec2 modify-image-attribute \
--region :region \
--image-id ami-0newamiid \
--ena-supportStep 4: Launch a fresh instance from the new AMI on the destination type:
aws ec2 run-instances \
--region :region \
--image-id ami-0newamiid \
--instance-type :new_type \
--subnet-id subnet-0abc \
--security-group-ids sg-0abc \
--key-name my-key \
--iam-instance-profile Name=my-instance-profileStep 5: Reattach Elastic IPs, update DNS / load balancer targets, terminate the old instance after verification.
This is the "no-shortcut" path. It costs an AMI creation (a few cents and 5 to 15 minutes), plus the overlap during which both instances run. Worth it for anything where a failed start-instances would cause customer-visible downtime.
Method 4: ASG rolling replace (zero downtime in production)
For production fleets, the right pattern is to push the new instance type into the Launch Template and let the Auto Scaling Group roll instances one at a time. Zero downtime, full health-check gating, automatic rollback if the new type fails its target-group health check.
Update the launch template with the new instance type:
aws ec2 create-launch-template-version \
--region :region \
--launch-template-id lt-0abc \
--source-version 1 \
--launch-template-data '{"InstanceType":":new_type"}'Point the ASG at the new version:
aws autoscaling update-auto-scaling-group \
--region :region \
--auto-scaling-group-name web-prod \
--launch-template "LaunchTemplateId=lt-0abc,Version=2"Trigger the rolling replace. The instance-refresh API replaces instances in batches while respecting min-healthy-percentage:
aws autoscaling start-instance-refresh \
--region :region \
--auto-scaling-group-name web-prod \
--preferences '{"MinHealthyPercentage":90,"InstanceWarmup":120}'MinHealthyPercentage: 90 means the ASG keeps at least 90% of the fleet in service during the rollover. InstanceWarmup is how long a new instance has to pass health checks before the next batch starts. The ASG launches a new instance on the new type, waits for the target group to mark it healthy, then drains the old instance and terminates it. Repeat until the whole fleet runs on the new type.
If the new type fails health checks (network missing because ENA isn't loaded, NVMe driver missing) the refresh halts automatically and the existing instances stay in service. Roll back by updating the launch template back to the previous version and starting another refresh.
This is the only pattern I run on customer-facing production. For internal tools and batch workers, Method 1 is fine.
Cost angle: vertical scale vs gp3 + smaller box
Before vertically scaling an instance because it's running out of capacity, check what's actually constrained. Three common findings turn a "bigger instance" decision into a cheaper "different EBS" decision:
- Disk IOPS is the bottleneck, not CPU. A
t3.mediumon agp2volume of less than 1 TB is throttled to ~3 IOPS per GB with a 3,000 IOPS burst. Switching the volume togp3gives you 3,000 IOPS / 125 MiB/s baseline plus provisioned IOPS up to 80,000 and throughput up to 2,000 MiB/s, at lower cost than the equivalentgp2upgrade. Often eliminates the need for a bigger instance. - EBS throughput is capped by instance type, not volume. A
t3.largecaps at 174 MB/s of aggregate EBS throughput regardless of how big the volume is. Am5.largeallows 593 MB/s on the same volume. Sometimes the right answer is a slightly bigger instance with the same disk, not a bigger disk on the same instance. - Memory pressure looks like CPU pressure. A small instance under memory pressure starts swapping (or OOM-killing); the visible symptom is CPU saturation. Run
free -hand check swap usage before assuming you need more vCPU.
The general order: profile first, switch gp2 to gp3 second, vertically scale third.
Stop vs Hibernate vs Terminate
Three operations look similar in the console; only one is right for changing an instance type.
| Operation | What it does | Use for resize? |
|---|---|---|
| Stop | Powers off, releases the underlying host, EBS volumes persist, instance-store volumes wipe | Yes |
| Hibernate | Saves RAM contents to the root EBS volume, then stops | No: incompatible with modify-instance-attribute for type changes |
| Terminate | Powers off and deletes the instance + (by default) the root EBS volume | No: this is destructive |
Stop is the correct verb for a resize. Hibernate preserves RAM state, which is useful for warm-restart workflows but conflicts with the instance-type change because the saved RAM image is tied to the original instance's CPU and memory shape. Terminate is for "I'm done with this instance forever," not "I want to make it bigger."
Comparison: four ways to change an instance type
| Approach | Downtime | Risk | When to use |
|---|---|---|---|
| Stop + modify + start | 30 to 90 seconds | Low (one instance) | Single dev / staging / batch instance |
| Snapshot AMI + relaunch | None on the original, then DNS cutover | Lower (old instance stays up) | Xen → Nitro jump, instance-store root, untested AMI |
| ASG rolling replace | None | Lowest (gated by health checks) | Production fleets behind a load balancer |
Terraform instance_type change | 30 to 90 seconds (default) | Low if lifecycle configured | Infra-as-code estates; pair with ASG for prod |
Terraform note: changing instance_type on an aws_instance resource performs an in-place modification (stop, modify, start), not a replacement. The instance ID stays the same. Confirm by running terraform plan first; the line should read ~ instance_type (modify) not -/+ (replace). If it reads replace, an unrelated attribute is forcing a new resource; look at the diff above to find which one.
Common pitfalls
1. Instance store data loss on stop. Anything on /dev/nvme1n1 or higher (the instance-store volumes on m5d, r5d, c5d, i3, x1, x2) is wiped the moment you stop the instance. EBS root survives. Instance store does not. Back up first.
2. Public IPv4 changes after stop. Unless you have an Elastic IP, the public IPv4 is released on stop and reassigned (from a different pool) on start. DNS records pointing at the old IP break. Either use an Elastic IP for anything where the IP matters, or update DNS as part of the resize procedure.
3. ENA / NVMe missing on the AMI. The instance boots, fails its 2/2 status check, and you spend 20 minutes wondering why. Either install the drivers in the AMI before the resize, or use Method 3 and bake a new AMI explicitly.
4. Burst credits reset on stop for T-family. T3, T3a, T4g instances accumulate CPU credits while running and burn them during spikes. Stop resets the accumulated credit balance to zero (Unlimited mode billing continues). Right after a T-family resize, expect cooler baseline performance for a few hours while credits rebuild.
5. Reserved Instance and Savings Plan coverage. If the instance was covered by a Reserved Instance for the old type, the RI does not automatically cover the new type. Convertible RIs can be exchanged; Standard RIs cannot. Savings Plans (Compute Savings Plans specifically) cover any instance type in the family region, so they're more forgiving for resize-heavy fleets.
6. Dedicated tenancy and dedicated hosts. A shared-tenancy instance cannot be modified into a dedicated-tenancy instance and vice versa. The error message reads "InvalidInstanceAttributeValue" with no further explanation; check tenancy first.
7. Placement group families. Cluster placement groups require 10 Gbps capable instance types. Trying to modify into a type that doesn't qualify (older T family, for example) fails the start. Spread placement groups have laxer rules. Partition placement groups have the same general constraint as cluster.
8. --instance-type JSON quoting on Windows / PowerShell. The '{"Value": "t3.large"}' JSON needs different quoting in PowerShell. Use --instance-type "{\"Value\": \"t3.large\"}" or the shorthand --instance-type t3.large if your CLI version supports it.
What to do next
- SSH into an EC2 instance for verifying the resize from inside the box.
- Add an EBS volume to an EC2 instance when "bigger disk" turns out to be a better fix than "bigger instance."
- Extend an EBS volume without restart for growing an existing volume in place.
- AWS IAM policy examples for the EC2 admin and pass-role policies the resize procedure needs.
- AWS S3 cp and sync cheat sheet for syncing instance-store data to S3 before the stop.
- Cross-cloud: Increase a Google Cloud VM disk size without rebooting, GCP add persistent disk to VM, SSH into a GCP VM without gcloud.
- Scripting: Bash for loop and Bash while loop for batch-resizing a fleet of instances or polling the
wait instance-runningstate. - SSH cheat sheet for the connection-survival flags worth setting during a stop / start cycle.
- External: AWS EC2 instance type changes (UserGuide), Compatibility for changing the instance type, aws ec2 modify-instance-attribute reference.
FAQ
No. The modify-instance-attribute API requires the instance to be in the stopped state before the instance type can be changed. A running instance returns IncorrectInstanceState.
The closest thing to a zero-downtime resize is the ASG rolling-replace pattern: push the new instance type into the launch template, then run start-instance-refresh. The Auto Scaling Group launches new instances on the new type, waits for health checks, and drains the old instances one batch at a time. The application never goes down, but no individual instance is "resized" in place.
EBS-backed root and data volumes survive a stop-modify-start cycle. The volumes detach from the host during stop and reattach when the instance starts on the new type. File contents, partition tables, mount points, and device names all persist.
Instance-store volumes do not survive. Anything on /dev/nvme1n1 and higher (the local SSDs on m5d, r5d, c5d, i3, and similar families) is wiped permanently the moment the instance stops. Snapshot or rsync that data to EBS or S3 before resizing.
Three commands in order: aws ec2 stop-instances --instance-ids i-0abc, then aws ec2 modify-instance-attribute --instance-id i-0abc --instance-type '{"Value": "t3.large"}', then aws ec2 start-instances --instance-ids i-0abc. Note the JSON wrapper around the type value; this is the documented form and works on every CLI version since 2013.
Use aws ec2 wait instance-stopped and aws ec2 wait instance-running between the steps if you're scripting the resize. The wait commands block until the state transition completes, which avoids a race where modify-instance-attribute fires before the instance has actually stopped.
Yes, but only if the AMI has the ENA network driver and NVMe block-storage driver loaded. Nitro families (C5, M5, R5, T3 and every newer family) use ENA for network and NVMe for EBS attachment, while older Xen families (M3, C3, M4, C4) use the Xen drivers.
If the AMI lacks either driver, the instance boots on the Nitro type but fails its status check (no network, no root volume). The safe path is the snapshot-AMI-and-relaunch flow: install the drivers, bake a new AMI, set the ena-support attribute explicitly with modify-image-attribute, then launch a fresh instance on the Nitro type from the new AMI.
Yes, as long as the destination type meets the AMI's compatibility requirements and the workload fits in the smaller memory footprint. Downsizes from m5.xlarge to m5.large, c5.2xlarge to c5.large, and t3.large to t3.medium all work with the same stop-modify-start procedure.
The constraint that catches people is memory: an instance that has been running for weeks with a 6 GB resident set cannot suddenly fit on a 4 GB instance. The instance starts on the new type, but the workload OOM-kills under load. Check the actual memory usage (not the allocated heap) before downsizing.
Yes, if the instance has only a default AWS-assigned public IPv4. The address is released on stop and reassigned from the regional pool on start; the new address is almost never the same as the old one.
An Elastic IP avoids this. The Elastic IP detaches from the instance during stop and reattaches automatically on start, so the public IPv4 is stable across resize operations. Private IPv4 addresses inside the VPC are always stable. IPv6 addresses, when configured, are also stable across stop-start.
For an EBS-backed instance, the full stop-modify-start sequence completes in 30 to 90 seconds. The stop takes the longest (15 to 60 seconds depending on the OS's shutdown hooks); the modify is instant; the start completes in 10 to 30 seconds.
Status checks (2/2) typically take another 60 to 120 seconds to pass after start. Application-level warmup (JIT compilers, in-memory caches, connection pools) can take longer, which is why production resizes go through an ASG rolling replace with an InstanceWarmup period before the next instance is drained.
Modify (the path described in this post) preserves the instance ID, EBS volume attachments, security groups, IAM role, key pair, tags, user data, and Elastic IP. The instance is the same instance afterward, just on different hardware.
Replace (launch a new instance from an AMI, terminate the old one) produces a new instance ID and requires reattaching anything that's instance-ID-scoped. Replacement is what the snapshot-AMI-and-relaunch and ASG rolling-replace patterns both do. Modify is faster for single instances; replace is required when the destination type is not driver-compatible with the current AMI.
Sources
Authoritative references this article was fact-checked against.
- AWS news — gp3 size + provisioned performance updateaws.amazon.com
- AWS EC2 — general purpose instance types (docs)docs.aws.amazon.com





