EC2: instances & pricing
AMIs, instance types, On-Demand/Reserved/Spot.
Renting compute from EC2 works like renting a car at an airport counter. You pick a class of vehicle that suits the job: economy for errands, a box truck for moving day. Then you pick a rate plan. There is the walk-up price, the prepaid corporate contract, and the steep standby discount that carries the risk of being bumped. Amazon EC2 (Elastic Compute Cloud, AWS's rented virtual servers) is that counter. You choose a hardware profile and a pricing model, AWS runs the machine somewhere on its fleet, and you pay for the seconds you hold it. Those two choices drive a surprising share of real AWS bills, and a surprising share of the questions on the SAA (Solutions Architect Associate) exam.
What an instance is actually made of
An EC2 instance is one virtual machine, assembled at launch out of a handful of named parts. Learn the names properly, because the exam and the command line both use them precisely. The AMI (Amazon Machine Image) is the template: a photocopy of a fully set-up boot disk, holding the operating system plus whatever software you baked in. Teams that pre-install their monitoring agents and their security hardening into a *golden AMI* get identical servers that boot fast, instead of machines that scramble to configure themselves the first time they start. The instance type is the hardware profile, and its name is a code you can read straight off the label. Take m7g.large. The m is the *family* (general purpose). The 7 is the *generation*, so a higher number is newer. The g means Graviton, AWS's own Arm processors, which price roughly 20% below the comparable x86 type per hour (compare m7g.large with m7i.large). And large is the *size*: 2 vCPU (virtual CPUs, roughly one hardware thread each) and 8 GiB (gibibytes) of memory. Sizes double as you step up, so xlarge is 4 vCPU / 16 GiB and 2xlarge is 8 / 32. Memorize the families: t and m are general purpose, c is compute-optimized, r and x are memory-optimized, i and d are storage-optimized, g and p carry GPUs (graphics processors, used here for rendering and machine learning) and other accelerators. Underneath all of it, current instances run on the Nitro System. AWS moved the networking, storage, and security work off the main processor and onto dedicated hardware cards, so nearly all of the host CPU goes to your virtual machine instead of to the hypervisor (the software layer that normally splits one physical server into many virtual ones).
Two command line habits pay off right away. Ask the catalog which instance types exist rather than guessing, and look up AMI IDs from AWS's public SSM (Systems Manager) parameters rather than typing them in by hand. AMI IDs are different in every region and go stale the moment AWS ships a newer image, so a hardcoded ami-... sitting in a script is a broken deploy waiting for a date nobody wrote down.
# Which current-generation types have 2 vCPUs and 8 GiB? (small web-tier candidates)aws ec2 describe-instance-types \--filters "Name=current-generation,Values=true" \"Name=vcpu-info.default-vcpus,Values=2" \"Name=memory-info.size-in-mib,Values=8192" \--query 'InstanceTypes[].{Type:InstanceType,Arch:ProcessorInfo.SupportedArchitectures[0]}' \--output table# ------------------------------# | DescribeInstanceTypes |# +-----------+----------------+# | Arch | Type |# +-----------+----------------+# | arm64 | m8g.large |# | x86_64 | m7i.large |# | arm64 | m7g.large |# | x86_64 | m6i.large |# +-----------+----------------+# (truncated — t3.large, m5.large, m6a.large, m7a.large and others also match)# Resolve the latest Amazon Linux 2023 AMI at launch time — never hardcode IDsaws ssm get-parameters \--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64 \--query 'Parameters[0].Value' --output text# ami-0c8f2a9e6b1d4e573
Launch one from your terminal
aws ec2 run-instances is the single API (application programming interface) call hiding behind the console's launch wizard, and it makes you say out loud everything the wizard quietly filled in for you. The subnet you pass decides which Availability Zone the machine lives in, which is the blast-radius thinking from the Global infrastructure lesson. The security group is that instance's own stateful firewall, covered properly in the VPC (Virtual Private Cloud) lesson. The instance profile is the piece newcomers skip and later regret. It works like a badge reader bolted to the machine: software on the box asks for credentials, and the profile hands over short-lived, auto-rotating ones belonging to an IAM (Identity and Access Management) role. No long-lived access key ever has to sit on disk.
AMI=$(aws ssm get-parameters \--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64 \--query 'Parameters[0].Value' --output text)aws ec2 run-instances \--image-id "$AMI" \--instance-type m7g.large \--subnet-id subnet-0f1e2d3c4b5a69788 \--security-group-ids sg-0a1b2c3d4e5f60718 \--iam-instance-profile Name=web-tier-profile \--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-01},{Key=env,Value=prod}]' \--count 1 \--query 'Instances[0].{Id:InstanceId,State:State.Name,AZ:Placement.AvailabilityZone}'# {# "Id": "i-0d4f8a2b9c3e17650",# "State": "pending",# "AZ": "eu-west-1a"# }# ~40 seconds later, confirm it reached "running"aws ec2 describe-instances --instance-ids i-0d4f8a2b9c3e17650 \--query 'Reservations[0].Instances[0].{State:State.Name,PrivateIp:PrivateIpAddress,Launched:LaunchTime}'# {# "State": "running",# "PrivateIp": "10.0.1.87",# "Launched": "2026-07-13T09:41:22+00:00"# }
The four ways to pay
On-Demand is the walk-up rate. Billing runs per second with a 60-second minimum (a few commercial operating system images, such as RHEL (Red Hat Enterprise Linux) and SUSE, still bill by the hour), and you commit to nothing. That makes it the right default for anything spiky, experimental, or short-lived. You pay a premium for the freedom to walk away. Reserved Instances, usually shortened to RIs, cut the price of one instance family in one region by up to about 72% in exchange for a 1-year or 3-year commitment. Two exam details hide in there. *Standard* RIs are the cheapest but lock you to the family you picked, while *Convertible* RIs cost a little more and can be swapped for a different family, operating system, or tenancy partway through the term. And only a *zonal* RI, one scoped to a single Availability Zone, actually holds capacity for you in that zone. A regional RI is a billing discount and nothing else.
Savings Plans have replaced RIs for most new purchases. You commit to spending a set number of dollars per hour instead of committing to one particular instance shape. A *Compute* Savings Plan stretches across families, sizes, and regions, and it covers Fargate and Lambda too, so the discount survives the day you re-architect. Spot Instances sell AWS's leftover capacity at up to about 90% off. The catch: AWS can take the machine back, and all you get first is a two-minute interruption notice. Spot stopped being an auction in 2017. You pay whatever the current Spot price is, and that price drifts slowly with supply and demand. Older exam dumps still describe bidding, and they are wrong. Spot fits work that can lose a node and shrug it off: batch jobs, CI (continuous integration) runners, extra web capacity sitting behind a load balancer. It is a terrible home for a stateful primary database.
Last comes tenancy, which is really the question of who else shares the physical hardware with you. Dedicated Instances run on hardware walled off for your account alone. A Dedicated Host goes further and hands you the whole physical server, including a view of its sockets and cores. That view is the entire point, because per-socket bring-your-own-license software (Windows Server, Oracle) has to count sockets to stay legal. Host versus Instance turns up on the exam again and again, and the licensing visibility is what decides the answer.
# What is Spot actually paying for m7g.large right now, per AZ?aws ec2 describe-spot-price-history \--instance-types m7g.large \--product-descriptions "Linux/UNIX" \--max-items 3 \--query 'SpotPriceHistory[].{AZ:AvailabilityZone,USDperHr:SpotPrice}' \--output table# --------------------------------# | DescribeSpotPriceHistory |# +--------------+---------------+# | AZ | USDperHr |# +--------------+---------------+# | eu-west-1b | 0.029800 |# | eu-west-1a | 0.031200 |# | eu-west-1c | 0.033500 |# +--------------+---------------+# On-Demand for m7g.large here is ~$0.09/hr — Spot is running roughly 66% off,# and the cheapest AZ changes over time. Diversify across AZs and instance types.
t3, t4g) work like a phone plan with rollover minutes. They bank CPU credits while idle and spend them to burst, and the speed they guarantee once the credits are gone is only a fraction of one core. Since the t3 generation, *unlimited mode* is the default. When the credits run out, the instance does not slow down. It keeps bursting and bills the surplus credits at about $0.05 per vCPU-hour on Linux pricing. So a t3.large pinned at 100% CPU quietly bills more than the m7g.large you turned down for being too expensive. Watch CPUSurplusCreditsCharged in CloudWatch (AWS's built-in metrics service). If it stays above zero for long stretches, move the workload to an m family instance.What each lifecycle state costs you
An instance walks through a short list of states, and every one of them means something different on your bill. pending is free. Billing starts the second the state flips to running. Stopping the instance ends the compute charge but keeps two things alive: its EBS (Elastic Block Store, AWS's network-attached disk) root volume, which bills monthly per GiB whether the machine runs or sits idle, and its private IP (Internet Protocol) address. The public IPv4 address goes away unless you attached an Elastic IP, and the machine comes back with a different one, which breaks anything that memorized the old address. A stop and start cycle also, in most cases, lands the instance on fresh physical hardware. That is the standard fix when AWS flags a host as degraded. Hibernate is a stop plus a dump of memory to the EBS root volume, so a process with a slow warm-up picks up where it left off instead of booting from scratch. Terminate is final. Nothing comes back.
aws ec2 stop-instances --instance-ids i-0d4f8a2b9c3e17650# {# "StoppingInstances": [# {# "InstanceId": "i-0d4f8a2b9c3e17650",# "CurrentState": { "Code": 64, "Name": "stopping" },# "PreviousState": { "Code": 16, "Name": "running" }# }# ]# }# Stopped: compute billing ends, but the 8 GiB root volume keeps billing monthly.aws ec2 terminate-instances --instance-ids i-0d4f8a2b9c3e17650# {# "TerminatingInstances": [# {# "InstanceId": "i-0d4f8a2b9c3e17650",# "CurrentState": { "Code": 32, "Name": "shutting-down" },# "PreviousState": { "Code": 80, "Name": "stopped" }# }# ]# }# The root volume is deleted with it (DeleteOnTermination=true by default).
Putting it together
The cheapest sensible architecture uses every model on the menu at once. A Savings Plan covers the predictable baseline, On-Demand soaks up the normal ups and downs, and Spot does the fault-tolerant bulk work. You will automate that mix when we reach Auto Scaling. Notice what EC2 never takes off your plate, though. The operating system is yours. So is patching it, hardening it, and paying for its idle hours at 3 a.m. When the real unit of work is a function or a container rather than a whole machine, renting a whole machine is the wrong thing to be renting. That trade, control against operational burden, is what the next lesson weighs up with Lambda, containers, and PaaS (platform as a service, where the provider runs the servers and you hand over your code).
Instance families are shorthand for the bottleneck you expect to hit. Compute-optimized when the CPU runs out first. Memory-optimized when the working set is enormous. Storage-optimized when local disk IOPS (input/output operations per second, meaning how many reads and writes the disk can handle) is the limit. General purpose when you honestly do not know yet. Guessing forever is how t3.2xlarge ends up as the default for every service in the account.
Treating Spot as cheap On-Demand is how teams get burned. It is interruptible capacity with a two-minute warning attached. Use it for fleets that can lose a node without anyone noticing: batch, CI, stateless web tiers. Never for the only copy of a database. Reserved Instances and Savings Plans pay you for committing to a steady baseline, so right-size the workload first. Otherwise you have committed to paying for waste on a schedule.
User data runs once, at first boot, unless you go out of your way to change that. Bake anything slow or security-sensitive into the AMI and keep user data for the last mile of configuration. And never bake an access key into an AMI. Attach an instance profile instead, so the credentials rotate on their own.
Try this
Look up a current Amazon Linux AMI, launch one tiny instance with the instance profile pattern in mind, then stop it and remember what stopping does and does not end (stop bills the disk, terminate deletes it). Run this in a lab VPC and subnet, never in production.
aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \--query 'Parameters[0].Value' --output textaws ec2 run-instances --image-id ami-0abcdef1234567890 --instance-type t3.micro \--subnet-id subnet-0b1de2f3a4c5d6e70 --count 1 \--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=saa-lab}]' \--query 'Instances[0].{Id:InstanceId,State:State.Name,Type:InstanceType}' --output tableaws ec2 stop-instances --instance-ids i-0abc123def4567890
ami-0c55b159cbfafe1f0---------------------------------------------| RunInstances |+----------------------+----------+---------+| Id | State | Type |+----------------------+----------+---------+| i-0abc123def4567890 | pending | t3.micro|+----------------------+----------+---------+# stop keeps the EBS volume; terminate would delete the instance
Takeaway
Three habits carry you through both the exam and the bill: pick the family by the bottleneck you expect, pick the purchase option by how much interruption and commitment you can live with, and reach for roles and baked AMIs rather than access keys and hand-tuned one-off servers.
Next, price one real workload three ways: On-Demand, a one-year Savings Plan, and Spot. Then write a single sentence for each, naming the failure you are agreeing to accept.