Cost cleanup

A repeatable monthly sweep that finds idle and oversized cloud spend, proves it is safe to cut, and books the savings.

Updated 2026-11-11Raw markdown

Strategy sections
On this page

Use this page for a monthly cost review or a savings target. Cost work is ideal for an agent: the finding is mostly read-only, and the changes are small, reviewable MRs.

Look in this order (biggest, safest wins first)

  1. Idle resources: unattached EBS volumes, old snapshots, idle load balancers, unused Elastic IPs, stopped instances with large volumes, NAT gateways in empty VPCs.
  2. Non-production running 24x7: schedule dev and test to stop nights and weekends.
  3. Oversized compute and databases: CPU under 10 percent for 30 days.
  4. Storage classes: S3 lifecycle to infrequent access or Glacier; gp2 to gp3 volumes.
  5. Logs and metrics: CloudWatch log groups with "never expire", high-cardinality custom metrics, verbose debug logging in production.
  6. Commitments: Savings Plans or reserved capacity only after the above, for steady baseline usage.

Find it (read-only)

# Spend by service, last full month
aws --profile <payer-ro> ce get-cost-and-usage \
  --time-period Start=<YYYY-MM-01>,End=<YYYY-MM-01> --granularity MONTHLY \
  --metrics UnblendedCost --group-by Type=DIMENSION,Key=SERVICE \
  --query 'ResultsByTime[0].Groups[].{svc:Keys[0],usd:Metrics.UnblendedCost.Amount}' --output table

# Unattached volumes
aws --profile <readonly-profile> --region <region> ec2 describe-volumes \
  --filters Name=status,Values=available \
  --query 'Volumes[].{id:VolumeId,gb:Size,type:VolumeType,created:CreateTime}' --output table

# Log groups with no retention
aws --profile <readonly-profile> --region <region> logs describe-log-groups \
  --query 'logGroups[?!retentionInDays].{name:logGroupName,bytes:storedBytes}' --output table

# Rightsizing suggestions
aws --profile <readonly-profile> --region <region> compute-optimizer get-ec2-instance-recommendations \
  --query 'instanceRecommendations[].{id:instanceArn,finding:finding,now:currentInstanceType,rec:recommendationOptions[0].instanceType}'

Turn findings into savings

  • One ticket per finding type per account, with monthly cost and the evidence.
  • Estimate the monthly saving with the price list, not a guess.
  • Changes as MRs; deletes follow the staged approach in decommissioning.
  • Track: finding, saving estimate, date changed, saving confirmed next month.
Finding Account Est. monthly saving Status Confirmed
unattached volumes <account-alias> USD MR open

Prevent it coming back

  • Required tags (owner, env, service, cost-center) enforced with tag policies or IaC checks.
  • Budgets with alerts per account.
  • Default log retention in your IaC modules.
  • Monthly report of top 10 movers by service and account.
  • Cost per request for each service, so growth is not mistaken for a leak.

$$\text{cost per request} = \frac{\text{daily cost}}{\text{daily requests}}$$

For example, 400 USD a day at 20 million requests is 20 USD per million requests. If daily cost jumps to 600 USD with flat traffic, it is 30 USD per million.

Pitfalls

  • Deleting snapshots that are the only backup. Check backup policy first.
  • Rightsizing a database before peak season based on quiet-month data.
  • Counting savings that never showed up on the bill. Confirm next month.