Session 040 — FinOps: Savings Plans vs Reserved Instances — differences and flexibility
Dependencies: none (standalone topic)
Objective
By the end of this session, you will be able to compare Compute Savings Plans, EC2 Instance Savings Plans and Reserved Instances by instance flexibility, regional vs zonal scope and discount; calculate the ROI of each option for a specific workload profile; and understand the risk of over-commitment.
Context
[FACT] On-Demand pricing is the default model: you pay per hour of usage with no commitment. AWS offers substantial discounts in exchange for a 1 or 3-year usage commitment. The two main mechanisms are Savings Plans (commitment in $/hour of usage) and Reserved Instances (commitment in specific capacity).
[FACT] AWS launched Savings Plans in November 2019 as an evolution of Reserved Instances, with greater flexibility. AWS itself recommends Savings Plans over Reserved Instances for most use cases — but Reserved Instances still offer specific advantages (capacity reservation, zonal discount).
Key concepts
1. Four types of Savings Plans
[FACT] AWS offers four types of Savings Plans:
Tipo Desconto max Serviços cobertos
────────────────────────────────────────────────────────────────────
Compute Savings Plans até 66% EC2 (qualquer família/tamanho/
região/OS/tenancy), Fargate,
Lambda
EC2 Instance até 72% EC2 dentro de uma família
Savings Plans específica em uma região
específica (ex: m5 em us-east-1)
— qualquer tamanho/OS/tenancy
Database Savings Plans até 35% Aurora, RDS, DynamoDB,
ElastiCache, DocumentDB,
Timestream, Neptune, Keyspaces,
DMS, OpenSearch
SageMaker AI até 64% SageMaker AI — qualquer família/
Savings Plans tamanho/região/componente
────────────────────────────────────────────────────────────────────
[FACT] Savings Plans commit a value in $/hour (not a number of instances). If you use more than the committed amount, the excess is charged at On-Demand. If you use less, you pay for the committed amount even without usage.
2. Types of Reserved Instances (RIs) for EC2
[FACT] Reserved Instances for EC2 have two main types by flexibility scope:
Tipo Desconto max Flexibilidade
────────────────────────────────────────────────────────────────────
Standard RI até 72% Família, tamanho, OS e região
fixos. Size flexibility em RIs
regionais (dentro da família).
Pode vender no Marketplace.
Convertible RI até 66% Pode fazer exchange para mudar
família, tamanho, OS ou tenancy.
Exchange é manual (não automático).
NÃO pode vender no Marketplace.
────────────────────────────────────────────────────────────────────
[FACT] RI Scope — Regional vs. Zonal:
Escopo Regional (recomendado):
- Desconto aplicado a qualquer instância na região
- Instance size flexibility: desconto se aplica
automaticamente a outros tamanhos da mesma família
(ex: comprou m5.large — aplica em m5.xlarge, m5.2xlarge etc.)
- SEM reserva de capacidade garantida
Escopo Zonal (específico por AZ):
- Desconto aplicado apenas à AZ especificada
- SEM size flexibility (instância exata)
- COM reserva de capacidade garantida nessa AZ
- Pode ser vendido no Marketplace (Standard RI)
3. Full comparison table
[FACT — AWS Docs]:
Compute EC2 Instance Convertible Standard
SP SP RI RI
─────────────────────────────────────────────────────────────────────────
Desconto máx. 66% 72% 66% 72%
Compromisso $/hora $/hora # instâncias # instâncias
Família flexível ✓ — manual —
Tamanho flexível ✓ ✓ manual regional only
OS flexível ✓ ✓ manual —
Tenancy flexível ✓ ✓ manual —
Cross-region ✓ — — —
Cobre Fargate ✓ — — —
Cobre Lambda ✓ — — —
Capacity reservation — — zonal only zonal only
Cancelamento — — — —
Exchange/modificação N/A N/A ✓ (manual) limitado
Venda no Marketplace — — — ✓ (zonal)
─────────────────────────────────────────────────────────────────────────
✓ = automático; — = não disponível; manual = requer ação explícita
4. Payment options and discount impact
[FACT] All types of Savings Plans and RIs offer three payment options:
Pagamento Compromisso Desconto
────────────────────────────────────────────────────────────────
All Upfront 100% adiantado Máximo (66%, 72% etc.)
Partial Upfront ~50% adiantado Intermediário
No Upfront Zero adiantado Menor (ainda substancial)
Prazo de 3 anos > prazo de 1 ano em desconto para todas as opções.
Exemplo (m5.xlarge Linux us-east-1):
On-Demand: $0.192/hora = $1.684/mês
Standard RI 1y All Up: $0.110/hora = $965/mês → 43% off
Standard RI 3y All Up: $0.069/hora = $605/mês → 64% off
Compute SP 1y All Up: $0.116/hora = $1.018/mês → 40% off
Compute SP 3y All Up: $0.073/hora = $640/mês → 62% off
EC2 Instance SP 1y: $0.109/hora = $957/mês → 43% off
[UNCERTAIN] The exact discount values vary by region, instance family and OS — always check the AWS pricing calculator for your specific case, as percentages change periodically.
5. How Savings Plans are applied
[FACT] The application of Savings Plans follows a hierarchy:
Ordem de aplicação:
1. Savings Plans cobrem primeiro as instâncias com MENOR preço
On-Demand no seu portfólio (maximiza cobertura)
2. EC2 Instance SP (mais específico) é aplicado ANTES de
Compute SP (mais genérico)
3. O que exceder o compromisso $/hora → cobrado On-Demand
Exemplo:
Você tem Compute SP de $1.00/hora (3 anos, no upfront)
Suas instâncias custam:
- m5.large Linux us-east-1: $0.096/hora On-Demand
- c5.xlarge Linux eu-west-1: $0.192/hora On-Demand
Com Compute SP, você paga o preço SP em vez do On-Demand
para as instâncias que cobrem o commitment:
~$1.00/hora → cobre essas instâncias com desconto de ~60%
Acima disso → On-Demand
[FACT] Savings Plans do not provide capacity reservation. If you need guaranteed capacity in a specific AZ (e.g., critical failover), use On-Demand Capacity Reservation (ODCR) — Savings Plans apply on top of ODCR.
6. ROI analysis and over-commitment risk
[CONSENSUS] The central risk of Savings Plans and RIs is over-commitment: committing more than you use. The commitment is irrevocable during the term — you pay even without usage.
Framework de decisão:
─────────────────────────────────────────────────────────────
Baseline usage → Comprometer (Savings Plans / RI)
Variável mas previsível → Comprometer baseline, On-Demand para picos
Imprevisível → On-Demand (ou Spot para tolerantes a interrupção)
Regra prática:
Comprometa no máximo o uso do seu P10 (percentil 10)
— o uso que você tem 90% do tempo como mínimo garantido.
Nunca comprometa o uso médio se houver sazonalidade,
pois nos períodos abaixo da média você paga sem receber.
Calculating break-even (Savings Plans 1 year vs. On-Demand):
On-Demand: $0.192/hora × 8.760h/ano = $1.682/ano
Compute SP 1y: $0.116/hora × 8.760h/ano = $1.016/ano (66% utilização)
Break-even: você precisa usar a instância pelo menos X% do tempo
para o SP ser mais barato que On-Demand:
Break-even % = SP_price / OD_price = 0.116 / 0.192 = 60.4%
→ Se a instância ficar ativa > 60.4% do tempo: SP é mais barato
→ Se ficar ativa < 60.4% do tempo: On-Demand seria mais barato
Practical example
Scenario: evaluating the FinOps strategy for an e-commerce platform
Workload:
- 10 instances m5.xlarge Linux in us-east-1 running 24/7 (stable web tier)
- 5 instances c5.2xlarge Linux in us-east-1 running 70% of the time (batch processing)
- Lambda functions (1,000,000 invocations/month, 512MB, 500ms avg duration)
- Fargate tasks (3,000 vCPU-hours/month, 6,000 GB-hours/month)
CDK Python — Cost Anomaly Detection + Budget alert
from aws_cdk import (
Stack,
aws_budgets as budgets,
aws_ce as ce, # Cost Explorer
)
from constructs import Construct
class FinOpsCostControlStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs):
super().__init__(scope, construct_id, **kwargs)
# ── Budget: alerta quando custo mensal exceder $10.000 ────────
monthly_budget = budgets.CfnBudget(
self, "MonthlyComputeBudget",
budget=budgets.CfnBudget.BudgetDataProperty(
budget_name="monthly-compute-budget",
budget_type="COST",
time_unit="MONTHLY",
budget_limit=budgets.CfnBudget.SpendProperty(
amount=10000,
unit="USD",
),
cost_filters={
"Service": ["Amazon Elastic Compute Cloud - Compute",
"AWS Lambda",
"AWS Fargate"],
},
cost_types=budgets.CfnBudget.CostTypesProperty(
include_tax=True,
include_subscription=True,
use_blended=False,
),
),
notifications_with_subscribers=[
budgets.CfnBudget.NotificationWithSubscribersProperty(
notification=budgets.CfnBudget.NotificationProperty(
comparison_operator="GREATER_THAN",
notification_type="ACTUAL", # custo real (não forecast)
threshold=80, # 80% do budget
threshold_type="PERCENTAGE",
),
subscribers=[
budgets.CfnBudget.SubscriberProperty(
address="finops@company.com",
subscription_type="EMAIL",
),
],
),
budgets.CfnBudget.NotificationWithSubscribersProperty(
notification=budgets.CfnBudget.NotificationProperty(
comparison_operator="GREATER_THAN",
notification_type="FORECASTED", # forecast excede budget
threshold=100,
threshold_type="PERCENTAGE",
),
subscribers=[
budgets.CfnBudget.SubscriberProperty(
address="finops@company.com",
subscription_type="EMAIL",
),
],
),
],
)
# ── Cost Anomaly Detection ────────────────────────────────────
# Detecta anomalias de custo automaticamente (ML-based)
anomaly_monitor = ce.CfnAnomalyMonitor(
self, "ComputeCostMonitor",
monitor_name="compute-cost-monitor",
monitor_type="DIMENSIONAL",
monitor_dimension="SERVICE",
)
anomaly_subscription = ce.CfnAnomalySubscription(
self, "ComputeAnomalyAlert",
subscription_name="compute-anomaly-alert",
monitor_arn_list=[anomaly_monitor.attr_monitor_arn],
subscribers=[
ce.CfnAnomalySubscription.SubscriberProperty(
address="finops@company.com",
type="EMAIL",
status="CONFIRMED",
),
],
# Alerta quando impacto total exceder $100
threshold_expression=ce.CfnAnomalySubscription.ExpressionProperty(
dimensions=ce.CfnAnomalySubscription.DimensionValuesProperty(
key="ANOMALY_TOTAL_IMPACT_ABSOLUTE",
values=["100"],
match_options=["GREATER_THAN_OR_EQUAL"],
)
),
frequency="DAILY",
)
ROI calculation — Python (offline analysis)
"""
Script de análise FinOps: calcula ROI de Savings Plans vs On-Demand
para o perfil de workload descrito no cenário.
Preços aproximados us-east-1 (verificar sempre no pricing calculator):
https://aws.amazon.com/ec2/pricing/reserved-instances/pricing/
"""
from dataclasses import dataclass
from typing import Literal
@dataclass
class WorkloadComponent:
name: str
on_demand_hourly: float
utilization_pct: float # 0.0 – 1.0
count: int
hours_per_year: float = 8760.0
def annual_on_demand_cost(w: WorkloadComponent) -> float:
return w.on_demand_hourly * w.utilization_pct * w.hours_per_year * w.count
def annual_savings_plan_cost(
w: WorkloadComponent,
sp_hourly_rate: float,
) -> float:
"""
Savings Plan é sempre cobrado pelo compromisso (sp_hourly_rate * count),
independente da utilização — por isso over-commitment é perigoso.
"""
committed = sp_hourly_rate * w.hours_per_year * w.count
return committed
def break_even_utilization(
on_demand_rate: float,
sp_rate: float,
) -> float:
"""Utilização mínima para SP ser mais barato que On-Demand."""
return sp_rate / on_demand_rate
def analyze_workload():
# ── Workload components ───────────────────────────────────────────
web_tier = WorkloadComponent(
name="Web tier (m5.xlarge, 10x)",
on_demand_hourly=0.192, # m5.xlarge Linux us-east-1
utilization_pct=1.0, # 100% — sempre ligado
count=10,
)
batch_tier = WorkloadComponent(
name="Batch tier (c5.2xlarge, 5x)",
on_demand_hourly=0.340, # c5.2xlarge Linux us-east-1
utilization_pct=0.70, # 70% do tempo
count=5,
)
# ── Preços aproximados Savings Plans 1y No Upfront ────────────────
# NOTA: sempre verifique o pricing atual em aws.amazon.com
WEB_SP_RATE = 0.116 # Compute SP ~40% off On-Demand (m5.xlarge)
BATCH_SP_RATE = 0.204 # Compute SP ~40% off On-Demand (c5.2xlarge)
print("=" * 65)
print("ANÁLISE FINOPS — Savings Plans vs On-Demand")
print("=" * 65)
for workload, sp_rate in [(web_tier, WEB_SP_RATE), (batch_tier, BATCH_SP_RATE)]:
od_annual = annual_on_demand_cost(workload)
sp_annual = annual_savings_plan_cost(workload, sp_rate)
savings = od_annual - sp_annual
be_util = break_even_utilization(workload.on_demand_hourly, sp_rate)
print(f"\n{workload.name}")
print(f" On-Demand anual: ${od_annual:,.0f}")
print(f" Savings Plan anual: ${sp_annual:,.0f}")
print(f" Economia: ${savings:,.0f} ({savings/od_annual*100:.1f}%)")
print(f" Break-even utilização: {be_util*100:.1f}%")
print(f" Utilização atual: {workload.utilization_pct*100:.0f}%")
print(f" → {'✓ SP RECOMENDADO' if workload.utilization_pct > be_util else '✗ On-Demand mais barato'}")
# ── Risco de over-commitment no batch tier ────────────────────────
print("\n--- ANÁLISE DE RISCO (Batch tier) ---")
print("Cenário: utilização cai para 40% (metade do esperado)")
batch_worst = WorkloadComponent(
name="Batch tier — worst case",
on_demand_hourly=0.340,
utilization_pct=0.40, # pior caso
count=5,
)
od_worst = annual_on_demand_cost(batch_worst)
sp_annual = annual_savings_plan_cost(batch_tier, BATCH_SP_RATE)
waste = sp_annual - od_worst
print(f" On-Demand (40% util): ${od_worst:,.0f}")
print(f" Savings Plan (fixo): ${sp_annual:,.0f}")
print(f" Desperdício: ${waste:,.0f} (pagando sem usar)")
print(f" → RECOMENDAÇÃO: comprometa apenas o baseline seguro (P10)")
if __name__ == "__main__":
analyze_workload()
CLI — Check recommendations and purchase Savings Plan
# 1. Obter recomendações de Savings Plans do Cost Explorer (AWS CLI)
aws ce get-savings-plans-purchase-recommendation \
--savings-plans-type COMPUTE_SP \
--term-in-years ONE_YEAR \
--payment-option NO_UPFRONT \
--lookback-period-in-days THIRTY_DAYS \
--query 'SavingsPlansPurchaseRecommendation.{
Summary:SavingsPlansPurchaseRecommendationSummary,
Details:SavingsPlansPurchaseRecommendationDetails[0:3]
}'
# 2. Ver utilização atual de Savings Plans existentes
aws ce get-savings-plans-utilization \
--time-period '{"Start":"2026-06-01","End":"2026-06-30"}' \
--query 'Total.{
Utilized:Utilization.UtilizationPercentage,
UnusedHours:Savings.NetSavings,
TotalCommitment:Utilization.TotalCommitment
}'
# 3. Ver cobertura de Savings Plans (% do On-Demand coberto)
aws ce get-savings-plans-coverage \
--time-period '{"Start":"2026-06-01","End":"2026-06-30"}' \
--granularity MONTHLY \
--query 'SavingsPlansCoverages[*].Coverage.{
CoverageHoursPercentage:CoverageHoursPercentage,
SpendCoveredBySavingsPlans:SpendCoveredBySavingsPlans,
OnDemandCost:OnDemandCost
}'
# 4. Comprar Savings Plan (CUIDADO — compromisso irrevogável)
# Sempre verificar recomendação do Cost Explorer antes
aws savingsplans create-savings-plan \
--savings-plan-offering-id "offering-id-from-describe" \
--commitment 1.00 \
--purchase-time "2026-07-01T00:00:00Z" \
--client-token "unique-idempotency-key-$(uuidgen)"
# 5. Listar Savings Plans ativos
aws savingsplans describe-savings-plans \
--states ACTIVE \
--query 'savingsPlans[*].{
Id:savingsPlanId,
Type:savingsPlanType,
Commitment:commitment,
Start:start,
End:end,
State:state,
PaymentOption:paymentOption,
Term:termDurationInSeconds
}'
# 6. Ver recomendações de Reserved Instances
aws ce get-reservation-purchase-recommendation \
--service "Amazon EC2" \
--lookback-period-in-days THIRTY_DAYS \
--term-in-years ONE_YEAR \
--payment-option NO_UPFRONT \
--query 'Recommendations[0:3].{
Summary:RecommendationSummary,
Details:RecommendationDetails[0:2]
}'
# 7. Ver utilização de Reserved Instances
aws ce get-reservation-utilization \
--time-period '{"Start":"2026-06-01","End":"2026-06-30"}' \
--query 'Total.UtilizationsByTime[*].{
UtilizationPct:Utilization.UtilizationPercentage,
UnusedHours:Utilization.UnusedHours
}'
Common pitfalls
1. Over-commitment: committing the average, not the baseline
[CONSENSUS] The most common pitfall in FinOps is buying Savings Plans or RIs based on the average usage of the last 30 days, without considering seasonality. A workload with a peak of 100 instances on Black Friday and a minimum of 20 instances in July should commit only the 20 baseline instances — not the average. Over-commitment wastes capital on idle capacity.
2. Savings Plans do not provide capacity reservation
[FACT] Unlike zonal Reserved Instances, Savings Plans do not guarantee that capacity will be available. In regional scarcity scenarios (e.g., during large-scale events or outages), On-Demand and Savings Plans compete for available capacity. For critical workloads with availability SLAs, combine Savings Plans with On-Demand Capacity Reservation (ODCR).
3. Savings Plans do not cover Spot usage
[FACT] Savings Plans and RIs are applied only to On-Demand and ODCR usage. Spot hours do not receive additional Savings Plans discounts — they already have their own discounted price. Planning Savings Plans coverage over workloads you intend to move to Spot results in unnecessary commitment.
4. Convertible RIs require manual exchange — they are not automatic
[FACT] Unlike EC2 Instance Savings Plans (which apply automatically to any size within the family), Convertible RIs require you to manually execute the exchange via console or API when you want to change family, size or OS. Forgetting to perform the exchange means the old RI remains unused while you pay On-Demand for the new instances.
5. Standard RI zonal: size flexibility absent
[FACT] Standard RIs with Zonal scope do not have size flexibility — the discount only applies to exactly the size, OS and tenancy specified at purchase. RIs with Regional scope have size flexibility within the family. To get size flexibility with RI, always use Regional scope.
6. Savings Plans are applied sequentially, not in parallel
[FACT] If you have multiple Savings Plans (e.g., EC2 Instance SP and Compute SP), the system applies the EC2 Instance SP (more specific) first, then the Compute SP for the remainder. Buying both without analyzing the overlap can result in redundant coverage paying for the Compute SP without utilizing it.
Reflection exercise
Your company has the following EC2 usage profile in us-east-1 (data from the last 90 days):
- Stable instances: 20 x
m5.2xlargeLinux running 100% of the time (web application) - Processing instances: 10 x
c5.4xlargeLinux running on average 60% of the time, with a minimum of 40% and maximum of 90% (daytime jobs) - ML instances: 3 x
p3.2xlargeLinux running on average 30% of the time (weekly training)
Answer:
-
For the stable instances, what is the annual financial difference between Compute Savings Plans 1 year All Upfront (~60% discount) and Standard RI 1 year All Upfront (~64% discount), considering
m5.2xlargeOn-Demand at $0.384/hour? When does the flexibility of Compute SP justify the discount difference? -
For the processing instances, what would be the safe Savings Plans commitment considering the risk of over-commitment? Justify using the P10 concept (10th percentile of utilization = 40%).
-
For the ML instances (
p3.2xlarge, 30% average utilization): does the break-even of Compute SP (~60% discount) justify the commitment? What would be the most suitable alternative for this type of workload?
Resources for further study
- [FACT] Savings Plans types (Compute, EC2 Instance, Database, SageMaker): https://docs.aws.amazon.com/savingsplans/latest/userguide/plan-types.html
- [FACT] Compute Savings Plans vs Reserved Instances (comparison table): https://docs.aws.amazon.com/savingsplans/latest/userguide/sp-ris.html
- [FACT] How Savings Plans apply to usage: https://docs.aws.amazon.com/savingsplans/latest/userguide/sp-applying.html
- [FACT] Reserved Instances for EC2 (regional vs zonal scope, Standard vs Convertible): https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-reserved-instances.html
- [UNCERTAIN] AWS Pricing Calculator (current prices — check before committing): https://calculator.aws/pricing/2/home