TechEarl

How to Change an AWS EC2 Instance Type (Resize Without Data Loss)

Stop the instance, modify the instance type, start it. The exact gcloud-equivalent AWS CLI syntax, the compatibility matrix for moving between families and generations, the Nitro vs Xen gotcha, the instance-store data-loss trap, and the production sequence (snapshot AMI, scale out, replace) that gets you to a new instance type with zero downtime.

Ishan KarunaratneIshan Karunaratne⏱️ 20 min readUpdated
Change an EC2 instance type without data loss: stop the instance, run aws ec2 modify-instance-attribute, start it again. Covers Nitro vs Xen compatibility, ENA and NVMe driver requirements, the instance-store ephemeral-data trap, and the zero-downtime ASG rolling-replace pattern.

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.

Try it with your own values

Jump to:

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:

bash
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:

bash
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:

bash
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:

bash
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:

bash
aws ec2 stop-instances --region :region --instance-ids :instance_id

Wait for the state to settle:

bash
aws ec2 wait instance-stopped --region :region --instance-ids :instance_id

Modify the type:

bash
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:

bash
aws ec2 start-instances --region :region --instance-ids :instance_id
aws ec2 wait instance-running --region :region --instance-ids :instance_id

Verify the new type:

bash
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 stateStop instance. After it shows Stopped, ActionsInstance settingsChange instance type. Pick the new type from the dropdown. Save. Instance stateStart 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:

ConstraintRuleWhat happens if violated
AMI architecturex86_64 → x86_64, arm64 → arm64Instance fails to boot; status check 1/2 fail
VirtualizationHVM → HVM only (PV is end-of-life)New type rejects the AMI at launch
ENA supportRequired for c5, m5, r5, t3, m6, c6, r6, and every newer familyInstance starts but has no network
NVMe supportRequired for Nitro families (same list as ENA)Instance starts but cannot mount root EBS
TenancyDedicated host families cannot move to shared tenancy and vice versaAPI error before stop completes
Placement groupCluster placement requires compatible types (10 Gbps capable)API error on start
Family-specific featuresEFA (HPC), Local Zones, WavelengthValidation 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):

bash
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:

bash
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:

bash
aws ec2 modify-image-attribute \
  --region :region \
  --image-id ami-0newamiid \
  --ena-support

Step 4: Launch a fresh instance from the new AMI on the destination type:

bash
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-profile

Step 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:

bash
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:

bash
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:

bash
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.medium on a gp2 volume of less than 1 TB is throttled to ~3 IOPS per GB with a 3,000 IOPS burst. Switching the volume to gp3 gives you 3,000 IOPS baseline + provisioned IOPS up to 16,000 at lower cost than the equivalent gp2 upgrade. Often eliminates the need for a bigger instance.
  • EBS throughput is capped by instance type, not volume. A t3.large caps at 174 MB/s of aggregate EBS throughput regardless of how big the volume is. A m5.large allows 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 -h and 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.

OperationWhat it doesUse for resize?
StopPowers off, releases the underlying host, EBS volumes persist, instance-store volumes wipeYes
HibernateSaves RAM contents to the root EBS volume, then stopsNo: incompatible with modify-instance-attribute for type changes
TerminatePowers off and deletes the instance + (by default) the root EBS volumeNo: 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

ApproachDowntimeRiskWhen to use
Stop + modify + start30 to 90 secondsLow (one instance)Single dev / staging / batch instance
Snapshot AMI + relaunchNone on the original, then DNS cutoverLower (old instance stays up)Xen → Nitro jump, instance-store root, untested AMI
ASG rolling replaceNoneLowest (gated by health checks)Production fleets behind a load balancer
Terraform instance_type change30 to 90 seconds (default)Low if lifecycle configuredInfra-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

FAQ

TagsAWSEC2Instance TypesNitroDevOpsCLIAuto ScalingEBS
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years across software, Linux systems, DevOps, and infrastructure — and a more recent focus on AI. Currently Chief Technology Officer at a tech startup in the healthcare space.

Keep reading

Related posts

Connect to an AWS EC2 instance using plain SSH with a key pair, EC2 Instance Connect, AWS Systems Manager Session Manager, or an EC2 Instance Connect Endpoint for private instances. Default usernames, security group rules, and troubleshooting Permission denied and Connection timed out.

How to SSH into an AWS EC2 Instance

Connect to an EC2 instance four ways: plain SSH with a key pair, EC2 Instance Connect, Session Manager, and EC2 Instance Connect Endpoint. Default usernames, security group rules, and the troubleshooting matrix that fixes Permission denied and Connection timed out.

Grow an AWS EBS volume with zero downtime: aws ec2 modify-volume to enlarge, wait for the optimizing state, then sudo growpart to extend the partition and sudo resize2fs (ext4) or sudo xfs_growfs (XFS) to stretch the filesystem. No detach, no reboot, on a live EC2 instance.

How to Extend an AWS EBS Volume Without a Restart

Grow an EBS volume on a running EC2 instance in four steps. Modify the volume, wait for the optimizing state, expand the partition with growpart, then stretch the filesystem with resize2fs or xfs_growfs. No detach, no reboot.

Four reliable ways to change a WordPress password: admin dashboard, WP-CLI, direct in the database, or email reset. Includes the WP 6.8+ bcrypt hash format.

How to Change a WordPress Password

Four reliable ways to change a WordPress password: admin dashboard, WP-CLI, directly in the database with the correct phpass or bcrypt hash, and the lost-password email reset.