Cloud Reserved Instance Strategy: Commitment-Based Discounts That Actually Save Money

How to build a reserved instance and savings plan strategy that maximizes cloud discounts without over-committing — covering AWS, Azure, and GCP commitment models.

Cloud Reserved Instance Strategy: Commitment-Based Discounts That Actually Save Money

A cloud reserved instance strategy is a disciplined approach to committing to compute capacity over a fixed term—typically one or three years—in exchange for significant per-hour discounts. It matters when workloads are stable, predictable, and long-lived: batch processing jobs, backend services, data warehouses, and database clusters that run continuously. The strategy is not merely about buying reserved instances; it is about aligning commitment terms with workload patterns, avoiding overcommitment, and capturing savings through coordinated provisioning, scaling, and renewal.

Planning the Commitment

The core of the strategy is defining the right commitment profile: instance type, region, duration, and scope. Each instance type has a distinct hourly rate, and the reservation provides a discount relative to the on-demand price. The decision must account for usage patterns, future load growth, and the cost of under- or over-provisioning.

To begin, extract historical usage data from the cloud provider’s metrics API. For example, in AWS, query the CloudWatch GetMetricStatistics endpoint with the InstanceType, CPUUtilization, and NetworkIn metrics over the past 180 days. Use curl to retrieve data:

curl -s \
  -H "Authorization: Bearer $AWS_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "Namespace": "AWS/EC2",
    "MetricDataQueries": [
      {
        "Id": "cpu",
        "MetricStat": {
          "Metric": {
            "Namespace": "AWS/EC2",
            "MetricName": "CPUUtilization",
            "Dimensions": [
              { "Name": "InstanceId", "Value": "i-1234567890abcdef0" }
            ]
          },
          "Period": 3600,
          "Stat": "Average"
        }
      }
    ],
    "StartTime": "2024-01-01T00:00:00Z",
    "EndTime": "2024-06-30T23:59:59Z"
  }' \
  https://monitoring.amazonaws.com

The response includes Datapoints with Timestamp, Average, and Unit. Aggregate these across all relevant instances to compute average CPU utilization per instance type.

From this, identify the instance types that are consistently utilized—say, c5.4xlarge at 60–80% CPU for 80% of the time—and determine the total expected instance-hours over the next 36 months.

Selecting the Right Reservation Type

Cloud providers offer multiple reservation types: standard, convertible, and scheduled. Each has distinct trade-offs in flexibility and discount.

Use curl to create a standard reservation for c5.4xlarge in us-west-2 for 3 years:

curl -s \
  -H "Authorization: Bearer $AWS_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "InstanceType": "c5.4xlarge",
    "AvailabilityZone": "us-west-2a",
    "Duration": 25920000,  -- 3 years in seconds
    "InstanceCount": 5,
    "OfferingType": "Standard",
    "PaymentOption": "AllUpfront"
  }' \
  https://ec2.amazonaws.com

The Duration field is in seconds: 365 × 24 × 3600 = 31,536,000, so 25,920,000 is 300 days (used for 3 years with 10% buffer). The PaymentOption determines upfront cost and discount structure:

The response includes ReservationId, Start, End, and Status. A reservation is Active when Start ≤ Now ≤ End.

Executing the Commitment

Once reserved, the system must consume the instances efficiently. The reservation is a commitment, not a guarantee: if the instance is not running during the reservation period, the provider applies the discount only to actual running hours.

To ensure alignment, create a deployment script that starts instances from a shared AMI and tags them with ReservationId and CommitmentType.

#!/bin/bash


INSTANCE_TYPE=$1
RESERVATION_ID=$2
REGION=$3

aws ec2 run-instances \
  --image-id ami-12345678 \
  --instance-type $INSTANCE_TYPE \
  --count 1 \
  --region $REGION \
  --tag-specifications \
    'ResourceType=instance,Tags=[{Key=ReservationId,Value='$RESERVATION_ID'}, {Key=CommitmentType,Value=Standard}]' \
  --security-groups sg-12345678 \
  --subnet-id subnet-12345678 \
  --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":100,"VolumeType":"gp3"}}]'

The tag-specifications field is a JSON array of ResourceType and Tags, each tag being a key-value pair. The tag ReservationId is used by billing systems to match instances to reservations.

Managing Overlap and Underutilization

A common failure: multiple reservations overlap, but not all instances are running. The reservation is paid, but the instance is idle. The system must track when instances are active and report usage to the cloud provider.

Use aws ec2 describe-instances to query the status:

aws ec2 describe-instances \
  --filters "Name=tag:ReservationId,Values=i-1234567890abcdef0" \
  --query 'Reservations[*].Instances[*].[InstanceId, State.Name, LaunchTime]' \
  --output table

The output includes InstanceId, State.Name (e.g., running, stopped), and LaunchTime. The system must ensure that instances are running during the reservation period.

If an instance is stopped during its reservation window, the provider applies the reservation discount only for the hours it was running. To avoid underutilization, implement a daily check that compares the reservation start/end with instance uptime.

#!/bin/bash
# check-reservation-utilization.sh

RESERVATION_ID=$1
REGION=$2

# Get reservation details
RESERVATION=$(aws ec2 describe-reservations \
  --reservation-ids $RESERVATION_ID \
  --region $REGION \
  --query 'Reservations[0].[Start, End, InstanceCount]' \
  --output json)

# Parse Start and End
START=$(echo "$RESERVATION" | jq -r '.[0]')
END=$(echo "$RESERVATION" | jq -r '.[1]')
INSTANCE_COUNT=$(echo "$RESERVATION" | jq -r '.[2]')

# Query all instances in the reservation
INSTANCES=$(aws ec2 describe-instances \
  --filters "Name=tag:ReservationId,Values=$RESERVATION_ID" \
  --region $REGION \
  --query 'Reservations[*].Instances[*].[InstanceId, State.Name, LaunchTime, TerminationTime]' \
  --output json)

# Compute total running hours
TOTAL_RUNNING_HOURS=0
for instance in $(echo "$INSTANCES" | jq -c '.[]'); do
  INSTANCE_ID=$(echo "$instance" | jq -r '.[0]')
  STATE=$(echo "$instance" | jq -r '.[1]')
  LAUNCH_TIME=$(echo "$instance" | jq -r '.[2]')
  TERMINATION_TIME=$(echo "$instance" | jq -r '.[3]')

  if [[ "$STATE" == "running" ]]; then
    # Running from launch to end of reservation
    RUN_START=$LAUNCH_TIME
    RUN_END=$END
  else
    # Running from launch to termination
    RUN_START=$LAUNCH_TIME
    RUN_END=$TERMINATION_TIME
  fi

  # Calculate difference in seconds
  RUN_DURATION=$(python3 -c "
import datetime
start = datetime.datetime.fromisoformat('$RUN_START')
end = datetime.datetime.fromisoformat('$RUN_END')
print((end - start).total_seconds() / 3600)
")

  TOTAL_RUNNING_HOURS=$(echo "$TOTAL_RUNNING_HOURS + $RUN_DURATION" | bc -l)
done

# Expected hours: instance_count × (end - start) in hours
EXPECTED_HOURS=$(echo "$INSTANCE_COUNT × ($END - $START) / 3600" | bc -l)

# Compare
if (( $(echo "$TOTAL_RUNNING_HOURS < 0.8 * $EXPECTED_HOURS" | bc -l) )); then
  echo "Reservation $RESERVATION_ID underutilized: $TOTAL_RUNNING_HOURS hours vs expected $EXPECTED_HOURS"
fi

This script uses jq to parse JSON output, bc for floating-point arithmetic, and python3 to calculate time differences from ISO 8601 timestamps.

Handling Convertible Reservations

When using convertible reservations, the system must track which instance types are being used and when they are switched.

A reservation can be converted by calling ModifyReservedInstances:

{
  "ReservedInstancesIds": ["r-12345678"],
  "TargetInstanceType": "c5.8xlarge"
}

The provider updates the reservation’s InstanceType and begins applying the discount for c5.8xlarge from the moment of the update.

The key failure mode: a conversion is applied, but the new instance type is not launched in time. The reservation is discounted for c5.8xlarge, but the instance is still c5.4xlarge. The discount is lost.

To catch this, implement a nightly job that compares the reservation’s InstanceType with the actual instance type of its instances:

#!/bin/bash
# validate-convertible-reservation.sh

RESERVATION_ID=$1
REGION=$2

RESERVATION=$(aws ec2 describe-reservations \
  --reservation-ids $RESERVATION_ID \
  --region $REGION \
  --query 'Reservations[0].[InstanceType, InstanceCount, State]' \
  --output json)

CURRENT_TYPE=$(echo "$RESERVATION" | jq -r '.[0]')
EXPECTED_COUNT=$(echo "$RESERVATION" | jq -r '.[1]')
CURRENT_STATE=$(echo "$RESERVATION" | jq -r '.[2]')

# Get all instances in reservation
INSTANCES=$(aws ec2 describe-instances \
  --filters "Name=tag:ReservationId,Values=$RESERVATION_ID" \
  --region $REGION \
  --query 'Reservations[*].Instances[*].[InstanceId, InstanceType]' \
  --output json)

# Check all instances have correct type
for instance in $(echo "$INSTANCES" | jq -c '.[]'); do
  INSTANCE_ID=$(echo "$instance" | jq -r '.[0]')
  INSTANCE_TYPE=$(echo "$instance" | jq -r '.[1]')

  if [[ "$INSTANCE_TYPE" != "$CURRENT_TYPE" ]]; then
    echo "Instance $INSTANCE_ID in reservation $RESERVATION_ID is $INSTANCE_TYPE, expected $CURRENT_TYPE"
  fi
done

If the instance type differs from the reservation’s InstanceType, the system logs a warning and triggers a remediation process.

Renewing and Reallocating Commitments

Reservations can be renewed manually or automatically. The renewal process must account for changes in workload, pricing, and commitment terms.

To renew a reservation, call PurchaseReservedInstancesOffering:

{
  "ReservedInstancesOfferingId": "ro-12345678",
  "InstanceCount": 3,
  "PaymentOption": "AllUpfront",
  "Tenancy": "default",
  "Tags": [
    { "Key": "Renewed", "Value": "true" }
  ]
}

The ReservedInstancesOfferingId is the ID of a new offering, typically generated by DescribeReservedInstancesOfferings and selected based on pricing and availability.

The renewal is not immediate. The new reservation starts at Start time, but the old reservation continues until End. During this overlap, instances may run under two reservations.

To manage this, track the Active period for each reservation and calculate the total commitment cost per instance-hour.

A sharp edge: when an instance is stopped during the overlap period, the provider applies the reservation discount for the instance type in the reservation that was active at the time of the stop.

Thus, a single instance may be charged under multiple reservations simultaneously, but each reservation applies only to the hours the instance was running during that reservation’s active window.

The system must collect usage data per reservation and compute the effective discount per hour.

Final Checks and Failure Modes

The following failures silently reduce savings:

The system must validate each reservation’s Start and End dates against actual instance uptime and instance type history.

A final check: compare the total reservation cost against the total on-demand cost for the same workload. The savings are realized only when the reservation is fully utilized.

The strategy is not just buying reserved instances. It is about ensuring that the commitment is honored—every hour, every instance, every conversion. The money is saved, but only if the mechanism is executed.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.