PassionWavesMedia
← All articles
AWS · 7 min read · 08 Aug 2026

Upgrading EC2 from M4.xlarge to M8i.xlarge

What we learned upgrading a long-running Windows EC2 instance four hardware generations at once, the AD outage it caused, and how we'd plan it differently next time.

Photo by Christina @ wocintechchat.com on Unsplash Photo by Christina @ wocintechchat.com on Unsplash

If you started an AWS project ten years ago, the instance type you picked back then is still sitting there today unless someone's actively gone back to change it. In that time, AWS has shipped somewhere around ten generations of new hardware in that same family. It's the equivalent of buying a new computer with a far better graphics card for roughly the same price you paid a decade ago: same budget, dramatically more performance. Faster applications, more headroom for memory or CPU spikes, and often a lower bill than the legacy generation you're still paying for.

That last part surprises people. Newer generations aren't just faster, they're frequently cheaper per hour than the older hardware they replace, because AWS has less incentive to keep discounting instance families nobody's buying anymore. Set a regular cadence with your team, quarterly is reasonable, to check what AWS has released and whether it's worth moving to. Treat it as a cost review as much as a performance one.

How to choose what to upgrade to

Before picking a target instance type, understand what your workload actually needs. Don't upgrade for the sake of upgrading. A few questions worth answering with real data, not guesses:

  • What does the CPU, memory, and network usage look like over the last several months, not just the last week?
  • Is usage steady, or does it spike at predictable times (a batch job, a reporting window, a traffic pattern tied to when your users are actually online)?
  • How does the application behave under peak load today? Does it degrade gracefully, or does it fall over?
  • Is this workload closer to general purpose, or does it lean hard into one resource (memory-optimized, compute-optimized, storage-optimized)?

That last question is also where the "flex" instance types come in, and it's worth thinking through deliberately rather than defaulting to whichever one sounds newer. AWS's flex variants (like M8i-flex) are built for workloads with a variable baseline: they're priced to reward bursty, uneven usage. If your peak load is a predictable batch window, like a bulk data reload at a set time, a flex instance can be a genuinely good fit. But if you're running a long-lived production system you've operated for years and you're not willing to bet its steady-state performance on a burstable pricing model you haven't tested yet, the standard instance type is the safer call. We were comfortable with the cost difference, so we optimized for predictability over squeezing out the last bit of savings.

Once you know the shape of the workload, price it out. Compare on-demand, Reserved Instance, and Savings Plan pricing for the instance type you're targeting, not just the sticker price of on-demand. Then write the upgrade plan down: what's changing, what the expected impact is, what the maintenance window looks like, and get it in front of whoever needs to sign off before you touch anything running in production.

Our story

Our metrics showed we were capping out on CPU and memory on an m4.xlarge that had been running, untouched, for years. When we looked at what AWS now offers in that family, the current generation was four generations ahead of what we were on, and the underlying processor itself was roughly ten generations newer. The cost difference was negligible, a small hourly increase, not something that changed the math at all. It was an easy call.

We seriously considered m8i-flex.xlarge first, since we do have a recurring peak: a scheduled reload task that pulls in a large batch of data at a set time each day. But this instance had been running our dev and production environment for a long time without incident, and we didn't want to introduce a burstable performance model we hadn't tested on a workload we couldn't afford to destabilize. We were fine paying for constant, predictable specs, so we settled on the standard m8i.xlarge instead.

Since this was our first time doing a cross-generation upgrade like this, we opened an AWS Support ticket to confirm the right procedure. That's where we learned the part that actually made this upgrade non-trivial: the hypervisor underneath changes. Our m4.xlarge ran on the older Xen hypervisor; m8i.xlarge runs on the AWS Nitro System. That shift meant the instance needed new device drivers before the change would work cleanly, specifically the Elastic Network Adapter (ENA) driver and the NVMe driver, since Nitro-based instances handle networking and storage differently at the hardware level.

The upgrade itself

With the plan approved, here's what we actually did:

  1. Took an AMI snapshot of the instance and a separate backup of its attached EBS volume, so we had a clean rollback point.
  2. Stopped the instance over a weekend maintenance window, once we'd confirmed no users were active.
  3. Installed the ENA and NVMe drivers while it was stopped.
  4. Changed the instance type to m8i.xlarge and started it back up.
  5. Ran validation checks and rebooted.

After the reboot, things broke. We started seeing 504 Gateway errors, and the load balancer's target group reported the instance as unhealthy. The root cause turned out to be networking: the switch to Nitro created new network adapters on the instance, and those new adapters weren't pointed at our AWS Managed Microsoft AD the way the old ones had been. The instance had effectively lost its connection to the directory it depended on. Once we reconfigured the new adapters to point at the AD, health checks passed and everything came back up cleanly.

Our rollback plan, if any of this had gone sideways, was to revert to m4.xlarge from the snapshot and confirm the application still worked before trying again. We didn't need it, but having it written down before we started is what made the maintenance window low-stress instead of stressful.

The actual takeaway

The performance and cost case for upgrading is almost always there, AWS ships new generations too often for it not to be. The part that actually requires care is the boundary you're crossing underneath the instance type name: a hypervisor change means new drivers, and anything tied to networking (AD, load balancer health checks, static routes) needs to be re-verified after the swap, not assumed to carry over. Snapshot first, test the driver and networking behavior deliberately, and have a rollback plan written down before you touch anything in production.