AWS bills surprise teams every month. Here are the 8 concrete actions that cut cloud spend by 40-60% without touching your application architecture.
A fintech startup in Mumbai was spending $28,000 per month on AWS. After a two-week audit, they discovered three things: development EC2 instances running 24/7 with 4% average CPU, S3 traffic routing through NAT Gateway instead of a free VPC Endpoint, and no Savings Plans despite running the same workload for 14 months. Three changes brought the bill to $16,000. No architecture changes. No application refactoring. Just fixing configuration decisions made in a hurry that nobody ever revisited.
AWS cost problems are almost always a collection of small waste points that accumulate over months. This guide walks through the eight highest-impact actions in order of effort and return.
Buying Reserved Instances or Savings Plans on oversized instances locks in waste at a discount. Rightsize first, then commit.
Cost Explorer → Rightsizing Recommendations shows every EC2 instance running below 40% CPU utilisation over the past 14 days with a specific recommendation:
Current: m5.2xlarge — $276/month On-DemandActual CPU: 8% average, 22% peakRecommended: t3.large — $60/month On-DemandProjected saving: $216/month on this one instanceA team with 20 oversized instances finding an average $80/month saving per instance saves $1,600/month — before buying any commitments.
Memory metrics require the CloudWatch Unified Agent on EC2. Enable it on every production instance:
## Install Unified CloudWatch Agent on Amazon Linux 2023sudo yum install -y amazon-cloudwatch-agent ## Start with memory collection configsudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \ -a fetch-config \ -m ec2 \ -c ssm:/cloudwatch-config/prod \ -sAfter rightsizing, your Savings Plan commitment is sized correctly — you are not locking in discount on compute you do not need.
On-Demand EC2 is the most expensive pricing option on AWS. Any workload that has been running for more than 2 months with a predictable pattern should have Savings Plans coverage.
## Check your current Savings Plans coverageaws ce get-savings-plans-coverage \ --time-period Start=2024-01-01,End=2024-01-31 \ --granularity MONTHLY \ --region us-east-1The recommendation engine in Cost Explorer does the work for you:
Cost Explorer → Savings Plans → RecommendationsTerm: 1-year Payment: No UpfrontRecommended commitment: $8.50/hourEstimated annual savings: $42,000 (38% reduction)Start conservative — commit to 80% of your baseline usage, not your peak. Spiky traffic above the commitment is charged at On-Demand rates, which is fine. The commitment covers your stable floor.
Compute Savings Plans apply automatically. When you switch instance types or regions, the discount follows. No manual reassignment like Reserved Instances require.
NAT Gateway charges $0.045 per GB processed. Every private EC2 instance accessing S3 through NAT Gateway pays this fee for traffic that never needed to touch the internet.
## Check how much you are paying for NAT Gateway processingaws ce get-cost-and-usage \ --time-period Start=2024-01-01,End=2024-01-31 \ --granularity MONTHLY \ --filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon \ Virtual Private Cloud"]}}' \ --metrics BlendedCost \ --region us-east-1The fix is a VPC Gateway Endpoint — free to create, instant, and reduces NAT costs for S3 and DynamoDB to zero:
## Create free S3 Gateway Endpointaws ec2 create-vpc-endpoint \ --vpc-id vpc-xxxxxxxx \ --service-name com.amazonaws.ap-south-1.s3 \ --route-table-ids rtb-private-1a rtb-private-1b \ --region ap-south-1 ## Create free DynamoDB Gateway Endpointaws ec2 create-vpc-endpoint \ --vpc-id vpc-xxxxxxxx \ --service-name com.amazonaws.ap-south-1.dynamodb \ --route-table-ids rtb-private-1a rtb-private-1b \ --region ap-south-1For a team with 15 TB/month of S3 traffic from private EC2:
Without endpoint: 15,000 GB × $0.045 = $675/month in NAT chargesWith endpoint: $0/monthAnnual saving: $8,100Two CLI commands. Eight thousand dollars per year saved.
Without tags, Cost Explorer shows you what you are spending. With tags, it shows you who is spending it. The difference is the ability to hold teams accountable.
Mandatory tag policy:
| Tag key | Example value | Purpose |
|---|---|---|
| Environment | prod, staging, dev | Split production from non-production costs |
| Team | backend, frontend, data | Charge costs to owning team |
| Project | payments-v2, search-revamp | Track project-level spend |
Enforce tags with AWS Config:
## Create Config rule to flag untagged EC2 instancesaws configservice put-config-rule \ --config-rule '{ "ConfigRuleName": "required-tags-ec2", "Source": { "Owner": "AWS", "SourceIdentifier": "REQUIRED_TAGS" }, "InputParameters": "{\"tag1Key\":\"Environment\",\"tag2Key\":\"Team\"}" }' \ --region ap-south-1Once tags are in place, use Cost Explorer to generate per-team cost reports monthly. When the backend team sees they spent $3,400 in dev last month, they become motivated to schedule dev instances correctly.
Development and staging instances are typically used during business hours only. Running them 24/7 wastes 65% of their cost.
Deploy AWS Instance Scheduler from the AWS Solutions Library — one CloudFormation stack:
## Deploy Instance Scheduler solutionaws cloudformation create-stack \ --stack-name instance-scheduler \ --template-url \ https://s3.amazonaws.com/solutions-reference/\aws-instance-scheduler/latest/aws-instance-scheduler.template \ --parameters \ ParameterKey=DefaultTimezone,ParameterValue=Asia/Kolkata \ --capabilities CAPABILITY_IAM \ --region ap-south-1Then tag instances with the schedule:
## Tag dev instances to start at 8 AM, stop at 10 PM weekdays ISTaws ec2 create-tags \ --resources i-0abc123def456 \ --tags Key=Schedule,Value=india-business-hours \ --region ap-south-1Before scheduler: 20 dev EC2 instances × $0.10/hr × 720 hrs = $1,440/monthAfter scheduler (14 hrs × 22 working days): $0.10 × 308 hrs = $616/monthSaving: $824/month on dev instances aloneTeams frequently store everything in S3 Standard regardless of how often it is accessed. Logs written 90 days ago and never read again continue paying Standard prices.
Check what you are actually paying per storage class:
## Get S3 storage class breakdownaws s3api list-bucket-metrics-configurations \ --bucket prod-app-logs \ --region ap-south-1Set lifecycle rules to move data automatically:
aws s3api put-bucket-lifecycle-configuration \ --bucket prod-app-logs \ --lifecycle-configuration '{ "Rules": [{ "ID": "archive-old-logs", "Status": "Enabled", "Transitions": [ {"Days": 30, "StorageClass": "STANDARD_IA"}, {"Days": 90, "StorageClass": "GLACIER_FLEXIBLE_RETRIEVAL"} ], "Expiration": {"Days": 365}, "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7} }] }' \ --region ap-south-1S3 Standard: $0.023/GB/monthStandard-IA: $0.0125/GB/month (46% cheaper)Glacier Flexible:$0.004/GB/month (83% cheaper)A team storing 50 TB of logs that are rarely accessed after 30 days:
All in Standard: 50,000 GB × $0.023 = $1,150/monthWith lifecycle: mixed tiers based on age = ~$280/monthSaving: $870/monthSpot Instances offer up to 90% savings on EC2. AWS can reclaim them with a 2-minute warning, making them unsuitable for stateful production services — but ideal for batch processing, data transformation, and CI/CD build agents.
## Launch Spot Instance fleet for nightly ETLaws ec2 request-spot-fleet \ --spot-fleet-request-config '{ "IamFleetRole": "arn:aws:iam::123456789012:role/SpotFleetRole", "AllocationStrategy": "lowestPrice", "TargetCapacity": 10, "LaunchSpecifications": [{ "ImageId": "ami-0f5ee92e2d63afc18", "InstanceType": "m5.xlarge", "SubnetId": "subnet-private-1a" },{ "ImageId": "ami-0f5ee92e2d63afc18", "InstanceType": "m4.xlarge", "SubnetId": "subnet-private-1a" }] }' \ --region ap-south-1Specify multiple instance types in your Spot Fleet — the more options you give AWS, the higher the probability of getting Spot capacity at any given time. For batch workloads that can checkpoint and resume, Spot Interruption Handling with a 2-minute warning handler is the standard pattern.
Cost anomaly detection uses ML to learn your normal spending patterns and alert you within hours when something unusual happens.
## Create a cost anomaly monitor for all AWS servicesaws ce create-anomaly-monitor \ --anomaly-monitor '{ "MonitorName": "all-services-monitor", "MonitorType": "DIMENSIONAL", "MonitorDimension": "SERVICE" }' \ --region us-east-1 ## Create alert subscriptionaws ce create-anomaly-subscription \ --anomaly-subscription '{ "MonitorArnList": ["arn:aws:ce::123456789012:anomalymonitor/MONITOR-ID"], "Subscribers": [{ "Address": "devops-alerts@company.in", "Type": "EMAIL" }], "Threshold": 20.0, "Frequency": "DAILY", "SubscriptionName": "daily-anomaly-alert" }' \ --region us-east-1With a threshold of $20, you are alerted any time a service spends $20 more than expected in a day. A forgotten GPU instance, a misconfigured S3 replication rule, a runaway Lambda function — caught within 24 hours instead of at month-end invoice.
| Approach | Effort | Monthly saving | Risk |
|---|---|---|---|
| Rightsize EC2 | Medium | $500-5,000 | Application testing needed |
| Savings Plans | Low | 30-72% of compute | 1-3 year commitment |
| NAT → VPC Endpoint | Very low | $200-2,000 | None |
| Instance Scheduler | Low | $500-2,000 | Dev workflow change |
| S3 Lifecycle rules | Low | $100-1,500 | Retrieval latency for archived data |
| Spot for batch | Medium | 70-90% on batch | Interruption handling needed |
| Cost Anomaly Detection | Very low | Prevents surprises | None |
Execute in this order — each step builds on the previous:
REMEMBER THIS**References and Further Reading** * [AWS Cost Explorer](https://aws.amazon.com/aws-cost-management/aws-cost-explorer/) — pricing analysis and rightsizing recommendations * [AWS Compute Optimizer](https://aws.amazon.com/compute-optimizer/) — ML-based resource recommendations * [Instance Scheduler on AWS](https://aws.amazon.com/solutions/implementations/instance-scheduler-on-aws/) — automated start/stop solution
Use Cost Explorer's Rightsizing Recommendations — it analyses the last 14 days of CloudWatch CPU and memory metrics across your entire fleet and flags instances running below 40% utilisation. Alternatively, enable AWS Compute Optimizer which uses ML to analyse 14 days of utilisation and recommends specific instance type changes with projected savings.
Compute Savings Plans offer up to 66% discount and apply automatically to any EC2 instance type, region, or OS, plus Lambda and Fargate. EC2 Instance Savings Plans offer up to 72% but lock you to a specific instance family and region. Reserved Instances offer up to 72% but require choosing specific instance type, region, and OS upfront. For most teams, Compute Savings Plans are the better choice — the flexibility to change instance types without losing the discount outweighs the slightly lower maximum discount.
Discussion0