Stopped instances with EBS: the basics
Why a 'stopped' EC2 instance is rarely free
Stopping an EC2 instance is the cloud equivalent of putting a laptop to sleep — the compute clock pauses, but the storage stays mounted. AWS bills EC2 hours and EBS storage on separate meters, and only the EC2 meter stops when you click Stop. Every gp3 volume still attached to that instance keeps accruing roughly $0.08 per GB-month for as long as it exists, whether the instance ever boots again or not.
The check flags an instance in the stopped state with one or more EBS volumes still attached. That's almost always the leftover of an experiment, a misnamed test box, a developer who quit, or a team that meant to come back "next sprint" and never did. A stopped instance with a 500 GB gp3 root volume is quietly $40 a month, every month, with no workload running on it.
It's flagged because the asymmetry surprises people. Engineers assume "stopped" means "free" and triage it accordingly — last. The longer it sits, the larger the cumulative bill, and the harder it gets to find anyone who can confidently say "yes, terminate that."
In this lesson you'll learn the EC2/EBS billing split that makes stopped instances surprisingly expensive, how to tell an abandoned instance from one that's legitimately paused, and the safe four-step pattern — snapshot, detach, delete, terminate — that lets you reclaim storage without destroying anything you might still need. You'll see the AWS CLI commands to audit a fleet, the edge cases that bite (RIs, instance store, encryption), and a tagging strategy that prevents the problem from coming back.
The 'temporary' instance that turned three
A platform team at a fintech ran an audit and found 412 stopped EC2 instances across their dev account. The oldest had last transitioned to stopped 1,147 days earlier — over three years. Its 200 GB gp2 root volume had quietly billed roughly $480 in storage with the instance never having booted again. The owner tag pointed to an engineer who had left the company in 2024. Total cleanup that quarter: $9,200 a month, all from volumes attached to instances that had been 'temporarily stopped' months or years before.
Cleaning up stopped instances in action
Nina runs the FinOps cadence at a mid-sized SaaS company. The dashboard flags 23 stopped EC2 instances with attached EBS, totalling about $340 of monthly wastage. The largest is a stopped m5.xlarge with a 160 GB gp3 root volume — $16 a month in storage, no compute charge, last booted in February.
She pulls the instance metadata to find the LastStateTransitionTime and the owner tag. The reason is User initiated (2026-02-14 11:42:01 GMT) and there's an Owner=preeti.k tag. She pings Preeti on Slack: "This i-0abc still needed?" The answer comes back in ten minutes — "Oh, that was a one-off load test. Definitely safe to nuke."
Nina doesn't terminate it blind. She takes a snapshot first (cheap insurance — $0.05/GB-month versus $0.08/GB-month for live storage), waits for completed, then terminates the instance with DeleteOnTermination=true on the volume. Total time: about four minutes; the snapshot stays in S3-backed storage in case Preeti needs to spin it back up in the next 90 days.
First, list every stopped instance with its attached volumes and the time it last transitioned to stopped.
Stopped-state inventory with the timestamp that exposes how long they've been parked.
Before terminating, snapshot the attached volume so you have a 30-90 day rollback path at a lower per-GB rate.
Snapshot the volume first; at ~$0.05/GB-month it's cheaper than the live volume and gives you an undo.
Stopped instances under the hooddeep dive
Stopping an EBS-backed EC2 instance keeps the instance metadata (ID, ENI configuration, IAM role, security groups, tags) and the attached EBS volumes intact. The hypervisor releases the underlying physical capacity back to the pool — that's why you stop paying for vCPU and RAM hours — but the volumes remain in their AZ, persistent and writable on next boot. Public IPv4 addresses are released on stop unless you've allocated an Elastic IP; that's a separate billing concern.
EBS storage is priced per provisioned GB-month per volume type: gp3 at $0.08, gp2 at $0.10, io2 at $0.125 plus IOPS, st1 at $0.045, sc1 at $0.015 (all rough US-East rates). A stopped instance with a 100 GB gp3 root and a 500 GB gp3 data volume is $48 a month of pure storage cost — no compute, no network, just bytes sitting on a disk waiting for an instance that will probably never boot. Reserved Instances and Savings Plans don't help here; both apply to compute hours, not storage.
The two state details that matter for forensic analysis are StateTransitionReason (a human-readable string like User initiated (2026-02-14 11:42:01 GMT) set when the instance stopped) and the corresponding CloudTrail StopInstances event. Together they tell you when it stopped, who stopped it, and whether anyone has touched it since. Anything stopped more than 60 days with no subsequent StartInstances event is a strong abandonment signal.
# Pull the original Stop event from CloudTrail to confirm intent and ownership.
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=i-0abc123 \
--max-results 5 \
--query 'Events[?EventName==`StopInstances`].[EventTime,Username]' \
--output table
# Confirm there's been no StartInstances event since — i.e. it's truly abandoned.
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=i-0abc123 \
--lookup-attributes AttributeKey=EventName,AttributeValue=StartInstances \
--max-results 1 What is the impact of stopped instances with EBS?
The direct cost is the EBS bill on volumes attached to instances that aren't doing anything. At gp3 rates a typical 100 GB root volume is $8 a month; a 500 GB data volume is $40 a month; a 2 TB analytics volume is $160 a month. Across a few hundred stopped instances in a long-lived dev account, that's $5–20k a month of pure waste, and the meter runs every hour regardless of whether the instance boots again.
There's a sneaky second-order cost too: snapshots. AWS will happily let you stop an instance, never come back to it, and then later replace the now-forgotten volume with a fresh one — leaving the original attached, billing, and invisible to anyone but a careful audit. Without lifecycle tags it's almost impossible to know which volumes are still load-bearing and which are orphaned remnants of past experiments.
Reserved Instances and Savings Plans introduce a third surprise. A Reserved Instance keeps billing on a stopped instance because the RI is a commitment to pay for the instance type whether you use it or not — stopped doesn't release the reservation. Dedicated Hosts are even more painful: the host fee continues even with zero running instances. Stopping a workload to "save money" while an RI is in flight saves you nothing on the compute side and still costs you on the storage side.
Finally, abandoned instances make incident response harder. Six months from now someone needs to roll back a customer to a known-good database snapshot, finds a candidate volume attached to a stopped instance, and has no idea whether it's safe to use. Cleaning these up isn't just a cost story — it's a hygiene story. A short list of stopped instances is much easier to reason about than a long one.
How do you clean up stopped instances safely?
Cleanup is a four-step loop that runs at every FinOps cadence: inventory what's stopped, confirm intent with the owner, snapshot-then-delete, and tag the survivors so this doesn't repeat.
1. Inventory every stopped instance with age and owner
Pull every stopped instance across every region and account into a single sheet with LastStateTransitionTime, Owner tag, attached volume IDs, volume sizes, and estimated monthly storage cost. Anything stopped more than 60 days is a candidate; anything more than 180 days with no Owner tag is almost certainly abandoned. Filter out anything labelled Lifecycle=long-paused or similar — those are deliberate.
2. Confirm intent with the owner before doing anything destructive
A two-line Slack message to the owner — "This instance has been stopped for 90 days; OK to terminate?" — surfaces 80% of the decisions in a day. For the remaining 20%, escalate to the team lead via the cost-allocation tag. Never auto-terminate without human sign-off on first run; build trust with the team before automating the destruction path.
3. Snapshot, then delete the volume, then terminate the instance
Take a snapshot of every attached volume (with an ExpiresAt tag 30-90 days out for safety net), wait for completed, then terminate the instance with the volume's DeleteOnTermination flag set to true. Snapshots are ~40% cheaper per GB than live volumes and give you a clean rollback path. For encrypted volumes the snapshot inherits the KMS key automatically — no extra step. For instance-store volumes (i3, c5d, etc.) there's nothing to snapshot; that data was already lost when the instance stopped.
4. Tag new instances with intent so the next audit is shorter
Adopt a simple two-tag convention: Lifecycle=ephemeral|persistent and ExpiresAt=YYYY-MM-DD. Enforce it with a tag policy or AWS Config rule. An EventBridge schedule can then automatically stop and snapshot ephemeral instances past their ExpiresAt, and the next month's report drops by half. The cleanup loop only stays manageable if new instances arrive pre-labelled.
# Bulk-snapshot every volume on a stopped instance, then terminate.
INSTANCE=i-0abc123
VOLS=$(aws ec2 describe-instances --instance-ids $INSTANCE \
--query 'Reservations[].Instances[].BlockDeviceMappings[].Ebs.VolumeId' \
--output text)
for v in $VOLS; do
aws ec2 create-snapshot --volume-id $v \
--description "Pre-termination $INSTANCE" \
--tag-specifications "ResourceType=snapshot,Tags=[{Key=Source,Value=$INSTANCE},{Key=ExpiresAt,Value=$(date -u -d '+90 days' +%F)}]"
done
# Wait for all snapshots to complete before terminating.
aws ec2 wait snapshot-completed --filters Name=tag:Source,Values=$INSTANCE
aws ec2 terminate-instances --instance-ids $INSTANCE Quick quiz
Question 1 of 5You find an m5.xlarge stopped 120 days ago with a 200 GB gp3 root volume and a 1-year Reserved Instance still active for that instance type. What's the right next move?
You scored
0 / 5
Keep learning
Dig deeper into EC2/EBS lifecycle, billing mechanics, and the tooling around cleanup.
- AWS EC2 instance lifecycle documentation Authoritative source on what stop/start/terminate does to compute, storage, and networking state.
- Amazon EBS pricing Current per-GB-month rates for gp3, gp2, io2, st1, sc1, and snapshots — the numbers behind the cleanup math.
- AWS Cost Optimization Hub — unattached and idle resources Managed AWS service that surfaces stopped instances with attached EBS alongside other wastage findings.
- FinOps Foundation — Cloud Rate and Usage Optimization How resource-cleanup discipline fits the broader FinOps lifecycle and operating model.
You've completed Clean up stopped EC2 instances with attached EBS. You now know why a stopped instance is rarely free, the four-step inventory-confirm-snapshot-terminate loop that reclaims storage safely, and the tagging convention that stops the problem from coming back. The next time the wastage report flags a stopped instance, you'll have a defensible path from "flagged" to "resolved" in under five minutes.