Expiring reservations: the basics
Why a commitment ending is a cost event, not a non-event
A Reserved Instance, Reserved Node, or Savings Plan is a fixed-term discount — one or three years. For the whole term, the workloads it covers bill at the committed rate, up to ~72% below On-Demand. The thing people forget is what happens at the end: the commitment simply expires. There is no renewal, no grace period, no warning email at the hour it lapses. The discount stops, and every hour of usage that was riding on it reverts to full On-Demand price the moment the term clock hits zero.
The check flags any reservation or Savings Plan whose end date falls inside a look-ahead window — typically 30 or 60 days out. That window exists because the workload almost always keeps running. An RI covering a production database doesn't know the RI expired; the database keeps serving traffic, now at On-Demand rates. The result is a sudden, mechanical bill increase on spend that didn't change — same instances, same hours, just no discount applied — and it shows up as a variance spike in the month after expiry, long after anyone remembers the commitment lapsed.
It's surfaced because the failure is silent and the math is brutal. A $40k/year RI portfolio reverting to On-Demand can add $15–25k/year of pure rate increase on identical usage. The discipline is to build an expiry calendar across every commitment type, re-evaluate the workload before the term ends, and queue a replacement so coverage is continuous — never letting a large chunk of spend hit the On-Demand cliff at once.
In this lesson you'll learn how commitment expiry actually works across RIs, Reserved Nodes (RDS, Redshift, ElastiCache, OpenSearch, MemoryDB) and Savings Plans, why a lapse produces a silent On-Demand spike, and the re-evaluate-before-renew discipline that prevents it. You'll see how to build an expiry calendar, the right-size-and-confirm-shape check to run before renewing, how to decide RI versus the more flexible Savings Plan and pick term and payment, the queue-purchase and auto-renew options and their risks, and why staggering expiries matters. You'll get the real CLI calls to list reservations by end date, find expiring Savings Plans, and queue a replacement.
The Tuesday the bill jumped 40%
A platform team had bought eleven 3-year Standard RIs in a single procurement push back in 2023 — convenient at the time, because one purchase order covered the whole production fleet. Exactly three years later they all expired in the same week. Nobody had built a calendar, so the first anyone noticed was the following month, when the EC2 line came in roughly 40% higher with zero change in usage. The workloads were all still running; the only thing that had changed was that $38k/year of committed discount had evaporated on a Tuesday. The fix was easy once spotted — queue replacement Savings Plans — but the lesson stuck: never let your expiries cluster on the same date you bought them.
Managing an expiring reservation in action
Devin runs the FinOps cadence at a mid-sized analytics company. The dashboard flags three commitments expiring in the next 45 days: two EC2 Standard RIs for r5.2xlarge in us-east-1, and a $4/hour Compute Savings Plan. Combined, they cover about $9k/month of spend that will revert to On-Demand if nothing replaces them — roughly a $3k/month increase on identical usage.
He doesn't just re-buy what's expiring. Before renewing, he checks the covered workloads are still running and still the same shape. Compute Optimizer flags that one of the r5.2xlarge workloads has been running at 30% CPU for months — it should be an r5.xlarge. Renewing the old RI as-is would have locked in a discount on an oversized instance for another three years. So he right-sizes first, then re-evaluates the product: the workload moved partly to Graviton last quarter, so a flexible Compute Savings Plan now fits better than a family-locked RI.
Devin queues a replacement Savings Plan sized to the right-sized floor, dated to start the day the current commitments lapse, so coverage is continuous with no gap. He deliberately picks a 1-year No Upfront term — the architecture is still moving — and sets the new expiry date 4 months offset from his other big commitment so they never cliff together. Calendar reminder set for 60 days before the next expiry. No spike, no stranded discount on the wrong shape.
First, list active EC2 Reserved Instances and surface their end dates so you can see what's about to lapse.
Sorting by end date turns the reservation inventory into an expiry calendar — the rows at the top are the cliff.
Now find Savings Plans approaching their end date, and check the utilisation before deciding whether to renew like-for-like.
Savings Plans expire on a date too; the $4/hr plan reverts that spend to On-Demand the second its term ends.
Reservation expiry under the hooddeep dive
Every commitment carries a fixed term and an immutable end timestamp. An EC2 Reserved Instance has Start, End, and a Duration in seconds; once End passes, AWS stops applying the RI's billing discount and the matching usage is invoiced at On-Demand from the next hour onward. There is no auto-extension and no grace window — the discount evaporates at the boundary. Reserved Nodes for RDS, Redshift, ElastiCache, OpenSearch, and MemoryDB behave identically, each with its own describe-reserved-* API exposing a StartTime and Duration you must compute the end from. Savings Plans expose an explicit end field via savingsplans describe-savings-plans.
The reason a lapse is silent is that the discount and the workload are decoupled. An RI or Savings Plan is a billing-layer construct, not a property of the instance — the instance never knows it's covered. So when coverage ends, nothing in the running system changes or alerts; only the rate applied at invoice time changes. This is why the spike surfaces a month late, in the bill, rather than in any operational signal. The only way to see it coming is to read the end dates ahead of time and build a calendar from them.
Renewal has two distinct mechanisms with different risk profiles. Standard RIs support a queued purchase — you schedule a buy with a future PurchaseTime so the new reservation activates as the old one lapses, giving continuous coverage. Some consoles and tools also offer auto-renew, which re-buys the same reservation on expiry; the danger is that it re-locks the original instance family, size, and region for another full term, even if the workload has since been right-sized or migrated. The disciplined pattern is to never auto-renew blindly: re-evaluate the workload first, then queue an explicit replacement — often a flexible Compute Savings Plan rather than a like-for-like RI.
# Reservations expiring within the next 60 days, across EC2 and RDS.
CUTOFF=$(date -u -d '+60 days' +%Y-%m-%dT%H:%M:%SZ)
aws ec2 describe-reserved-instances \
--filters Name=state,Values=active \
--query "ReservedInstances[?End<='$CUTOFF'].[ReservedInstancesId,InstanceType,InstanceCount,End]" \
--output table
aws rds describe-reserved-db-instances \
--query "ReservedDBInstances[?State=='active'].[ReservedDBInstanceId,DBInstanceClass,StartTime,Duration]" \
--output table
# Queue a replacement EC2 RI to activate as the old one lapses (continuous coverage, no On-Demand gap).
aws ec2 purchase-reserved-instances-offering \
--reserved-instances-offering-id 4a8b1c2d-1234-5678-9abc-def012345678 \
--instance-count 4 \
--purchase-time 2026-06-30T00:00:00Z What is the impact of an expiring reservation?
The direct impact of a lapse is a pure rate increase on unchanged usage. A Standard RI or Savings Plan typically discounts 30–72% off On-Demand; when it expires uncovered, that whole discount reverses on the next billing hour. A $40k/year commitment portfolio reverting to On-Demand commonly adds $15–25k/year to the bill for zero change in what's running. Because the workload is identical, there's nothing operational to point at — it's invisible until the invoice, which is the worst time to find it.
The second-order impact is variance and forecast damage. The spike lands in the month after expiry and shows up in 'unexplained variance' buckets, sending finance and engineering on a hunt for a usage change that never happened. Every uncoordinated expiry erodes the credibility of the cost forecast, because the model didn't account for a discount cliff that was, in fact, perfectly knowable from the end dates all along.
There's a clustering impact that bites hardest. Commitments bought together — a single procurement push, a batch of 3-year RIs signed the same quarter — expire together. When a large slice of spend hits the On-Demand cliff in one month, the spike is large and concentrated rather than small and absorbable. Staggering expiry dates across the calendar turns one big shock into a series of small, manageable renewals.
Finally there's a renewal-quality impact, and it cuts both ways. Letting a commitment lapse strands savings; but blindly auto-renewing strands the wrong shape. If the covered workload was right-sized down, migrated to Graviton, or moved region since the original buy, an auto-renew re-locks the obsolete commitment for another full term with no resale market for Savings Plans. The expensive mistakes live at both ends — no renewal, and thoughtless renewal — and the discipline of re-evaluate-then-renew is what avoids both.
How do you manage expiring reservations safely?
Managing expiries well is a repeatable loop on the FinOps cadence: build the calendar, re-evaluate the workload before renewing, queue the replacement for continuous coverage, and stagger terms so you never face a cliff on a large chunk of spend at once.
1. Build a single expiry calendar across every commitment type
Pull end dates for EC2 RIs, every Reserved Node type (RDS, Redshift, ElastiCache, OpenSearch, MemoryDB) and all Savings Plans into one calendar, with the monthly spend each one covers and the dollar exposure if it lapses. Sort by end date. Anything inside 60 days is an active renewal item; anything inside 90 days needs a re-evaluation started. The calendar is the whole game — once you can see the cliffs coming, none of them is a surprise.
2. Re-evaluate the workload before you renew anything
Never renew like-for-like on autopilot. Before replacing a commitment, confirm the covered workload is still running, run Compute Optimizer to confirm it's still the right size, and check it hasn't migrated family, region, or platform (e.g. to Graviton or Fargate). Right-size first if needed, then decide the product: a family-locked RI or Reserved Node if the workload is genuinely fixed and durable, or the more flexible Compute Savings Plan if it's moving. Renewing an obsolete shape locks a multi-year discount onto the wrong thing.
3. Queue the replacement for continuous coverage
Once you've decided the replacement, schedule it to activate as the old commitment lapses so there's no On-Demand gap — Standard RIs support a queued purchase with a future purchase time, and Savings Plans can be bought to start on the expiry date. Pick term and payment for durability and cash: 1-year No Upfront as the default for workloads in flux, 3-year and upfront only on proven-durable spend. Treat auto-renew with caution — it guarantees coverage but re-locks the original shape, so only use it on workloads you're certain are stable.
4. Stagger expiries so you never face a single large cliff
Deliberately offset the end dates of large commitments by a few months instead of buying them all in one procurement push. Staggering turns one big concentrated spike into a series of small, absorbable renewals, smooths the forecast, and spreads the re-evaluation work across the calendar rather than landing it all in one week. When replacing a clustered set of expiries, vary the new terms on purpose to break the cluster for good.
# Build the expiry calendar: every active EC2 RI sorted by end date, next 90 days highlighted.
CUTOFF=$(date -u -d '+90 days' +%Y-%m-%dT%H:%M:%SZ)
aws ec2 describe-reserved-instances \
--filters Name=state,Values=active \
--query "sort_by(ReservedInstances[?End<='$CUTOFF'],&End)[].[ReservedInstancesId,InstanceType,InstanceCount,End]" \
--output table
# Confirm the covered workload before renewing — is it still the right size?
aws compute-optimizer get-ec2-instance-recommendations \
--query 'instanceRecommendations[].[instanceArn,currentInstanceType,recommendationOptions[0].instanceType]' \
--output table
# Queue the replacement to activate as the old RI lapses (no On-Demand gap).
aws ec2 purchase-reserved-instances-offering \
--reserved-instances-offering-id 4a8b1c2d-1234-5678-9abc-def012345678 \
--instance-count 4 \
--purchase-time 2026-06-30T00:00:00Z Quick quiz
Question 1 of 5A 3-year Standard RI for r5.2xlarge expires in 40 days. The covered workload is still running, but Compute Optimizer shows it's been at 25% CPU for months and part of it migrated to Graviton last quarter. What's the right move?
You scored
0 / 5
Keep learning
Dig deeper into commitment lifecycles, expiry mechanics, and how rate optimisation fits the FinOps lifecycle.
- AWS Reserved Instances User Guide Authoritative reference on RI terms, how the discount applies, queued purchases, and what happens at expiry.
- AWS Savings Plans User Guide How Compute and EC2 Instance Savings Plans work, their terms and end dates, and how they compare with Reserved Instances.
- AWS Cost Explorer — reservation and Savings Plans recommendations How AWS generates purchase recommendations and reports coverage and utilisation — the inputs to a re-evaluation before renewal.
- FinOps Foundation — Cloud Rate and Usage Optimization How commitment management and renewal discipline fit the broader FinOps lifecycle and operating model.
You've completed Manage expiring reservations. You now know why a commitment ending is a silent cost event, how expiry works across RIs, Reserved Nodes, and Savings Plans, the re-evaluate-before-renew discipline that prevents locking in the wrong shape, the queue-purchase pattern that keeps coverage continuous, and why staggering expiries stops a wave of renewals from spiking the bill. The next time the dashboard flags a commitment nearing its end date, you'll have a defensible path from 'expiring soon' to a clean, gap-free replacement.
Back to the library