AWS Compute

Choosing the Right EC2 Instance Type: Families, Naming & Right-Sizing

You need to run something on a server in AWS, and the console offers you a dropdown with over 800 EC2 instance typest3.micro, m7g.2xlarge, c7i.4xlarge, r7iz.8xlarge, i4i.metal, p5.48xlarge. Nobody explains what the letters and numbers mean, so most people pick t2.micro because it’s free, or m5.large because a blog said so, and then either fight CPU throttling for months or burn money on a box that idles at 8% all day. Both mistakes are avoidable once you can read the name and match a workload to a family.

An EC2 instance type is just a fixed recipe of virtual hardware: a number of vCPUs, an amount of memory, a processor (Intel, AMD, or AWS’s own Graviton ARM chips), a network bandwidth ceiling, an EBS bandwidth ceiling, and sometimes local NVMe disks or GPUs. The name encodes most of that in a handful of characters. m7g.2xlarge reads as family m (general purpose) · generation 7 · attribute g (Graviton) · size 2xlarge (8 vCPU / 32 GiB). Learn that grammar and the 800-item dropdown collapses into a short decision: what shape is my workload, which family matches that shape, and how big does it need to be?

This guide teaches the whole grammar. You will decode the name character by character, walk every family with real specs and use-cases, choose between Graviton, Intel and AMD on price and performance, understand the Nitro system that underpins modern instances, and then right-size with Compute Optimizer and CloudWatch so you stop paying for capacity you never use. There’s a copy-pasteable lab that uses aws ec2 describe-instance-types to pick a type, launches it, reads the right-sizing signals, changes the type, and tears everything down — in both the CLI and Terraform. Purchasing models (On-Demand, Spot, Savings Plans, Reserved) are a separate axis from which type — they’re covered in their own article; here we focus entirely on picking the right hardware shape.

What problem this solves

Picking an instance type wrong costs you in two directions at once, and both are silent for weeks.

Over-provisioning is the common one. You size for a peak you imagined, ship an m5.4xlarge (16 vCPU / 64 GiB) for a service that runs at 6% CPU and 20% memory, and it costs the same whether it serves a million requests or zero — roughly $490/month in us-east-1 for a box doing the work of a large. Multiply by a fleet and by every environment (dev, staging, prod) and over-provisioning is usually the single biggest line on an EC2 bill. Nobody notices because the app works; it just costs 6x what it should.

Under-provisioning is nastier because it looks like an application bug. A T burstable instance runs out of CPU credits and gets throttled to a 10-20% baseline, so your API latency triples at 3pm every day and you spend a week blaming the code. A memory-optimized job lands on a compute-optimized box and the kernel OOM-kills it. An EBS-bandwidth-bound database saturates the instance’s storage ceiling — not the volume’s — and no amount of CPU or IOPS on the volume helps. A Graviton binary won’t start on the ARM box because it was compiled for x86. Each of these is a wrong-type problem masquerading as something else, and each has a specific fingerprint you’ll learn to spot.

Everyone who runs EC2 hits this: developers launching a first instance, architects standardising a fleet, SREs chasing a latency regression, and finance asking why compute doubled. Getting the type right the first time — and re-checking it with data every quarter — is the difference between a predictable bill and a mystery. It’s also squarely on the exams: CLF-C02 expects you to know the families and burstable behaviour, and SAA-C03 expects you to right-size and to reason about Graviton, Nitro, and storage-optimized trade-offs.

Learning objectives

By the end of this article you can:

Prerequisites & where this fits

You should have an AWS account, the AWS CLI v2 configured (aws configure), and permission to launch and describe EC2. Basic comfort with a VPC (a default VPC is fine), a security group, and an SSH key pair is assumed — if you’ve never launched an instance, start with Launch Your First EC2 Instance and SSH In and come back. You don’t need to know Terraform to read the CLI path, but the IaC snippets use the hashicorp/aws provider.

Where this sits: choosing a type is step one of running any server workload. It comes after you’ve decided EC2 is the right service at all — if you’re still weighing EC2 against Lambda, ECS and EKS, read AWS Compute: EC2, Lambda, ECS and EKS — Which One to Choose? first. It comes before you bootstrap the box with startup scripts (EC2 User Data and cloud-init Bootstrapping), put it behind an Auto Scaling group and load balancer, or commit to a purchasing model. Type selection and purchasing model are independent axes: you pick the shape here (this article) and the commitment — On-Demand, Spot, Savings Plans, Reserved — separately. A c7g.xlarge is the same hardware whether you pay On-Demand or under a Savings Plan.

You are here in the workflow Covered here? Where it lives
Is EC2 even the right service? No EC2 vs Lambda vs ECS vs EKS
Which instance type / family / size? Yes This article
Which CPU architecture (Graviton/Intel/AMD)? Yes This article
Right-size an existing instance Yes This article
How do I pay for it (On-Demand/Spot/SP/RI)? No — separate axis Purchasing models article
Bootstrap the OS on boot No User data / cloud-init article
Scale a fleet automatically No Auto Scaling / ALB article

Core concepts

Before the families, pin down the vocabulary. Every instance type is defined by these dimensions, and the whole art of choosing is matching them to your workload.

Term What it means Why it decides your choice
vCPU A virtual CPU — one thread of a physical core (2 vCPUs ≈ 1 hyper-threaded core on x86; on Graviton 1 vCPU = 1 physical core, no SMT) Compute-bound work needs more vCPUs; the count sets the base price
Memory (RAM) GiB of RAM allocated to the instance Caches, JVMs, in-memory DBs, analytics need memory; drives the family (C/M/R)
CPU:memory ratio GiB of RAM per vCPU The single most useful signal for family selection: C≈2, M≈4, R≈8, X≈16-32
Processor Intel Xeon, AMD EPYC, or AWS Graviton (ARM64) Sets price/performance and whether your binaries run (arch)
Network bandwidth Gbps the instance can push, often “Up to X” (burst) on small sizes Chatty services, replication, HPC, data movers care
EBS bandwidth Dedicated Gbps/IOPS to EBS volumes, separate from network Databases and I/O-heavy apps hit this ceiling before the volume’s
Instance store Local NVMe/SSD physically attached to the host (the d attribute) Ultra-fast, but ephemeral — gone on stop/resize/failure
Nitro AWS’s hardware offload + lightweight hypervisor for modern instances Enables near-bare-metal perf, ENA/EFA, EBS-as-NVMe, bare metal
Burstable (T) Instances that run at a low baseline and spend CPU credits to burst Cheapest way to run spiky, mostly-idle workloads
Generation The number in the name (5, 6, 7…) — newer silicon Newer gens are usually faster and cheaper per unit — prefer them

Two ideas do most of the work. First, the CPU:memory ratio picks the family. Measure how much RAM your workload actually uses per vCPU of CPU it burns, and the family falls out: 2 GiB/vCPU → C (compute), 4 → M (general), 8 → R (memory), 16-32+ → X (extreme memory). Second, the size picks the amount, and sizes double. From large upward, each named step doubles vCPU and memory (and lifts the network/EBS ceilings), so xlarge is 2× large, 2xlarge is 4× large, and price scales roughly linearly with it. Everything else — attributes, processor, Nitro — is refinement on top of those two decisions.

Decode the instance name

Every EC2 name has the same grammar: <family><generation><attributes>.<size>. Read it left to right.

Take m7g.2xlarge:

Part Value Meaning
Family m General purpose (balanced CPU:memory)
Generation 7 7th generation — the silicon vintage (newer = faster/cheaper)
Attributes g AWS Graviton processor (ARM64)
Separator . Splits the hardware class from the size
Size 2xlarge 8 vCPU / 32 GiB — four steps up the ladder from large

A denser example, r7iz.8xlarge: family r (memory-optimized) · gen 7 · attributes iz (i = Intel, z = high frequency / high clock) · size 8xlarge (32 vCPU / 256 GiB). And c6gd.4xlarge: family c (compute) · gen 6 · attributes gd (g = Graviton, d = local NVMe instance store) · size 4xlarge.

The attribute letters

The lowercase letters between the generation and the dot are additional capabilities. Order matters only for readability; each letter is independent. Memorise this table — it’s the part people never learn:

Letter Means Example Notes
g AWS Graviton (ARM64) processor m7g, c7g, r7g Best price/performance; needs ARM64 binaries
i Intel processor m7i, c7i, r7i Used when the family’s default isn’t Intel
a AMD EPYC processor m7a, c7a, r7a ~10% cheaper than Intel i, x86-compatible
d Local NVMe instance store (disk) m6gd, c6id, r6gd Fast ephemeral storage on the host
n Network- and EBS-optimized (higher bandwidth) c6in, m5n, r6in For network- or EBS-bound workloads
e Extra storage or memory x1e, x2iedn Bumps memory/storage above the base spec
z High frequency (higher all-core clock) m5zn, r7iz, z1d For per-core-licensed or latency-sensitive apps
b Block storage (EBS) optimized to extreme r5b Very high EBS throughput/IOPS
q Qualcomm inference accelerators dl2q Niche ML inference
flex Flex — cheaper, can’t hold 100% forever m7i-flex, c7i-flex ~5% cheaper; bursts to full, sustains ~40%

So c6in = compute · gen 6 · Intel · network-optimized; x2iedn = extreme-memory · gen 2 · Intel · extra-memory · NVMe · network-optimized. When a family has multiple processor variants in the same generation — m7i, m7a, m7g — they’re the same shape (same vCPU:memory) on three different chips, and you choose on price/performance and architecture.

A subtle one: on some older families the absence of a processor letter meant Intel by default (m5 = Intel, m5a = AMD, m5n = Intel+network). Newer families spell it out (m7i/m7a/m7g). When you see a bare m6i.large next to m6a.large, they’re deliberately distinguished; a lone m5.large is Intel.

The size ladder

After the dot comes the size. Sizes form a ladder, and from large up, each step roughly doubles vCPU and memory (and raises the network and EBS ceilings). The small end (nanomedium) is mostly T-family territory.

Size Relative vCPU (M/C/R families) Typical use Notes
nano shared/burst, ~2 vCPU, 0.5 GiB (T only) Tiny cron, bastion T family only
micro ~2 vCPU, 1 GiB (T only) Free-tier, tiny apps Free-tier eligible
small ~2 vCPU, 2 GiB (T only) Small sites T family only
medium 1–2 vCPU, 4 GiB Small services, c*.medium=1 vCPU First non-T size in some families
large 2 vCPU The baseline “1×” step M=8 GiB, C=4 GiB, R=16 GiB
xlarge 4 vCPU (2×) Small production service
2xlarge 8 vCPU (4×) Mid service
4xlarge 16 vCPU (8×) Busy service / small DB
8xlarge 32 vCPU (16×) Large service / DB
12xlarge 48 vCPU (24×) Large workload Not every family has 12xl
16xlarge 64 vCPU (32×) Very large
24xlarge 96 vCPU (48×) Very large
32xlarge 128 vCPU (64×) Huge (X/mem families)
48xlarge 192 vCPU (96×) Largest virtualized 7th-gen top size
metal / metal-24xl / metal-48xl Whole physical host, no hypervisor Nested virt, licensing, ultra-low latency You get the entire server

The doubling rule is not just about specs — price roughly doubles too, so 2xlarge costs about 4× large. That makes the ladder your cost dial: right-sizing is usually “move down one step,” which cuts the bill ~50% for that box. .metal is special: you get the raw physical server with no hypervisor, for workloads that need hardware features, their own hypervisor (nested virtualization), or licences tied to physical cores.

The instance families, end to end

There are five broad categories — general purpose, compute-optimized, memory-optimized, storage-optimized, accelerated — plus burstable (a special general-purpose sub-type) and HPC. Here’s the whole map first, then each family in depth.

The family cheat-sheet

Family Category CPU:mem (GiB/vCPU) Best for Example type Example spec
T (t3, t3a, t4g, t2) Burstable general ~2–4 (variable) Spiky, mostly-idle: small web, dev, microservices t4g.medium 2 vCPU / 4 GiB
M (m7i, m7a, m7g, m5) General purpose 4 Balanced default: app servers, mid DBs, backends m7g.xlarge 4 vCPU / 16 GiB
C (c7g, c7i, c7a, c5) Compute-optimized 2 CPU-bound: encoding, build farms, game/ad servers, HPC front-ends c7g.2xlarge 8 vCPU / 16 GiB
R (r7i, r7g, r7a, r5) Memory-optimized 8 RAM-heavy: caches, in-memory DBs, real-time analytics r7g.2xlarge 8 vCPU / 64 GiB
X (x2idn, x2iedn, x1e) Extreme memory 16–32 Big in-memory DBs, SAP, Spark x2idn.16xlarge 64 vCPU / 1024 GiB
z (z1d, r7iz) High-frequency memory 8 Per-core-licensed EDA, high-freq trading z1d.2xlarge 8 vCPU / 64 GiB @ 4.0 GHz
High-mem (u7i, u-*) Extreme memory up to ~48 SAP HANA scale-up u7i-12tb.224xlarge 896 vCPU / 12 TiB
I (i4i, i3, i4g) Storage (IOPS) ~8 NoSQL, OLTP, search, high random IOPS i4i.2xlarge 8 vCPU / 64 GiB + NVMe
D / Dn (d3, d3en) Storage (dense HDD) ~4–8 Data lakes, MapReduce, distributed FS d3.2xlarge 8 vCPU / 64 GiB + 6 TB HDD
Im / Is (im4gn, is4gen) Storage (dense NVMe) 4 / 6 High-density SSD, log/stream stores im4gn.2xlarge 8 vCPU / 32 GiB + NVMe
P (p5, p4d, p3) Accelerated (GPU) varies ML training, HPC, big simulations p5.48xlarge 192 vCPU / 2 TiB / 8× H100
G (g6, g5, g4dn) Accelerated (GPU) varies Inference, graphics, small training g5.xlarge 4 vCPU / 16 GiB / 1× A10G
Inf (inf2, inf1) Accelerated (Inferentia) varies Cost-efficient ML inference inf2.xlarge 4 vCPU / 16 GiB / 1× Inferentia2
Trn (trn1, trn2) Accelerated (Trainium) varies Cost-efficient ML training trn1.32xlarge 128 vCPU / 512 GiB / 16× Trainium
DL (dl1, dl2q) Accelerated (Gaudi/Qualcomm) varies Deep-learning training/inference dl1.24xlarge 96 vCPU / 768 GiB / 8× Gaudi
Hpc (hpc7g, hpc6a, hpc7a) HPC varies Tightly-coupled MPI: CFD, weather, genomics hpc7g.16xlarge 64 vCPU / 128 GiB + EFA

T — burstable performance

T instances are the cheapest way to run something that’s busy in bursts and idle the rest of the time — small websites, dev boxes, low-traffic microservices, bastion hosts, cron runners. Instead of paying for a full vCPU you rent a baseline slice of one and earn CPU credits while you’re under baseline; when you burst above it you spend credits. One CPU credit = one vCPU running at 100% for one minute.

Each size has a fixed baseline (as a % of a vCPU) and earns credits at a fixed rate. Run below baseline and your balance grows (capped at 24 hours’ worth); run above and it drains.

Type vCPU Baseline / vCPU Credits earned / hr Max credit balance
t3.nano 2 5% 6 144
t3.micro 2 10% 12 288
t3.small 2 20% 24 576
t3.medium 2 20% 24 576
t3.large 2 30% 36 864
t3.xlarge 4 40% 96 2304
t3.2xlarge 8 40% 192 4608

t4g (Graviton) uses the same baseline/credit table at a lower price, which makes it the default choice for new burstable workloads that can run ARM64. t2 is the older generation with lower baselines and no earned-credit carry-over on stop — avoid it for new work.

Standard vs Unlimited mode — and the billing risk

What happens when credits hit zero depends on the mode:

Mode When credits run out Cost behaviour Default on Risk
Standard CPU is throttled to baseline No extra charge — you’re capped T2 Latency/throughput collapse under sustained load
Unlimited CPU keeps bursting above baseline AWS bills surplus credits at a flat rate T3, T3a, T4g Silent runaway cost if CPU stays pinned

Unlimited mode’s surplus rate (Linux) is about $0.05 per vCPU-hour for T3/T3a and $0.04 for T4g. That’s the trap: a T3 default is Unlimited, so if a runaway process pins the CPU at 100% for a week, you don’t get throttled — you get a surplus bill that can quietly exceed the cost of a right-sized M instance. A t3.large pinned at 100% in Unlimited can bill more per month than an m7g.large that would have done the job better. The fix is a CloudWatch alarm on CPUSurplusCreditBalance (and CPUCreditBalance approaching zero), and choosing Standard mode when you want a hard cost ceiling and can tolerate throttling.

Signal (CloudWatch) Watch for Tells you
CPUCreditBalance Trending to 0 You’re about to be throttled (Standard) or start paying surplus (Unlimited)
CPUCreditUsage Consistently > earned rate Workload exceeds baseline — T is the wrong family
CPUSurplusCreditBalance > 0 and rising Unlimited mode is billing you extra
CPUUtilization flat-lined at baseline % Pinned at 10/20/40% Standard-mode throttling in action

If CPUCreditUsage is chronically above the earned rate, T is simply the wrong family — you have a steady load, not a bursty one, and you should move to M or C.

M — general purpose

M is the balanced default — 4 GiB of RAM per vCPU — and where you should start when you genuinely don’t know the shape. It fits web/app servers, mid-size relational databases, backends, caching fleets, and most microservices that run at a steady, moderate load. m7i (Intel), m7a (AMD) and m7g (Graviton) are the same shape on three chips.

Type vCPU Memory Network Notes
m7g.medium 1 4 GiB Up to 12.5 Gbps Smallest steady general box, ARM
m7g.large 2 8 GiB Up to 12.5 Gbps Common app-server size
m7g.xlarge 4 16 GiB Up to 12.5 Gbps
m7g.2xlarge 8 32 GiB Up to 15 Gbps
m7g.4xlarge 16 64 GiB Up to 15 Gbps
m7g.8xlarge 32 128 GiB 15 Gbps (sustained)
m7g.16xlarge 64 256 GiB 30 Gbps
m7i.48xlarge 192 768 GiB 50 Gbps Largest 7th-gen M

C — compute-optimized

C gives you 2 GiB per vCPU — more CPU, less memory — for genuinely CPU-bound work: video/audio encoding, build and CI farms, batch processing, game servers, ad-serving, scientific modelling, and tight high-throughput web tiers. If your CloudWatch shows high CPU and low memory, this is where an M box should move.

Type vCPU Memory Best for
c7g.medium 1 2 GiB Small CPU-bound task
c7g.large 2 4 GiB Web tier, small encoder
c7g.2xlarge 8 16 GiB Build agent, game server
c7g.4xlarge 16 32 GiB CI farm node
c7i.16xlarge 64 128 GiB Large batch/encode
c7gn.16xlarge 64 128 GiB Network-heavy compute (200 Gbps)

R, X and z — memory-optimized

Memory-optimized families give you the RAM. R is 8 GiB per vCPU for in-memory caches (Redis/Memcached), real-time analytics, mid-large relational and NoSQL databases, and search. X pushes to 16–32+ GiB per vCPU for large in-memory databases, SAP, and big Spark/analytics. High-memory (u7i/u-*) goes to multiple terabytes for SAP HANA scale-up. z1d / r7iz trade some memory ratio for a sustained ~4.0 GHz clock, which matters for per-core-licensed software (EDA, some databases) and latency-sensitive engines.

Type vCPU Memory GiB/vCPU Use case
r7g.large 2 16 GiB 8 Cache node, small in-memory DB
r7g.2xlarge 8 64 GiB 8 Redis, analytics
r7iz.8xlarge 32 256 GiB 8 High-freq memory (4.0 GHz)
x2idn.16xlarge 64 1024 GiB 16 Large in-memory DB
x2iedn.32xlarge 128 4096 GiB 32 Extreme in-memory (SAP, HANA)
u7i-12tb.224xlarge 896 12 TiB ~13.7 SAP HANA scale-up
z1d.2xlarge 8 64 GiB 8 Per-core-licensed, 4.0 GHz

I, D, Im, Is — storage-optimized

Storage-optimized families attach local disks physically on the host for very high, very cheap I/O — but that storage is instance store: ephemeral, lost on stop/resize/host failure. Use it for data you can rebuild (caches, scratch, shard replicas, search indexes), never as your only copy.

Family Disk type Optimised for Example Local storage
I (i4i, i3) NVMe SSD High random IOPS, low latency — NoSQL, OLTP, search i4i.2xlarge 1× 1875 GB NVMe
Im (im4gn) NVMe SSD (Graviton) High-density SSD, better price/GB than I im4gn.2xlarge 1× 3.75 TB NVMe
Is (is4gen) NVMe SSD (Graviton) Highest SSD density, log/stream stores is4gen.2xlarge 1× 7.5 TB NVMe
D (d3) HDD Dense, cheap sequential — data lakes, MapReduce d3.2xlarge up to 6 TB HDD
Dn (d3en) HDD + network Densest HDD + high network — distributed FS d3en.2xlarge up to ~28 TB HDD
H (h1) HDD Throughput-oriented big data h1.4xlarge 2× 2 TB HDD

The number to check on these is IOPS and throughput of the local NVMe, not EBS — that’s the whole point of the family. And the number to remember is that the data disappears on a stop.

P, G, Inf, Trn, DL — accelerated computing

Accelerated families add GPUs or purpose-built ML chips. They are the most expensive instances by a wide margin and — critically for beginners — new accounts start with a 0 On-Demand vCPU quota for them, so your first launch fails until you request an increase.

Family Accelerator Best for Example Rough On-Demand $/hr
P (p5, p4d, p3) NVIDIA H100 / A100 / V100 Large-scale ML training, HPC p5.48xlarge (8× H100) ~$98/hr
G (g6, g5, g4dn) NVIDIA L4 / A10G / T4 Inference, graphics, small training g5.xlarge (1× A10G) ~$1.01/hr
Inf (inf2, inf1) AWS Inferentia Cost-efficient ML inference inf2.xlarge ~$0.76/hr
Trn (trn1, trn2) AWS Trainium Cost-efficient ML training trn1.32xlarge ~$21.50/hr
DL (dl1, dl2q) Habana Gaudi / Qualcomm Deep-learning training/inference dl1.24xlarge ~$13/hr

The lesson for the accelerated tier: right-size the accelerator to the model, not just the vCPU. A model that fits one L4 shouldn’t be on eight H100s. Use G/Inf for inference and reserve P/Trn for real training scale, and expect to manage capacity actively (quotas, Spot, Capacity Blocks) because these are supply-constrained.

HPC — tightly-coupled clusters

Hpc families (hpc6a, hpc7a on AMD; hpc7g on Graviton) are built for MPI workloads where hundreds of nodes must talk with low latency — CFD, weather, molecular dynamics, genomics. They pair high core counts with EFA (Elastic Fabric Adapter) for sub-microsecond inter-node networking, and they’re priced and configured (often no extra EBS-heavy design) for scale-out clusters, not single boxes.

Graviton vs Intel vs AMD — the processor choice

Within most families you can pick the same shape on three chips. This is one of the highest-leverage cost decisions on AWS.

Graviton (g) Intel (i) AMD (a)
Architecture ARM64 (aarch64) x86-64 x86-64
Relative price Lowest (baseline) Highest (~+20-25% vs g) Middle (~+10% vs g, ~-10% vs i)
Price/performance Best (AWS claims up to ~40% better) Good Good, cheaper than Intel
vCPU = 1 physical core (no SMT) 1 thread (2 vCPU/core, SMT) 1 thread (2 vCPU/core, SMT)
Binary compatibility Needs ARM64 builds Runs any x86 binary Runs any x86 binary
Special features Nitro, DDR5, best perf/watt AVX-512, some ISV certs, high clock (z) High core counts, x86 at lower cost
Pick when You control the build / use common runtimes You need x86-only ISV software or AVX-512 You want x86 compatibility cheaper than Intel

Graviton is the default recommendation for anything you compile yourself or run on a mainstream runtime (Java, Python, Node.js, Go, .NET, most containers) — it’s typically the cheapest and the best price/performance, and every Graviton instance is Nitro-based. AMD is the easy x86 win: same instruction set as Intel, usually ~10% cheaper, drop-in for anything that runs on Intel. Intel earns its premium only when you need AVX-512, a vendor that certifies only on Intel, or the sustained-high-clock z variants.

The Graviton porting caveat

The catch is architecture. A Graviton instance runs ARM64; an x86 binary or container image simply won’t execute — you’ll get exec format error or a container that crashes on start. Before moving to Graviton:

Check How If it fails
OS/AMI is ARM64 Use an arm64 AMI (al2023-arm64, ubuntu/.../arm64) Launch will fail or the wrong AMI boots
Your runtime has ARM64 build Most do (JVM, Python, Node, Go, .NET) Recompile or stay x86
Container images are multi-arch docker manifest inspect shows arm64 Rebuild images with --platform linux/arm64
Third-party agents/drivers exist for ARM Vendor docs Some monitoring/security agents lag on ARM
Native/compiled deps pip/npm native modules build on ARM May need build toolchain on the box

Confirm you’re actually on ARM with uname -maarch64. If any dependency is x86-only, either stay on i/a, or run a mixed fleet (ARM where it works, x86 where it doesn’t) behind the same load balancer using multi-arch images.

The Nitro system

Modern instances (C5/M5/R5 and everything newer, and all Graviton) run on the Nitro system — AWS’s re-architecture of the hypervisor. Instead of a heavy software hypervisor stealing CPU for networking and storage, Nitro offloads those jobs to dedicated hardware cards, leaving almost all of the host’s compute for your instance.

Component What it does Why you care
Nitro Cards Offload VPC networking, EBS, instance storage, and the controller to dedicated silicon Near bare-metal performance; the hypervisor barely touches your vCPUs
Nitro Security Chip Hardware root of trust; locks down firmware/hardware Stronger isolation and boot integrity
Nitro Hypervisor A thin, KVM-based hypervisor for memory/CPU allocation only Minimal overhead vs the old Xen stack

What Nitro enables is the practical payoff: ENA and ENA Express high-bandwidth networking (up to 100–200 Gbps on the right sizes), EFA for HPC, EBS volumes presented as NVMe devices, bare-metal (.metal) instances, faster instance provisioning, and consistent performance because I/O no longer competes with your workload for CPU. When you’re comparing an old m4 to an m7g, Nitro is a big part of why the newer box is faster and cheaper.

How specs scale across sizes

The name tells you vCPU and memory, but network and EBS bandwidth also scale with size — and on small sizes they’re burst numbers, not sustained. This trips people constantly. Here’s a real family (m7i) laid out:

Type vCPU Memory Network EBS bandwidth
m7i.large 2 8 GiB Up to 12.5 Gbps Up to 10 Gbps
m7i.xlarge 4 16 GiB Up to 12.5 Gbps Up to 10 Gbps
m7i.2xlarge 8 32 GiB Up to 12.5 Gbps Up to 10 Gbps
m7i.4xlarge 16 64 GiB Up to 12.5 Gbps Up to 10 Gbps
m7i.8xlarge 32 128 GiB 12.5 Gbps (sustained) 10 Gbps (sustained)
m7i.16xlarge 64 256 GiB 25 Gbps 20 Gbps
m7i.24xlarge 96 384 GiB 37.5 Gbps 30 Gbps
m7i.48xlarge 192 768 GiB 50 Gbps 40 Gbps

Network baseline vs burst

The word “Up to” is the whole story. Sizes at or below roughly 8xlarge16xlarge in most families get a baseline bandwidth and can burst to the “Up to” figure for a limited window (governed by network I/O credits, similar in spirit to CPU credits). Larger sizes get the figure as a sustained guarantee.

If the spec says… It means Implication
“Up to 12.5 Gbps” Burst ceiling; lower sustained baseline Fine for spiky traffic; a sustained transfer will settle below 12.5
“12.5 Gbps” (no “Up to”) Sustained/guaranteed You can hold it continuously
Small size, sustained heavy network You’ll hit baseline, not the “Up to” Size up, or pick a n (network-optimized) variant

So a c6in.large (network-optimized, “Up to 25 Gbps”) is great for bursty chatter but will throttle to its baseline under a continuous firehose; if you need 25 Gbps sustained, you need a bigger size or a family/size where that number isn’t prefixed “Up to”. The same logic applies to EBS bandwidth: a small size’s “Up to 10 Gbps” to EBS is a burst — a database doing continuous large I/O can be EBS-bandwidth-bound at the instance, and the fix is a bigger instance (or an n/b variant), not a faster volume.

Right-sizing with Compute Optimizer and CloudWatch

Choosing well the first time is half the job; the other half is checking with data and correcting. AWS gives you two tools: CloudWatch (the raw metrics) and Compute Optimizer (recommendations built on them).

The signals

CloudWatch exposes these per-instance metrics. Note the big gap: CPU, network and EBS are free by default, but memory and disk-space are NOT — the hypervisor can’t see inside your OS, so you must install the CloudWatch agent to get mem_used_percent. Sizing on CPU alone is the #1 cause of an OOM after a “right-size.”

Signal CloudWatch metric Default? Tells you
CPU CPUUtilization Yes Compute pressure; low+steady = over-provisioned
Memory mem_used_percent No — needs agent RAM pressure; the blind spot that causes OOM
Network NetworkIn / NetworkOut / NetworkPacketsIn/Out Yes Bandwidth/packet pressure; near ceiling = size up or n
EBS throughput EBSReadBytes / EBSWriteBytes Yes Storage bandwidth; near instance ceiling = EBS-bound
EBS burst EBSIOBalance% / EBSByteBalance% Yes Burst-balance draining = sustained I/O over baseline
T credits CPUCreditBalance / CPUSurplusCreditBalance Yes Burstable exhaustion / surplus billing
Disk space disk_used_percent No — needs agent Volume filling up

Compute Optimizer findings

Compute Optimizer analyses ~14 days of these metrics (93 days with enhanced infrastructure metrics) and classifies each instance, then recommends up to three alternative types with projected utilisation and price.

Finding Meaning Typical action
Under-provisioned A dimension is constrained (CPU/mem/net/EBS) Size up, or move to the family that matches the tight dimension
Over-provisioned You’re paying for idle capacity Size down a step, or move to a cheaper family/architecture
Optimized Well-matched Leave it (or try Graviton for price)
None Not enough data (< 14 days, or no agent for memory) Wait / install the agent

The finding also carries a reasonCPUOverprovisioned, MemoryUnderprovisioned, EBSThroughputOverprovisioned, NetworkBandwidthUnderprovisioned — which tells you which dimension drove it, and therefore whether the fix is a smaller size or a different family. A box flagged CPUOverprovisioned and MemoryUnderprovisioned shouldn’t just shrink — it should move from C or M toward R.

The cost of over-provisioning

Because price tracks the ladder, right-sizing down one step is a ~50% saving on that box. A quick sense of the stakes (approx us-east-1, On-Demand Linux, ~730 hrs/month; ₹ at ~₹85/$):

Type vCPU/mem ~$/hr ~$/month ~₹/month
m7i.4xlarge (over-sized) 16 / 64 0.8064 ~$589 ~₹50,000
m7i.xlarge (right-sized) 4 / 16 0.2016 ~$147 ~₹12,500
m7g.xlarge (right + Graviton) 4 / 16 0.1632 ~$119 ~₹10,100
t4g.medium (bursty, right) 2 / 4 0.0336 ~$25 ~₹2,100

Moving from an over-sized m7i.4xlarge to a right-sized, Graviton m7g.xlarge is roughly an 80% cut on that instance — same conceptual workload, matched hardware. That’s why right-sizing is the highest-ROI cost lever after just turning idle things off. Purchasing models (Savings Plans, Spot) stack on top of this and are covered separately — but you always right-size first, because committing to the wrong size just locks in the waste.

Architecture at a glance

The diagram frames selection as a left-to-right funnel: you profile the real workload, decide whether it’s small-and-spiky (a burstable T) or steady, pick the family by CPU:memory ratio (M general, C compute, R/X memory) or a specialised need (I/D local NVMe, or a P/G/Inf/Trn accelerator), and finally land on one concrete type and size that you then right-size with data. Each numbered badge marks the decision that most often goes wrong on that hop — the CPU:mem ratio you must actually measure (1), the T-credit billing trap (2), compute-vs-general confusion (3), the ephemeral-storage surprise (4), accelerator cost and quotas (5), and the decode-then-right-size step at the end (6).

EC2 instance selection funnel from workload profile through burstable, family, and specialised branches to a chosen right-sized instance type, with numbered badges on the six choices that most often go wrong

Read it as the exact sequence you’ll follow in the lab below: measure first, branch on shape, choose a type, verify with Compute Optimizer.

Real-world scenario

Streamly, a fictional 40-person video startup in Bengaluru, ran everything on a fleet of m5.2xlarge (8 vCPU / 32 GiB) instances — 30 of them, chosen once during a rushed launch because “general purpose is safe.” The monthly EC2 bill had crept to $11,400 (~₹9.7 lakh), and finance asked the platform team to explain it before renewing a Savings Plan.

The team turned on Compute Optimizer and installed the CloudWatch agent for memory across the fleet, then waited 14 days. The findings were blunt. The web/API tier (12 boxes) was CPUUnderprovisioned and MemoryOverprovisioned — pinned at 85% CPU while using 18% of RAM. That’s a compute-bound workload wearing a general-purpose costume. They moved it to c7g.2xlarge (Graviton, 8 vCPU / 16 GiB): more headroom on the dimension that was tight, half the memory they weren’t using, and the ARM price. Their Java and Node services already had ARM64 builds, so the port was a base-AMI swap and a CI pipeline change.

The transcoding workers (10 boxes) were the opposite of what everyone assumed — not memory-heavy, but EBS-bandwidth-bound: EBSByteBalance% on the m5.2xlarge drained to zero mid-job because the instance’s EBS ceiling (not the volume) capped throughput. They moved these to c7gn.4xlarge (network/EBS-optimized compute) and jobs got ~30% faster with no volume change. The Redis cache tier (5 boxes) was genuinely memory-shaped — 82% RAM, 20% CPU — so it moved up the memory ratio to r7g.xlarge (Graviton, 8 GiB/vCPU), correctly this time. The three admin/back-office boxes averaged 4% CPU with brief spikes; those became t4g.large in Standard mode with a CPUCreditBalance alarm, since a hard cost cap mattered more than never throttling.

Net result before any purchasing commitment: the fleet dropped from 30 m5.2xlarge to a right-shaped mix, and On-Demand spend fell from $11,400 to about $4,900/month — a 57% cut purely from matching hardware to workload and moving suitable tiers to Graviton. Then they bought a Compute Savings Plan on the new, smaller baseline, taking it under $3,400. The lesson the team wrote in their runbook: “General purpose is not a default; it’s a shape. Measure the shape first.”

Advantages and disadvantages

Choosing types deliberately (vs defaulting to one general-purpose SKU everywhere) is a trade-off:

Advantages of matching type to workload Disadvantages / costs
30–80% lower cost by ending over-provisioning Requires 14+ days of data (CloudWatch + agent) before you trust it
Better performance on the dimension that’s actually tight More SKUs to reason about, test, and standardise
Graviton/AMD cut price 10–40% for the same shape Graviton needs ARM64 builds / multi-arch images
Right-sizing compounds with Savings Plans/Spot later Some types aren’t available in every AZ/region
Fewer surprise incidents (OOM, credit throttling, EBS caps) Changing type needs a stop (brief downtime) unless behind an ASG
Newer generations are faster and cheaper Local-NVMe families add an ephemeral-data footgun

The through-line: the only real cost of doing this well is the discipline to measure before you choose and to re-check periodically. For a single hobby box, defaulting to t3.micro/t4g.micro is fine. For anything with a bill attached, the measure-then-match loop pays for itself in the first month.

Hands-on lab

You’ll use describe-instance-types to pick a type from requirements, launch it, read the right-sizing signals, change its type, and tear it all down. Free-tier-friendly (t3.micro/t4g.micro are eligible; everything else here is metadata queries that cost nothing). Set your region first.

export AWS_REGION=us-east-1
export AWS_DEFAULT_REGION=us-east-1

⚠️ Cost note: a running t3.micro/t4g.micro is free-tier-eligible for the first 12 months (750 hrs/month). The describe-* calls are free. If you launch anything larger than micro, you pay by the second while it runs — the teardown at the end stops the meter.

Step 1 — Decode a name before you trust it

Confirm what a type actually is, straight from the API:

aws ec2 describe-instance-types --instance-types m7g.2xlarge \
  --query "InstanceTypes[0].{vCPU:VCpuInfo.DefaultVCpus, MemMiB:MemoryInfo.SizeInMiB, Arch:ProcessorInfo.SupportedArchitectures, Net:NetworkInfo.NetworkPerformance, Burstable:BurstablePerformanceSupported}" \
  --output table

Expected output:

--------------------------------------------------------
|                 DescribeInstanceTypes                |
+-----------+----------+---------+----------+-----------+
|   Arch    | Burstable|  MemMiB |   Net    |   vCPU    |
+-----------+----------+---------+----------+-----------+
|  arm64    |  False   |  32768  | Up to 15 Gbps | 8    |
+-----------+----------+---------+----------+-----------+

That confirms the decode: m7g is arm64 (Graviton), 2xlarge is 8 vCPU / 32768 MiB (32 GiB), not burstable, “Up to 15 Gbps” (burst) network.

Step 2 — Let the API pick a type from requirements

Say the workload needs 4 vCPU, 16 GiB, ARM64, current generation, and it must NOT be burstable (steady load). Filter for exactly that:

aws ec2 describe-instance-types \
  --filters "Name=processor-info.supported-architecture,Values=arm64" \
            "Name=vcpu-info.default-vcpus,Values=4" \
            "Name=memory-info.size-in-mib,Values=16384" \
            "Name=current-generation,Values=true" \
            "Name=burstable-performance-supported,Values=false" \
  --query "sort_by(InstanceTypes, &InstanceType)[].InstanceType" \
  --output text

Expected (an M-family, 4:1 ratio, ARM64 result set):

m6g.xlarge      m6gd.xlarge     m7g.xlarge      m7gd.xlarge

m7g.xlarge is the pick: newest generation, no local disk you don’t need. The useful describe-instance-types filters:

Filter Example value Selects
processor-info.supported-architecture arm64 / x86_64 CPU architecture
vcpu-info.default-vcpus 4 Exact vCPU count
memory-info.size-in-mib 16384 Exact memory (MiB)
current-generation true Latest-gen only
burstable-performance-supported false Exclude/include T family
instance-storage-supported true Has local NVMe (the d types)
bare-metal true .metal only
network-info.network-performance 25 Gigabit By network tier
instance-type m7* Name pattern

Step 3 — Check the type is offered in your AZ

A type can exist in a region but not every AZ. Confirm before you launch:

aws ec2 describe-instance-type-offerings \
  --location-type availability-zone \
  --filters "Name=instance-type,Values=m7g.xlarge" \
  --query "InstanceTypeOfferings[].Location" --output text

Expected: the AZs that offer it, e.g. us-east-1a us-east-1b us-east-1d us-east-1f. If your subnet’s AZ isn’t listed, launching there returns “not supported in your requested Availability Zone.”

Step 4 — Launch a free-tier instance (CLI)

We’ll launch a t3.micro to keep it free, using the latest Amazon Linux 2023 AMI resolved from SSM.

AMI=$(aws ssm get-parameters \
  --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query "Parameters[0].Value" --output text)

SG=$(aws ec2 create-security-group \
  --group-name ec2-sizing-lab --description "sizing lab" \
  --query GroupId --output text)

IID=$(aws ec2 run-instances \
  --image-id "$AMI" --instance-type t3.micro \
  --security-group-ids "$SG" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=sizing-lab}]' \
  --query "Instances[0].InstanceId" --output text)

echo "Launched $IID on the $AMI AMI"
aws ec2 wait instance-running --instance-ids "$IID"

Expected: Launched i-0abc123... on the ami-0abc... AMI, then the wait returns silently when it’s running.

Step 5 — Read the right-sizing signals

Pull recent CPU from CloudWatch (memory would need the agent — that’s the point of the OOM warning later):

aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value="$IID" \
  --start-time "$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --period 300 --statistics Average Maximum --output table

A brand-new idle box reads near 0–2% average — the textbook over-provisioned signal if this were a real, steady workload. To see Compute Optimizer’s verdict (after you’ve opted in and given it ~14 days on a real instance):

aws compute-optimizer get-ec2-instance-recommendations \
  --instance-arns "arn:aws:ec2:${AWS_REGION}:$(aws sts get-caller-identity --query Account --output text):instance/${IID}" \
  --query "instanceRecommendations[0].{Finding:finding, Current:currentInstanceType, Rec:recommendationOptions[0].instanceType}" \
  --output table

On a fresh instance you’ll get a finding of None (not enough data yet); on a mature one you’d see Over-provisioned with a smaller Rec.

Step 6 — Change the instance type (stop / modify / start)

You can’t change type on a running instance — stop it, modify, start it. (Behind an Auto Scaling group you’d instead change the launch template and roll the fleet, with no downtime.)

aws ec2 stop-instances --instance-ids "$IID"
aws ec2 wait instance-stopped --instance-ids "$IID"

aws ec2 modify-instance-attribute \
  --instance-id "$IID" --instance-type "{\"Value\": \"t3.small\"}"

aws ec2 start-instances --instance-ids "$IID"
aws ec2 wait instance-running --instance-ids "$IID"

aws ec2 describe-instances --instance-ids "$IID" \
  --query "Reservations[0].Instances[0].InstanceType" --output text

Expected final line: t3.small. Note we stayed on x86 (t3t3); switching to Graviton (t4g) would also require an ARM64 AMI, so you can’t just modify the type on an x86 AMI and expect it to boot.

Step 7 — The same launch in Terraform

For the IaC path, the type is one attribute — which is exactly why standardising it matters. var.instance_type makes right-sizing a one-line change and terraform apply.

terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}
provider "aws" { region = "us-east-1" }

variable "instance_type" {
  type    = string
  default = "t3.micro" # right-size here; one line, one apply
}

data "aws_ssm_parameter" "al2023" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}

resource "aws_instance" "sizing_lab" {
  ami           = data.aws_ssm_parameter.al2023.value
  instance_type = var.instance_type
  tags          = { Name = "sizing-lab-tf" }
}

output "chosen_type" { value = aws_instance.sizing_lab.instance_type }
terraform init && terraform apply -auto-approve
# to right-size later, no code edit needed:
terraform apply -auto-approve -var="instance_type=t3.small"

Changing instance_type on an existing aws_instance triggers a stop → modify → start in place (Terraform handles it) — the same flow as the CLI, not a destroy/recreate, as long as the AMI architecture still matches.

Step 8 — Teardown

Stop the meter and remove everything.

# CLI path:
aws ec2 terminate-instances --instance-ids "$IID"
aws ec2 wait instance-terminated --instance-ids "$IID"
aws ec2 delete-security-group --group-id "$SG"

# Terraform path:
terraform destroy -auto-approve

Verify nothing is left running: aws ec2 describe-instances --filters "Name=tag:Name,Values=sizing-lab,sizing-lab-tf" --query "Reservations[].Instances[].State.Name" --output text should show terminated (or nothing).

Common mistakes & troubleshooting

The playbook. Each row is a real failure with the exact way to confirm it and the fix.

# Symptom Root cause Confirm (command / console) Fix
1 Latency spikes daily; CPU flat-lined at 10/20/40% T-instance credits exhausted, Standard mode throttling to baseline CloudWatch CPUCreditBalance ≈ 0; CPUUtilization pinned at baseline % Move to M/C (steady load ≠ T), or enable Unlimited (accept surplus cost), or size up the T
2 Surprise EC2 bill with no size change T in Unlimited mode billing surplus credits on a pinned CPU CPUSurplusCreditBalance > 0 and rising; Cost Explorer “CPU Credits” line Alarm on surplus; switch to Standard for a hard cap; move steady load off T
3 run-instances fails: “not supported in your requested Availability Zone” Type not offered in that AZ aws ec2 describe-instance-type-offerings --location-type availability-zone --filters Name=instance-type,Values=<type> Launch in a listed AZ, or pick a type offered there
4 App OOM-killed right after a “right-size” down Sized on CPU only; memory was the tight dimension (no agent) dmesg | grep -i oom; no mem_used_percent metric exists Install CloudWatch agent; re-check memory; move to M/R, not a smaller C
5 Disk throughput plateaus below the volume’s rated IOPS/MBps EBS-bandwidth-bound at the instance, not the volume EBSByteBalance%/EBSIOBalance% draining to 0; instance EBS ceiling < volume rating Size up, or use an n/b variant; the volume isn’t the bottleneck
6 Container/binary won’t start: exec format error Graviton (arm64) running an x86 image/binary uname -m = aarch64; file <binary> shows x86-64 Use arm64 AMI + multi-arch image, or stay on i/a
7 Sustained network transfer settles far below the “Up to” number Baseline vs burst confusion — small size only bursts Spec says “Up to X Gbps”; throughput drops after a burst window Size up (sustained tiers), or a n network-optimized variant
8 run-instances fails: InsufficientInstanceCapacity AWS is out of that type in that AZ right now The API error; try another AZ Retry in another AZ, use a slightly different type, or a placement across AZs
9 GPU launch fails: VcpuLimitExceeded / capacity error On-Demand quota = 0 for P/G on new accounts Service Quotas → “Running On-Demand G/P instances” = 0 Request a quota increase; use Spot or Capacity Blocks
10 Data gone after a stop/start Used a d/I/D instance-store family; store is ephemeral Volume is instance store, not EBS (describe-volumes shows none) Keep only reproducible data local; persist to EBS/S3
11 modify-instance-attribute fails: InvalidInstanceType/incompatible Target type incompatible with the AMI (virt/arch/ENA/NVMe) Error string; AMI is PV or x86 vs an arm64/Nitro target Use a compatible (Nitro/HVM, matching-arch) AMI; can’t x86→arm by modify
12 Over-sized fleet at single-digit CPU, huge bill Over-provisioned default (one big general SKU everywhere) Compute Optimizer Over-provisioned; CPU/mem both low Right-size down a step and/or to Graviton before any Savings Plan
13 t2.micro unavailable in a region Older gen not offered in newer regions describe-instance-type-offerings lacks t2.micro Use t3.micro/t4g.micro (free-tier maps to these there)
14 Per-core-licensed software costs balloon on many small cores Wrong shape — licence is per core, not per instance Licence model vs vCPU count Use high-frequency z/z1d (fewer, faster cores) to cut core count

Error/status reference

Error string Where Meaning Fix
InstanceLimitExceeded / VcpuLimitExceeded run-instances You hit the per-family On-Demand vCPU quota Service Quotas → request increase for that family
InsufficientInstanceCapacity run-instances AWS lacks capacity for that type/AZ now Another AZ, another type, or retry later
Unsupported“not supported in your requested Availability Zone” run-instances Type not offered in that AZ describe-instance-type-offerings; change AZ/type
InvalidInstanceType run/modify Type name wrong or not in this region Check name and regional availability
IncompatibleInstanceType / boot failure after modify modify-instance-attribute AMI arch/virtualization ≠ target Matching-arch Nitro/HVM AMI
InvalidParameterCombination run-instances e.g. type needs a subnet/placement it didn’t get Fix the subnet/placement/network config
exec format error inside the OS x86 binary on arm64 (or vice-versa) Rebuild for the arch / multi-arch image

The three that hurt most

T-credit throttling (rows 1–2) is the classic beginner trap because it’s invisible in code and CPU-percent looks fine (it’s just capped). Always chart CPUCreditBalance; if it trends to zero under real load, you have a steady workload on a bursty family — the answer is M or C, not “buy more credits.” Memory-blind right-sizing (row 4) happens because CloudWatch shows no memory by default, so Compute Optimizer returns None or reasons only about CPU; install the agent before you resize down, or you’ll shrink into an OOM. EBS-bandwidth-bound (row 5) fools people into buying faster volumes (io2, more IOPS) that change nothing, because the ceiling is the instance’s EBS bandwidth — chart EBSByteBalance%/EBSIOBalance%, and if they drain, size the instance up or pick an n/b variant.

Best practices

Security notes

Instance-type choice is mostly a cost/performance decision, but a few security threads run through it:

Concern Guidance
Nitro isolation Modern (Nitro) instances have a hardware root of trust and stronger tenant isolation; prefer current-gen families for the isolation properties alone
Bare metal exposure .metal gives you the whole host and firmware surface — you own more of the stack; only use it when you actually need it (nested virt, licensing)
Instance store data Ephemeral NVMe isn’t wiped by you on stop — but it is cryptographically inaccessible after release; still, never put secrets/only-copies there. Nitro instance-store is encrypted at rest by default
EBS encryption Independent of type, but enable account-level default EBS encryption so every instance’s root/data volumes are encrypted regardless of family
IMDSv2 Enforce IMDSv2 (--metadata-options HttpTokens=required) on every type — it’s a launch attribute, not a family property, but it’s the single most important instance hardening
Right-size ≠ under-size Don’t right-size so aggressively you remove headroom for security agents (EDR/AV) — account for their CPU/memory when sizing
GPU/accelerated Accelerated instances often run privileged drivers; keep NVIDIA/Neuron drivers patched and restrict who can launch these high-cost, high-privilege types via IAM

Least-privilege the ability to launch expensive families: an IAM policy with a Condition on ec2:InstanceType can stop a developer from accidentally launching a p5.48xlarge.

Cost & sizing

What actually drives an EC2 instance’s bill:

Driver Effect on cost Lever
Size (ladder step) ~2× per step up Right-size down; each step ≈ 50%
Family R > M > C for the same vCPU; accelerated ≫ all Match family to shape; don’t over-buy memory
Processor Intel > AMD > Graviton for the same shape Prefer g, then a
Generation Newer often cheaper per unit Use latest gen
Region us-east-1 cheapest; Mumbai/others higher Place where latency allows
Hours running Linear — you pay per second while running Turn off non-prod off-hours (schedules)
Instance store / GPUs d/accelerated carry a big premium Only when needed
T Unlimited surplus Extra $/vCPU-hr when bursting past baseline Alarm / Standard mode

Rough On-Demand Linux us-east-1 anchors (approximate — always check the pricing page; ₹ at ~₹85/$):

Type vCPU / mem ~$/hr ~$/mo (730h) ~₹/mo
t4g.micro 2 / 1 0.0084 ~$6 ~₹520 (free-tier eligible)
t3.micro 2 / 1 0.0104 ~$8 ~₹645 (free-tier eligible)
t4g.medium 2 / 4 0.0336 ~$25 ~₹2,100
m7g.large 2 / 8 0.0816 ~$60 ~₹5,100
m7i.large 2 / 8 0.1008 ~$74 ~₹6,300
c7g.large 2 / 4 0.0723 ~$53 ~₹4,500
r7g.large 2 / 16 0.1071 ~$78 ~₹6,650
g5.xlarge 4 / 16 + A10G 1.006 ~$734 ~₹62,400

Free tier: 750 hours/month of t3.micro (or t2.micro, or t4g.micro where offered) for the first 12 months — enough to run one small instance 24×7 free. Beyond that, the cheapest real production shapes are the Graviton t4g/m7g/c7g/r7g line. Right-sizing is the biggest lever, then Graviton, then turning off idle non-prod, then (separately) Savings Plans/Spot on the corrected baseline.

Interview & exam questions

Q1. Decode r7iz.4xlarge. (CLF-C02 / SAA-C03) Memory-optimized (r), 7th generation, Intel (i) with high-frequency clock (z), size 4xlarge = 16 vCPU / ~128 GiB at a sustained ~4.0 GHz. You’d pick it for memory-heavy, per-core-licensed, or latency-sensitive work.

Q2. A t3.large API is fast most of the day but crawls every afternoon. Why, and how do you confirm it? It’s exhausting CPU credits and (in Standard mode) being throttled to its 30%/vCPU baseline. Confirm with CloudWatch CPUCreditBalance trending to zero while CPUUtilization pins at baseline. If load is steady, move to M/C; T is for bursty, not sustained.

Q3. What’s the billing risk of Unlimited mode? When credits run out, an Unlimited-mode T instance keeps bursting and bills surplus credits (~$0.05/vCPU-hr for T3 Linux). A pinned CPU can silently exceed a right-sized M instance’s cost. Mitigate with a CPUSurplusCreditBalance alarm or Standard mode.

Q4. When would you choose C over M? When the workload is CPU-bound — high CPU, low memory (a ~2:1 GiB/vCPU shape). Encoding, build farms, game servers, tight web tiers. Compute Optimizer flagging CPUUnderprovisioned + MemoryOverprovisioned on an M box is the signal.

Q5. Graviton is cheaper — why not use it everywhere? Graviton is ARM64; x86-only binaries/containers won’t run (exec format error). You need arm64 AMIs and multi-arch images, and some ISV agents lack ARM builds. Use it wherever your stack has ARM64 support (most mainstream runtimes do).

Q6. Your database’s disk throughput is stuck below the volume’s rated MBps. What’s likely wrong? The instance’s EBS bandwidth ceiling (not the volume) is the bottleneck — the box is EBS-bandwidth-bound. Confirm with EBSByteBalance%/EBSIOBalance% draining to zero. Fix by sizing the instance up or using an n/b variant; a faster volume won’t help.

Q7. What does the Nitro system give you? (SAA-C03) Hardware offload of networking/storage to Nitro Cards, a thin KVM-based hypervisor, and a security chip — yielding near-bare-metal performance, ENA/EFA high-bandwidth networking, EBS-as-NVMe, and bare-metal instances. All modern and all Graviton instances are Nitro-based.

Q8. How do Compute Optimizer findings map to actions? Under-provisioned → size up or change family toward the tight dimension; Over-provisioned → size down / cheaper family or Graviton; Optimized → leave (maybe try Graviton); None → not enough data (14 days) or no memory agent. The reason code tells you which dimension drove it.

Q9. Why can right-sizing on CPU alone cause an outage? CloudWatch shows no memory without the agent, so you might shrink a memory-tight box and get OOM-killed. Always install the CloudWatch agent for mem_used_percent before resizing down, and reason about memory, not just CPU.

Q10. What’s the difference between “Up to 12.5 Gbps” and “12.5 Gbps”? “Up to” is a burst ceiling on smaller sizes with a lower sustained baseline (network I/O credits); the bare number is sustained. A continuous transfer on an “Up to” size settles below the headline — size up or use an n variant for sustained throughput.

Q11. How do you change an instance’s type, and what constrains it? Stop → modify-instance-attribute --instance-type → start. Constraints: the AMI must match the target’s architecture (can’t x86→Graviton) and virtualization (Nitro/HVM, ENA/NVMe support); the type must be offered in the AZ; instance-store data is lost on stop.

Q12. New account, first GPU launch fails with a vCPU limit. Why? Accelerated families (P/G) have an On-Demand vCPU quota of 0 on new accounts. Request an increase in Service Quotas (“Running On-Demand G/P instances”), or use Spot / Capacity Blocks for GPU capacity.

Quick check

  1. Split c6gd.4xlarge into family, generation, attributes, and size — and say what each attribute means.
  2. Your t3.medium shows CPUCreditBalance at zero every afternoon and latency triples. What’s happening and what’s the fix?
  3. A workload runs at 90% CPU and 22% memory on an m7i.2xlarge. Which family should it move to, and why?
  4. Why is “the data disappeared after I stopped the instance” a type-selection bug, and which families cause it?
  5. You want the cheapest way to run a steady Java service that has an ARM64 build. Which processor attribute, and what must you verify first?

Answers

  1. Family c (compute-optimized) · generation 6 · attributes gd = g (Graviton/ARM64) + d (local NVMe instance store) · size 4xlarge (16 vCPU / 32 GiB). It’s an ARM compute box with fast ephemeral local disk.
  2. The T instance has exhausted its CPU credits and, in Standard mode, is being throttled to its 20%/vCPU baseline. The load is steady, not bursty, so the fix is to move to M or C, not to buy more credits or flip to Unlimited (which would just bill surplus).
  3. C (compute-optimized) — the shape is CPU-bound (~2:1 GiB/vCPU), so it’s paying for idle memory on M (4:1). Move to the same-size c7g/c7i.2xlarge (or up one C size for headroom); Compute Optimizer would flag CPUUnderprovisioned + MemoryOverprovisioned.
  4. Because you chose a family whose storage is instance store (ephemeral): the d attribute and the I/D/Im/Is storage families put NVMe physically on the host, and that data is lost on stop/resize/host failure. It’s a type decision, not an app bug. Persist anything you can’t rebuild to EBS/S3.
  5. Use g (Graviton) — e.g. m7g/c7g. First verify your AMI is arm64 and your runtime/containers have ARM64 builds (multi-arch images); confirm on the box with uname -m = aarch64. Fall back to a (AMD) if any dependency is x86-only.

Glossary

Term Definition
vCPU A virtual CPU — one thread of a physical core (2 vCPU ≈ 1 x86 core; 1 vCPU = 1 Graviton core)
Instance family The letter(s) at the start of a type name that set its category/shape (T, M, C, R, I, P…)
Generation The number in the name (5, 6, 7) — the silicon vintage; newer is usually faster and cheaper
Attribute letter Lowercase capability flags after the generation: g Graviton, i Intel, a AMD, d local NVMe, n network-opt, z high-freq
Size The part after the dot (large, 2xlarge, metal) — sets vCPU/memory; doubles per step from large
CPU:memory ratio GiB of RAM per vCPU; the primary signal for picking a family (C≈2, M≈4, R≈8, X≈16-32)
Burstable (T) Instances that run at a low baseline and spend CPU credits to burst above it
CPU credit One vCPU running at 100% for one minute; earned under baseline, spent above it
Baseline The steady % of a vCPU a T size is entitled to without spending credits
Unlimited mode T mode that keeps bursting past exhausted credits and bills surplus (the T3/T4g default)
Standard mode T mode that throttles to baseline when credits run out (no surplus charge)
Graviton AWS’s ARM64 (aarch64) processors — best price/performance; needs ARM64 binaries
Nitro AWS’s hardware-offload + lightweight hypervisor for modern instances; enables ENA/EFA, EBS-as-NVMe, bare metal
Instance store Local NVMe/SSD on the host (the d attribute) — very fast but ephemeral
EBS bandwidth The instance’s dedicated throughput to EBS volumes — a ceiling separate from network and from the volume’s own limits
Compute Optimizer AWS service that reads ~14 days of metrics and recommends right-sized types
Right-sizing Matching instance size/family to measured demand to cut cost without losing performance
.metal A bare-metal instance — the whole physical host, no hypervisor

Next steps

AWSEC2Instance TypesGravitonNitroRight-SizingCompute OptimizerBurstable
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading