Advanced Cloud Cost Optimization Strategy Teams Overlook
Why a static rightsizing approach often falls short
Most FinOps teams start with a one‑time right‑sizing sprint: they pull a list of under‑utilized EC2 instances, change the instance type, and call it a day. The effort feels tangible, but the savings quickly plateau because usage patterns are not static. Workloads that are idle at night may spike during a nightly batch, and new services are constantly being spun up. A static snapshot cannot capture these dynamics, leading to two common problems:
- Re‑introducing waste – After the initial right‑size, a workload may again become under‑utilized, but no process revisits it.
- Missing discount opportunities – Compute Savings Plans and EC2 Spot Fleet allocations are most effective when usage is predictable and continuously aligned with actual demand.
The overlooked tactic is to close the loop: continuously collect utilization data, translate it into actionable schedule changes, and apply those changes automatically. The loop runs on a daily cadence, ensuring that rightsizing decisions evolve with the workload.
The dynamic rightsizing loop concept
A dynamic rightsizing loop consists of four tightly coupled components:
| Component | Role | Typical AWS service |
|---|---|---|
| Data collector | Captures CPU, memory, network, and storage metrics | aws compute-optimizer + CloudWatch |
| Decision engine | Maps metrics to instance‑type or schedule recommendations | Lambda function (Python/Node) |
| Scheduler | Enforces start/stop or instance‑type changes based on recommendations | AWS Instance Scheduler or EventBridge + SSM |
| Feedback monitor | Verifies that the change produced the expected cost/usage outcome | Cost Explorer + CloudWatch Alarms |
When these pieces are wired together, the loop runs automatically:
- Collect the latest utilization data.
- Analyze it against Compute Optimizer recommendations.
- Translate the analysis into a concrete schedule (e.g., stop the instance at 20:00 UTC, start at 08:00 UTC, or switch from
t3.largetot3.medium). - Apply the schedule via EventBridge rules or the Instance Scheduler.
- Validate the cost impact and feed the result back into the next iteration.
The loop can be scoped per‑account, per‑environment, or per‑tag, giving teams granular control without manual intervention.
Step 1: Gather utilization data with Compute Optimizer
AWS Compute Optimizer provides machine‑learning‑driven recommendations for EC2, Auto Scaling groups, EBS volumes, and Lambda functions. To use it for the loop, you need a read‑only view of the recommendation data.
- Enable the service (if not already enabled) in the target account:
bash aws compute-optimizer enable-recommendations --resource-types EC2,AutoScalingGroup - Export the latest recommendations to an S3 bucket for downstream processing:
bash aws compute-optimizer get-recommendation-summaries \ --output json \ > /tmp/co-summary.json aws s3 cp /tmp/co-summary.json s3://my-cost-optimizations/compute-optimizer/$(date +%Y-%m-%d).json - Pull detailed instance recommendations for a specific region:
bash aws compute-optimizer get-ec2-instance-recommendations \ --region us-east-1 \ --output json \ > /tmp/ec2-recs.json - Store the JSON in a DynamoDB table keyed by
instance-idandrecommendation-datefor quick lookup by the Lambda decision engine.
Tip: Use the free AWS waste finder tool at
/tools/aws-waste-finderto get a quick inventory of idle resources before you start the loop.
Step 2: Translate recommendations into schedule policies
Compute Optimizer returns a list of potential instance types with an estimated monthly savings percentage. The decision engine must decide whether to stop/start an instance (if the workload is truly idle) or change the instance type (if it is over‑provisioned).
2.1 Define thresholds
| Metric | Threshold for stop/start | Threshold for type downgrade |
|---|---|---|
| CPU Utilization (average 7‑day) | < 5% | > 30% unused capacity (e.g., t3.large → t3.medium) |
| Network In (bytes) | < 1 KB/s | < 10 KB/s |
| EBS Read/Write Ops | < 10 ops/s | < 20 ops/s |
These thresholds are conservative; adjust them based on SLAs.
2.2 Build the schedule JSON
The Instance Scheduler expects a JSON document per tag key. Example for a schedule=nightly tag:
{
"name": "nightly",
"description": "Stop non‑critical instances at night",
"timezone": "UTC",
"periods": [
{"start": "20:00", "end": "08:00"}
]
}
When the decision engine decides an instance should be stopped, it adds the schedule=nightly tag to the instance via the SSM aws:runShellScript document.
Step 3: Automate schedule creation with Lambda
The Lambda function runs once per day (triggered by an EventBridge rule at 02:00 UTC). Its responsibilities are:
- Read the DynamoDB table of Compute Optimizer recommendations.
- Apply the thresholds defined in Step 2.1.
- Tag instances that meet the stop/start criteria with the appropriate schedule tag.
- Invoke the Instance Scheduler API to refresh the schedule definitions.
Sample Lambda code (Python 3.9)
import boto3, json, os
ddb = boto3.resource('dynamodb')
ssm = boto3.client('ssm')
co_table = ddb.Table(os.getenv('CO_TABLE'))
THRESHOLD_CPU = 5.0
THRESHOLD_NET = 1_000 # bytes per second
def lambda_handler(event, context):
resp = co_table.scan()
for item in resp['Items']:
cpu = float(item.get('cpuUtilizationAvg', 0))
net = float(item.get('networkInAvg', 0))
instance_id = item['instanceId']
if cpu < THRESHOLD_CPU and net < THRESHOLD_NET:
tag_instance(instance_id, 'schedule', 'nightly')
return {'status': 'complete'}
def tag_instance(instance_id, key, value):
ssm.send_command(
InstanceIds=[instance_id],
DocumentName='AWS-RunShellScript',
Parameters={'commands': [
f"aws ec2 create-tags --resources {instance_id} --tags Key={key},Value={value}"
]}
)
Deploy the function with the following IAM policy (minimum‑privilege):
{
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": ["dynamodb:Scan", "dynamodb:GetItem"], "Resource": "arn:aws:dynamodb:*:*:table/ComputeOptimizer"},
{"Effect": "Allow", "Action": ["ssm:SendCommand"], "Resource": "*"},
{"Effect": "Allow", "Action": ["ec2:CreateTags"], "Resource": "*"}
]
}
Step 4: Continuous feedback via CloudWatch and Cost Explorer
Automation without verification can create unintended downtime. The loop must close the feedback circle:
- Create a CloudWatch alarm for each instance that has been stopped. The alarm watches for a sudden rise in
CPUUtilizationafter the scheduled start time.bash aws cloudwatch put-metric-alarm \ --alarm-name "RestartAlert-$(date +%Y%m%d)" \ --metric-name CPUUtilization \ --namespace AWS/EC2 \ --statistic Average \ --period 300 \ --threshold 20 \ --comparison-operator GreaterThanThreshold \ --dimensions Name=InstanceId,Value=i-0abcd1234efgh5678 \ --evaluation-periods 2 \ --alarm-actions arn:aws:sns:us-east-1:123456789012:OpsAlerts - Query Cost Explorer weekly to measure the dollar impact of the schedule changes:
bash aws ce get-cost-and-usage \ --time-period Start=$(date -d '-7 days' +%Y-%m-%d),End=$(date +%Y-%m-%d) \ --granularity DAILY \ --filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon Elastic Compute Cloud - Compute"]}}' \ --metrics "UnblendedCost" - Log the results back into DynamoDB. The next Lambda run can compare the actual savings against the projected savings from Compute Optimizer and adjust thresholds if needed.
By keeping the loop observable, teams can trust the automation and iterate on the policy without manual audits.
Comparison of rightsizing approaches
| Approach | Frequency of review | Automation level | Typical savings | Operational overhead |
|---|---|---|---|---|
| Manual one‑off right‑size | Quarterly or ad‑hoc | Low (human‑driven) | 5‑15 % of EC2 spend | High (manual data collection, ticketing) |
| Scheduled stop/start (static) | Weekly or monthly | Medium (cron‑based) | 10‑20 % of idle compute | Medium (maintaining schedules) |
| Dynamic rightsizing loop (this article) | Daily (auto) | High (Lambda + Compute Optimizer) | 15‑30 % of variable compute | Low after initial setup |
The table shows why many teams settle for the first two methods: they require less engineering effort. The dynamic loop demands an upfront investment in Lambda code and IAM policies, but it pays off with continuous, data‑driven savings.
Frequently asked questions
How often should the loop run?
The loop is typically scheduled to run once per day during off‑peak hours. Daily cadence captures weekday pattern changes while keeping API costs low.
Will stopping instances break my CI/CD pipelines?
Only tag instances that are not part of the build fleet. Use a tag like pipeline=true to exclude them from the schedule logic.
Can this strategy be applied to RDS or Redshift?
Yes, the same principle works for managed databases. Compute Optimizer also provides recommendations for RDS, and the Instance Scheduler can be extended with RDS start/stop APIs.
Do I need a paid AWS support plan to use Compute Optimizer?
No. Compute Optimizer is available to all accounts at no additional charge. You only pay for the underlying resources you continue to run.
Key takeaways
- Static right‑sizing stops delivering incremental savings because workloads change.
- A dynamic rightsizing loop combines Compute Optimizer, Lambda, and the Instance Scheduler to automate continuous optimization.
- Define clear utilization thresholds, store recommendations in DynamoDB, and let Lambda tag instances for schedule enforcement.
- Close the loop with CloudWatch alarms and Cost Explorer reports to verify dollar impact.
- The approach yields higher, repeatable savings with minimal ongoing operational effort.
Ready to see idle resources on your AWS account right now? Try the free AWS waste finder at /tools/aws-waste-finder and then create a free account at /register to start automating the loop.
CloudBudgetMaster automates this dynamic rightsizing loop for you. Today it scans AWS accounts in read‑only mode, identifies idle and over‑provisioned resources, and reports the exact dollar impact. Support for GCP, Azure, and Snowflake is coming soon.
CloudBudgetMaster