Advanced Cloud Cost Optimization Strategy Teams Overlook
Why a hidden‑cost strategy matters
Most engineering and platform teams focus on obvious levers – instance right‑sizing, reserved capacity, and storage tiering. Those actions can shave 10‑20 % off a bill, but the majority of wasted spend lives in low‑visibility resources that never trigger an alert. The strategy outlined here surfaces those hidden costs, quantifies their dollar impact, and provides concrete steps to eliminate them.
1. Audit cross‑region and inter‑AZ data transfer
What the cost looks like
AWS charges for data that moves between regions and between Availability Zones (AZs) in the same region. Even a modest amount of traffic between two AZs can add up to hundreds of dollars per month because the price is per GB transferred.
How to discover the traffic
aws ce get-cost-and-usage \
--time-period Start=$(date -d "-30 days" +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY \
--metrics "UnblendedCost" \
--group-by Type=DIMENSION,Key=USAGE_TYPE
Look for usage types that contain DataTransfer- and InterRegion. Export the result to CSV and sum the UnblendedCost column.
Mitigation steps
- Consolidate services into a single AZ when latency requirements allow. Move databases, caches, and batch workers to the same AZ as the compute that accesses them.
- Enable VPC endpoints for S3 and DynamoDB to keep traffic on the AWS backbone instead of the public internet.
- Use Amazon CloudFront or an internal ALB for cross‑region reads instead of direct API calls.
- Set up a CloudWatch metric filter that watches
DataTransfer-InterRegionand triggers an SNS alarm when daily spend exceeds a threshold.
2. Consolidate NAT gateways and transit gateways
Hidden expense of idle gateways
A NAT gateway costs $0.045 per hour plus $0.045 per GB processed. Teams often spin up a NAT gateway per VPC and forget to delete it after the workload ends. Even an idle gateway that processes only a few megabytes per day can cost $30‑$40 per month.
Finding idle gateways
aws ec2 describe-nat-gateways \
--query "NatGateways[?State=='available'].[NatGatewayId, VpcId, CreateTime]" \
--output table
Then check traffic:
aws cloudwatch get-metric-statistics \
--namespace AWS/NATGateway \
--metric-name BytesOutToDestination \
--statistics Sum \
--period 86400 \
--start-time $(date -d "-7 days" +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--dimensions Name=NatGatewayId,Value=<gateway-id>
If the sum is zero for a week, the gateway is idle.
Consolidation tactics
- Share a single NAT gateway across multiple private subnets using VPC peering or a transit gateway.
- Replace NAT gateways with NAT instances for low‑throughput workloads; a t3.nano instance costs a fraction of a gateway.
- Automate deletion with a Lambda function that runs weekly, checks the metric above, and calls
aws ec2 delete-nat-gatewayfor idle IDs.
3. Leverage instance hibernation and stop‑start cycles
When to hibernate vs stop
Long‑running development or test environments often sit idle for days. Hibernating an EC2 instance preserves RAM state to EBS and reduces start‑up time, but it still incurs EBS storage cost. Stopping the instance eliminates compute charges entirely.
CLI workflow
# List stopped or hibernated instances older than 7 days
aws ec2 describe-instances \
--filters Name=instance-state-name,Values=stopped,hibernated \
--query "Reservations[].Instances[?LaunchTime<='$(date -d '-7 days' +%Y-%m-%d)'].InstanceId" \
--output text
# Stop a running instance that has low CPU utilization (example using CloudWatch metric)
INSTANCE_ID=i-0abcd1234efgh5678
aws ec2 stop-instances --instance-ids $INSTANCE_ID
Automation
Create an EventBridge rule that triggers a Lambda function every night. The function:
1. Queries CloudWatch CPUUtilization for the past 24 h.
2. If average < 5 %, calls stop-instances.
3. Sends a Slack notification with the instance ID and expected monthly savings.
4. Apply lifecycle policies to snapshots and AMIs
Snapshot creep explained
Every EBS snapshot is stored in S3 and billed per GB‑month. Teams that create snapshots after each backup or test run quickly accumulate terabytes of stale data.
Detecting old snapshots
aws ec2 describe-snapshots \
--owner-ids self \
--query "Snapshots[?StartTime<='$(date -d '-90 days' +%Y-%m-%d)'].{ID:SnapshotId,Size:VolumeSize,Age:StartTime}" \
--output table
Lifecycle policy creation
aws dlm create-lifecycle-policy \
--execution-role-arn arn:aws:iam::123456789012:role/AWSDataLifecycleManagerDefaultRole \
--description "Delete EBS snapshots older than 90 days" \
--policy-details '{
"ResourceTypes": ["VOLUME"],
"TargetTags": [{"Key":"Backup","Value":"true"}],
"Schedules": [{
"Name": "DeleteOldSnapshots",
"CreateRule": {"Interval": 1, "IntervalUnit": "DAYS"},
"RetainRule": {"Count": 0},
"CopyTags": false,
"DeleteRule": {"Enabled": true, "Age": 90}
}]
}'
The policy automatically deletes snapshots older than 90 days for any volume tagged Backup=true.
5. Tag‑driven cost allocation and automated alerts
Why tags matter for hidden spend
Resources that lack cost allocation tags appear in the “Unallocated” bucket of the Cost Explorer report. Without visibility, teams cannot assign accountability, and waste persists.
Enforcing tag policies
- Create an IAM policy that denies
Create*actions unless the request includes required tags.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": ["ec2:RunInstances", "rds:CreateDBInstance"],
"Resource": "*",
"Condition": {"StringNotEquals": {"aws:RequestTag/Owner": "*"}}
}]
}
- Deploy AWS Config rules such as
required-tagsto continuously evaluate compliance. - Set up a CloudWatch alarm that triggers when the “Unallocated” cost exceeds 5 % of total spend.
aws cloudwatch put-metric-alarm \
--alarm-name "UnallocatedCost" \
--metric-name "UnallocatedCost" \
--namespace "AWS/Billing" \
--statistic Sum \
--period 86400 \
--threshold $(aws ce get-cost-and-usage --time-period Start=$(date -d "-30 days" +%Y-%m-%d),End=$(date +%Y-%m-%d) --metrics "UnblendedCost" --group-by Type=DIMENSION,Key=SERVICE --query "ResultsByTime[0].Total.UnblendedCost.Amount" --output text) \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:BillingAlerts
When the alarm fires, the responsible team receives a ticket that includes the list of untagged resources, generated by a Lambda that calls aws resourcegroupstaggingapi get-resources.
6. Comparison of hidden‑cost categories
| Category | Typical monthly charge (per unit) | Primary mitigation | Example CLI to discover |
|---|---|---|---|
| Inter‑AZ data transfer | $0.01 per GB | Co‑locate resources | aws ce get-cost-and-usage … --group-by Key=USAGE_TYPE |
| NAT gateway (idle) | $30 per gateway + $0.045/GB | Share or delete | aws ec2 describe-nat-gateways |
| Unused Elastic IPs | $3.65 per IP per month | Release IPs | aws ec2 describe-addresses --filters Name=domain,Values=vpc |
| Stale EBS snapshots | $0.05 per GB‑month | Lifecycle policy | aws ec2 describe-snapshots |
| Idle RDS instances | On‑demand hourly rate | Stop/hibernate | aws rds describe-db-instances |
Frequently asked questions
How often should I run an idle‑resource scan?
Running the scan weekly catches most waste without overwhelming the team. For high‑velocity environments, a daily Lambda that checks CloudWatch metrics can provide near‑real‑time visibility.
Can I automate deletion of idle resources without risking production impact?
Yes. Combine metric thresholds with a tagging convention such as AutoDelete=true. The Lambda only deletes resources that meet both the idle metric and the tag, reducing the chance of accidental termination.
Do lifecycle policies delete snapshots that are still needed for compliance?
Lifecycle policies operate on tags and age. By tagging snapshots that must be retained (e.g., Compliance=true), the policy will skip them, ensuring regulatory data remains untouched.
What is the best way to get visibility into cross‑region traffic?
Enable VPC Flow Logs and send them to CloudWatch Logs. Then use a CloudWatch Logs Insights query to aggregate bytes by srcaddr and dstaddr across regions. The query returns the top talkers and the associated cost.
Key takeaways
- Hidden costs such as inter‑AZ data transfer, idle NAT gateways, and stale snapshots can dwarf the savings from traditional right‑sizing.
- Use the AWS Cost Explorer API and CloudWatch metrics to surface these low‑visibility spend items.
- Automate discovery and remediation with Lambda, EventBridge, and lifecycle policies.
- Enforce tag‑driven cost allocation to keep unallocated spend under control.
- Regularly audit and consolidate shared infrastructure like NAT and transit gateways.
How CloudBudgetMaster helps you execute this strategy
CloudBudgetMaster scans your AWS account in read‑only mode today, identifies idle and wasted resources, and reports the dollar impact of each item. GCP, Azure and Snowflake support is coming soon. Use our free AWS waste finder to get started, then create a free account to see the full automated report.
CloudBudgetMaster