Photo by Alfred Quartey on Unsplash

Part 6: Demystifying Reserved Instances

Christopher Harris
Understanding the AWS CUR
9 min readJun 25, 2024

--

Breaking down Purchasing and Amortization

My name is Christopher Harris, and I am a maintainer on the FinOps FOCUS project. I’ve built cost management products at Datadog and CloudHealth (Broadcom) over the past 8 years, and I’m excited to share some of what I have learned with you.

Reserved Instances, Reservations, or RIs, offer incredible discounts (up to 77%!) in return for contractually obligated spending over a 1-year or 3-year term. AWS introduced EC2 Reserved Instances in 2009 as its first commitment program to help customers with predictable workloads save big on computing. While having a similar name, On-Demand Capacity Reservations, or ODCRs, do not offer price discounts but offer customers the ability to secure EC2 availability ahead of use.

Eligibility

AWS offers Reserved Instances across 8 native services: EC2, RDS, RedShift, ElastiCache, OpenSearch (Elasticsearch), DynamoDB, Elemental Media Live, and MemoryDB.

Typically, Reserved Instances match against physical or virtual computing infrastructure. For example, customers can match reservations against various instance types like:

  • EC2: m5.2xlarge
  • RDS: db.m5.2xlarge
  • Redshift: dc2.8xlarge
  • ElastiCache: cache.m5.2xlarge
  • OpenSearch (Elasticsearch): m5.2xlarge.search
  • MemoryDB: r6g.8xlarge

In some cases, Reserved Instances match against sets of operations including:

  • DynamoDB: Read-Capacity Units (RCU) & Write-Capacity Units (WCU)
  • Elemental Media Live: Inputs & Outputs

Purchasing

All Reserved Instances require customers to select a term, payment option, and location.

Term

A term typically spans either 1 year (8,760 hours or 31,536,000 seconds) or 3 years (26,280 hours or 94,608,000 seconds). Each second of a Reserved Instance can match an eligible resource or set of operations within a term.

Payment Option

A payment option that defines whether AWS receives payment before and/or during the term:

  • All Upfront: Pay now (# of term hours * hourly commitment)
  • Partial Upfront: Pay 1/2 now (0.5 * # of term hours * hourly commitment)
  • No Upfront: Pay nothing now.

Location

Customers typically choose a region (ex: us-east-1), pinning that Reserved Instance to infrastructure within that region. In some cases for EC2, customers may choose an Availability Zone (AZ), instead of a region, to leverage capacity guarantees instead of size flexibility. Customers with zonal Reserved Instances are guaranteed EC2 capacity without buying ODCRs.

Regional EC2 and RDS Reserved Instances also qualify for instance size flexibility … more on this later.

Special Configurations

On top of general configurations, some services require additional configurations to further specify the type of infrastructure/operations running and/or the type of Reserved Instance purchased. Below is a summary of these configurations.

  • EC2: Operation System (ex: Linux/Unix), Tenancy (ex: Shared or Dedicated)
  • RDS: Database Engine (ex: MySQL)
  • DynamoDB: # of Read-Capacity Units (RCU) & Write-Capacity Units (WCU)
  • Elemental Media Live: Inputs & Outputs

Amortization

For the rest of this article, we’ll consider an AWS account with 1 RDS Reserved Instance (1 year, Partial Upfront, $0.4172 per hour, MySQL) and 1 RDS instance (db.m5.2xlarge, us-east-1, MySQL, single-AZ)

There is never an inherent link between a specific Reserved Instance and a specific resource or set of operations. AWS applies a bin-packing heuristic that matches against eligible, running infrastructure to guarantee the most efficient utilization of commitments on the customer’s behalf.

Reserved Instances

Customers applying a Reserved Instance to cover the entire cost of this RDS instance can commit to 1 Reserved Instance. Unlike Savings Plans, Reserved Instances match against specific infrastructure instead of monetary units.

The annual total cost of this Reserved Instance with an hourly commitment of $0.4172 is $3,654.672 ($0.4172 per hour * 8,760 hours).

After a Reserved Instance is purchased, AWS distributes the Reserved Instance’s pre-committed upfront and/or recurring fees across each second of an eligible, running infrastructure over the Reserved Instance’s term. This is amortization. If the matched infrastructure stops or is terminated, the Reserved Instance can be applied to other eligible, running resources or operations.

In the example below, the left side of the vertical line shows a list of committed spend per hour for all 8,760 hours of our Reserved Instance. The right side of the line shows how our 1 RDS instance was amortized across various instance states: running → green, stopped → orange, terminated → red.

For each hour, here’s what happened:

  • Hours 1–3 → 1 RDS instance ran and amortized 3 RI hours.
  • Hours 4–6 → 1 RDS instance was stopped and 3 RI hours were wasted.
  • Hours 7–8 → 1 RDS instance ran and amortized 2 RI hours.
  • Hours 9–8756 → (not shown) 1 RDS instance ran and amortized 8,748 RI hours.
  • Hour 8,757–8,758 → 1 RDS instance ran and amortized 2 RI hours.
  • Hours 8,759–8,760 → 1 RDS instance was terminated and 2 RI hours were wasted.

As a result, our Reserved Instance amortized 8,755 of 8,760 hours resulting in a 99.94% utilization rate, 29.91% savings against its on-demand rate, and $1.345 accrued waste.

Reserved Instance Total Cost: ($0.4172/hour * 8,760 running hours) = $3,652.586

On-Demand Cost: $0.684 per hour * 8,755 running hours = $5,988.42

Utilization Rate: 8,755 utilized hours / 8,760 committed hours * 100 = 99.94%

Total Savings: (1 - $3,654.672 / $5,988.42) * 100 = 38.97%

Accrued Waste: $0.4172 * 5 hours = $2.086

Amortization within the CUR

There are 3 Charge Types (lineItem/LineItemType) associated with Reserved Instances within the CUR:

  • Fee → The amount paid upfront at the beginning of the term for All Upfront or Partial Upfront Reserved Instance.
  • RIFee → The amount paid monthly across the Reserved Instance’s term ($0 for an All Upfront Reserved Instance).
  • DiscountedUsage → The amortized, discounted rate applied to an eligible resource or operation during the usage period.

Below is the same diagram as above, but with red and blue annotations denoting how each Reserved Instance charge type applies to either Purchasing or Usage, respectively.

Fee

Since the Reserved Instance chosen is configured with a Partial Upfront payment option, a single Fee row is created at the beginning of the Reserved Instance’s term.

Note that the lineItem/LineItemType value, Fee, is generally used and does not always apply to Reserved Instance fees. To easily identify whether a Fee is tied to a Reserved Instance, and thus amortizable, look for a non-null value existing for reservation/ReservationARN, as shown below.

Below is an abbreviated CUR showing 1 row at the time of purchase for half (Partial Upfront) of the committed amount across the 1-year term.

See more about amortized costs in my first article, Analyzing AWS’ cost measures.

+-------------------------+-------------------------+------------------------+--------------------------------------------------------------+
| lineItem/UsageStartDate | lineItem/LineItemType | lineItem/UnblendedCost | reservation/ReservationARN |
+-------------------------+-------------------------+------------------------+--------------------------------------------------------------+
| 20230101T00:00:00Z | Fee | 1827.336 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
+-------------------------+-------------------------+------------------------+--------------------------------------------------------------+

RDS Reserved Instance Upfront Fee:
$0.4172 per hour * 8,760 hours * 0.5 = $1,827.336

RIFee

All yellow boxes within the red annotation in the above diagram represent the Reserved Instance’s unutilized spending. The CUR tracks the Reserved Instance’s utilization under a row with an RIFee charge type. Unlike Savings Plan’s recurring fees that repeat at the CUR’s selected granularity, only 1 RIFee row exists per Reserved Instance subscription per billing period. Because this row is backdated to the first interval of the billing period and is updated throughout the month, this is one reason why it’s important to always analyze the latest CUR for the most accurate cost and usage information of a billing period.

Additionally, RDS (and EC2 regional) Reserved Instances benefit from size flexibility allowing for a single Reserved Instance to cover partial or multiple hosts, depending on their size. For example, 1 db.m5.4xlarge Reserved Instance can cover 2 db.m5.2xlarge instances per hour, and conversely, a db.m5.2xlarge Reserved Instance can cover 1/2 of a db.m5.4xlarge instance.

Columns, reservation/UnusedQuantity and reservation/TotalReservedNormalizedUnits, show normalized hours. A normalized hour is a unit hour multiplied by a predetermined size normalization factor or coefficient. The formula is:

reservation/UnusedQuantity: 
# of unused hours * normalization size factor * reservation/NumberOfReservations

reservation/TotalReservedNormalizedUnits:
# of committed hours * normalization size factor * reservation/NumberOfReservations

In terms of our RDS instance, db.m5.2xlarge, its predetermined size normalization factor is 16 and only 1 reservation was purchased, so 1 hour translates to 16 normalized hours: 1 hour * 16 * 1 reservation = 16 normalized hours.

The abbreviated CUR below shows the Reserved Instance’s utilization for each of the 12 monthly billing periods of the Reserved Instance’s term. Fully utilized months show a 0 reservation/UnusedQuantity while underutilized months show a > 0 reservation/UnusedQuantity.

+-------------------------+-----------------------+----------------------------+------------------------------------------+--------------------------------------------------------------+----------------------------------+
| lineItem/UsageStartDate | lineItem/LineItemType | reservation/UnusedQuantity | reservation/TotalReservedNormalizedUnits | reservation/ReservationARN | reservation/NumberOfReservations |
+-------------------------+-----------------------+----------------------------+------------------------------------------+-------------------------------------------------------------------------------------------------+
| 20230101T00:00:00Z | RIFee | 48 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230201T00:00:00Z | RIFee | 0 | 10752 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230301T00:00:00Z | RIFee | 0 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230401T00:00:00Z | RIFee | 0 | 11520 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230501T00:00:00Z | RIFee | 0 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230601T00:00:00Z | RIFee | 0 | 11520 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230701T00:00:00Z | RIFee | 0 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230801T00:00:00Z | RIFee | 0 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20230901T00:00:00Z | RIFee | 0 | 11520 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20231001T00:00:00Z | RIFee | 0 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20231101T00:00:00Z | RIFee | 0 | 11520 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
| 20231201T00:00:00Z | RIFee | 32 | 11904 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 | 1 |
+-------------------------+-----------------------+----------------------------+------------------------------------------+--------------------------------------------------------------+----------------------------------+

Normalized Units per month:
- Months with 31 days: 24 hours * 31 days * 16 = 11904 normalized units
- Months with 30 days: 24 hours * 30 days * 16 = 11520 normalized units
- Feb 2023: 24 hours * 28 days * 16 = 10752 normalized units

--

Utilization Rates:
Jan 2023:
(1 - 3 unused hours * 16 * 1 / 11904 total hours) * 100 = 99.60% utilization
Feb 2023: 100% utilization
Mar 2023: 100% utilization
Apr 2023: 100% utilization
May 2023: 100% utilization
Jun 2023: 100% utilization
Jul 2023: 100% utilization
Aug 2023: 100% utilization
Sep 2023: 100% utilization
Oct 2023: 100% utilization
Nov 2023: 100% utilization
Dec 2023:
(1 - 2 unused hours * 16 * 1 / 11904 total hours) * 100 = 99.73% utilization

--

Totals:
Total Commitment Wasted: 80 unused hours / 16 (size factor) * $0.4172 = $2.086
Total Commitment Used: $1,826.293 - $2.086 = $1,824.207
Total Commitment Utilization: $1,824.207 / $1,826.293 * 100 = 99.89%

A Reserved Instance’s utilization can be queried as (Athena):

SELECT
YEAR(bill_billing_period_start_date) AS year,
MONTH(bill_billing_period_start_date) AS month,
(
SUM(1 - reservation_unused_quantity) /
SUM(reservation_total_reserved_normalized_units) * 100
) AS utilization_rate
FROM
<cur>
WHERE
line_item_line_item_type = 'RIFee'
GROUP BY
1,2
ORDER BY
1,2

DiscountedUsage

All green boxes within the blue annotation denote where the RDS Reserved Instance is amortized across the running RDS instance. Instead of paying the on-demand rate of $0.684 per hour, the discounted rate ($0.4172 per hour) from the Reserved Instance is amortized for each hour and the customer effectively saves 39% off the list price.

When calculating the (net) amortized cost for a DiscountedUsage charge type, the reservation/EffectiveCost (or reservation/NetEffectiveCost) is used instead of the lineItem/UnblendedCost (or lineItem/NetUnblendedCost).

Unlike Savings Plans, Reserved Instances apply a $0 lineItem/UnblendedCost (or lineItem/NetUnblendedCost), so costs during aggregation are not double-counted, and no negation charge type, like a RINegation, is needed.

For more information on Savings Plans’ calculations, see my previous article on Demystifying Savings Plans.

The abbreviated CUR below shows how each charge type is represented, and DiscountedUsage rows only exist when amortization occurs.

+-------------------------+-------------------------+----------------------------+-----------------------------------+--------------------------------------------------------------+
| lineItem/UsageStartDate | lineItem/LineItemType | lineItem/UnblendedCost | reservation/EffectiveCost | reservation/ReservationARN |
+-------------------------+-------------------------+----------------------------+-----------------------------------+--------------------------------------------------------------+
| 20230101T00:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| 20230101T01:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| 20230101T02:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| 20230101T06:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| 20230101T07:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| ... 8,748 hours ... | DiscountedUsage | 0.0 | 3649.6656 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| 20231231T08:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
| 20231231T09:00:00Z | DiscountedUsage | 0.0 | 0.4172 | arn:aws:rds:us-east-1:123456789012:reserved-instances/abc123 |
+-------------------------+-------------------------+----------------------------+-----------------------------------+--------------------------------------------------------------+
8,755 hours 0.0 3652.586

Hopefully, this article clarifies some of the complexities of how Reserved Instances are purchased and amortized within the CUR. Please feel free to comment with any questions or for additional clarification.

Check out my other stories about Understanding the AWS CUR.

--

--

Christopher Harris
Understanding the AWS CUR

I am a maintainer on the FinOps FOCUS project and have built cost management products at Datadog and CloudHealth (Broadcom) over the past 8 years.