CloudFinOps logo
CloudFinOps
AWS

How to Reduce Your AWS Bill by 30% Without Downtime (Step-by-Step)

Nishant Jain

Nishant Jain

Co-Founder & CTO · · 15 min read

Most engineering teams discover the same uncomfortable truth around month three of running production workloads on AWS: the bill grows faster than the infrastructure actually needs to. A service that costs $12,000/month at launch quietly balloons to $18,000 without a single new customer. The instances are bigger than they need to be. The EBS volumes from that failed migration are still attached. The NAT Gateway is routing traffic that could flow through a VPC endpoint for a fraction of the cost.

The good news is that for most organizations spending between $10,000 and $100,000 per month on AWS, a 30% reduction is not only achievable — it is repeatable using a structured approach that never touches production availability. This guide walks through exactly how to do it, step by step, using a combination of native AWS tooling, CLI commands, and systematic process changes.

Why AWS Bills Spiral Out of Control

Before jumping into fixes, it helps to understand the mechanics of how AWS costs compound. Unlike a traditional hosting bill that stays flat until you explicitly upgrade, AWS charges accrue from dozens of independent meters running simultaneously. Each one is small enough to ignore individually but devastating in aggregate.

The Accumulation Problem

AWS bills grow through three primary mechanisms:

  1. Resource sprawl — Engineers spin up instances, volumes, and load balancers during development or incident response, then forget to tear them down. A single forgotten m5.xlarge instance costs $0.192/hour, which translates to $140/month sitting idle.

  2. Over-provisioning bias — When choosing instance sizes, teams almost always round up. Nobody wants to be the person whose undersized database caused an outage at 2 AM. So a workload that needs 2 vCPUs and 4 GB RAM ends up running on an m5.2xlarge with 8 vCPUs and 32 GB RAM — 4x more than necessary.

  3. Architecture debt — Early design decisions (cross-AZ traffic routing, unoptimized S3 access patterns, oversized NAT Gateways) create recurring charges that become invisible because nobody remembers the original tradeoff.

The Numbers That Matter

According to AWS's own reporting and third-party analyses in 2026, the average organization wastes 27-35% of its cloud spend. The breakdown typically looks like this:

Waste CategoryTypical % of Total BillCommon Culprits
Idle/unused resources8-12%Stopped EC2 with attached EBS, unused EIPs, orphan snapshots
Over-provisioned compute10-15%EC2, RDS, ElastiCache running at <20% CPU
Missing commitment discounts8-12%On-demand pricing for stable baseline workloads
Data transfer inefficiency3-5%Cross-AZ traffic, unoptimized NAT Gateway usage
Storage bloat2-4%Old EBS snapshots, S3 without lifecycle policies

The following six steps address each of these categories systematically.

Step 1: Audit and Eliminate Idle Resources

The fastest path to savings is removing resources that provide zero value. These are resources you are paying for right now that serve no production purpose whatsoever.

Find Idle EC2 Instances

An EC2 instance is effectively idle if its average CPU utilization stays below 5% over a 14-day period and it handles negligible network traffic. Use CloudWatch to identify these:

aws cloudwatch get-metric-statistics \ --namespace AWS/EC2 \ --metric-name CPUUtilization \ --dimensions Name=InstanceId,Value=i-0abc123def456 \ --start-time $(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%S) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ --period 86400 \ --statistics Average

For a fleet-wide scan, AWS Cost Explorer's rightsizing recommendations already flag instances with sustained low utilization. Navigate to Cost Explorer > Recommendations > Rightsizing in the console.

Identify Unattached EBS Volumes

Unattached EBS volumes are pure waste. They cost between $0.08/GB/month (gp3) and $0.125/GB/month (io2) while providing zero functionality:

aws ec2 describe-volumes \ --filters Name=status,Values=available \ --query 'Volumes[*].{ID:VolumeId,Size:Size,Type:VolumeType,Created:CreateTime}' \ --output table

Key Insight: In our experience auditing AWS accounts, unattached EBS volumes alone account for $200-$800/month in waste for teams spending $30K+ monthly. The volumes accumulate because deleting an EC2 instance does not automatically delete its root volume unless you explicitly configured that at launch.

Release Unused Elastic IPs

Each unattached Elastic IP costs $3.60/month (as of 2026 pricing). That sounds trivial until you discover 15 of them sitting idle in a staging account:

aws ec2 describe-addresses \ --query 'Addresses[?AssociationId==null].{IP:PublicIp,AllocationId:AllocationId}' \ --output table

Audit NAT Gateway Usage

NAT Gateways charge $0.045/hour ($32.40/month) plus $0.045/GB of data processed. If your private subnets are routing traffic to AWS services (S3, DynamoDB, SQS) through a NAT Gateway, you are paying egress fees unnecessarily. VPC Gateway Endpoints for S3 and DynamoDB are free. VPC Interface Endpoints cost $0.01/hour but eliminate NAT data processing charges entirely.

Check your NAT Gateway data processing in Cost Explorer by filtering on the NatGateway usage type. If you see more than 100 GB/month flowing through it, investigate whether Interface Endpoints would be cheaper.

Action Checklist

  • Terminate EC2 instances with <5% CPU over 14 days (after confirming they are not bastion hosts or cron workers)
  • Delete all unattached EBS volumes (snapshot first if uncertain)
  • Release all unassociated Elastic IPs
  • Replace NAT Gateway traffic to AWS services with VPC Endpoints
  • Remove unused Application Load Balancers ($16.20/month base + LCU charges)

Expected savings from this step alone: 8-12% of total bill.

See how much you're wasting

Get a free 7-day cloud audit. No credit card, no agents, read-only access.

Start Free Audit

Step 2: Rightsize Over-Provisioned Instances

Rightsizing is the process of matching instance capacity to actual workload requirements. It is the single highest-impact optimization for most teams because over-provisioning is so pervasive.

How to Identify Rightsizing Opportunities

AWS Compute Optimizer analyzes 14 days of CloudWatch metrics and provides specific instance type recommendations. Enable it account-wide:

aws compute-optimizer update-enrollment-status --status Active

After 12-24 hours, pull recommendations:

aws compute-optimizer get-ec2-instance-recommendations \ --query 'instanceRecommendations[*].{Instance:instanceArn,Current:currentInstanceType,Recommended:recommendationOptions[0].instanceType,Savings:recommendationOptions[0].estimatedMonthlySavings.value}' \ --output table

Rightsizing Decision Framework

Not every recommendation should be accepted blindly. Use this framework:

Current UtilizationRecommendationRisk LevelAction
CPU <10%, Memory <20%Drop 2 sizes (e.g., xlarge to small)LowExecute immediately
CPU 10-30%, Memory 20-50%Drop 1 size (e.g., xlarge to large)LowExecute with monitoring
CPU 30-50%, Memory 50-70%Stay or move to newer generationMediumTest in staging first
CPU >50%, Memory >70%Already well-sized or upgrade neededN/ANo action

Graviton Migration for Free Performance

AWS Graviton3 instances (denoted by the g suffix, e.g., m7g.xlarge) offer 25% better price-performance versus their x86 equivalents. If your workload runs on Linux and does not depend on x86-specific compiled binaries, switching from m5.xlarge ($0.192/hr) to m7g.xlarge ($0.153/hr) saves 20% with equivalent or better performance.

# Check if your AMI supports arm64 aws ec2 describe-images --image-ids ami-0abc123 \ --query 'Images[0].Architecture'

RDS Rightsizing

Database instances are the most commonly over-provisioned resource because teams fear database performance issues more than anything else. Check your RDS CPU and memory utilization:

aws cloudwatch get-metric-statistics \ --namespace AWS/RDS \ --metric-name CPUUtilization \ --dimensions Name=DBInstanceIdentifier,Value=production-db \ --start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ --period 3600 \ --statistics Average Maximum

If your db.r5.2xlarge ($0.96/hr, ~$691/month) averages 15% CPU, a db.r5.large ($0.24/hr, ~$173/month) handles the same load at 60% utilization — saving $518/month from a single instance change.

Key Insight: Rightsize databases during your next maintenance window. RDS supports instance class modification with minimal downtime (typically under 30 seconds during the multi-AZ failover). This is not a risky change — it is a routine operational action.

Execute Rightsizing Without Downtime

For EC2 instances backed by EBS (which is nearly all of them), rightsizing is a stop-start operation:

# Stop the instance aws ec2 stop-instances --instance-ids i-0abc123def456 # Modify instance type aws ec2 modify-instance-attribute \ --instance-id i-0abc123def456 \ --instance-type '{"Value": "m5.large"}' # Start the instance aws ec2 start-instances --instance-ids i-0abc123def456

For zero-downtime rightsizing, place instances behind an Auto Scaling Group or load balancer. Launch new correctly-sized instances, wait for health checks to pass, then terminate the old ones. This is the standard blue-green deployment approach and adds zero risk.

Expected savings from this step: 10-20% of total bill.

Step 3: Leverage Savings Plans and Reserved Instances

Once you have eliminated waste and rightsized your fleet, the remaining baseline represents your true steady-state cost. This is the portion you should commit to for volume discounts.

Savings Plans vs. Reserved Instances in 2026

FeatureCompute Savings PlansEC2 Instance Savings PlansReserved Instances
Discount (1-year, no upfront)20-30%30-40%35-42%
Discount (3-year, all upfront)40-55%55-65%60-72%
FlexibilityAny instance family, region, OSSpecific instance family in a regionSpecific instance type, AZ
Applies toEC2, Fargate, LambdaEC2 onlyEC2 only
Best forDynamic workloadsStable workloads, single familyKnown, unchanging workloads

Calculating Your Commitment Level

The safest approach is to commit to 70-80% of your minimum sustained usage and leave 20-30% on demand for flexibility. Here is how to determine that floor:

  1. Open Cost Explorer > Savings Plans > Recommendations
  2. Set the lookback period to 60 days
  3. Choose "Compute Savings Plans" for maximum flexibility
  4. Set the term to 1 year, no upfront payment (lowest risk)
  5. AWS shows your recommended hourly commitment and estimated savings

For a team spending $50,000/month on EC2, a typical recommendation might be a $25/hour Compute Savings Plan commitment, saving approximately $8,000-$12,000/month (16-24% of total).

The Commitment Ladder Strategy

Do not commit 100% of your baseline on day one. Instead, ladder your commitments:

  • Month 1: Commit to 50% of your minimum hourly spend (safest possible commitment)
  • Month 3: Add another 20% after validating your usage patterns held steady
  • Month 6: Evaluate whether to add the final 10-15% or shift to 3-year terms on the most stable portions

This approach prevents the common failure mode of over-committing before a migration or architecture change, which turns "savings" into sunk costs.

Reserved Instances for Databases

RDS Reserved Instances deserve special attention because databases are the most predictable workloads in most environments. A db.r6g.xlarge on-demand costs $0.48/hour ($346/month). A 1-year no-upfront RI drops that to $0.30/hour ($216/month) — a 37% savings. With 3-year all-upfront pricing, it drops to $0.17/hour ($122/month) — a 65% savings.

# Check current RI coverage aws rds describe-reserved-db-instances \ --query 'ReservedDBInstances[*].{Class:DBInstanceClass,Count:DBInstanceCount,State:State,End:StartTime}' \ --output table

Expected savings from this step: 15-25% of remaining on-demand spend (after steps 1-2).

Step 4: Optimize Data Transfer Costs

Data transfer is the most opaque line item on an AWS bill. It does not show up as a single charge — it is distributed across dozens of service-specific line items that are easy to miss individually.

Understanding AWS Data Transfer Pricing

Transfer TypeCost
Data in from internetFree
Data out to internet (first 10 TB/month)$0.09/GB
Cross-AZ transfer (same region)$0.01/GB each direction ($0.02 roundtrip)
Cross-region transfer$0.02/GB
S3 to CloudFrontFree
VPC Endpoint to S3/DynamoDB (Gateway)Free
NAT Gateway data processing$0.045/GB

Reduce Cross-AZ Traffic

If your microservices communicate across Availability Zones, every API call incurs a $0.01/GB charge in each direction. For a service handling 50 GB/day of inter-service traffic across AZs, that is $30/day or $900/month.

Mitigation strategies:

  1. Use AZ-affinity routing — Configure your service mesh or load balancer to prefer same-AZ targets. ALB supports this natively with cross-zone load balancing disabled.

  2. Deploy stateless services in a single AZ — For services where brief unavailability is acceptable during an AZ failure, running in one AZ eliminates cross-AZ transfer entirely.

  3. Compress inter-service payloads — gzip compression on gRPC/HTTP calls between services reduces transfer volume by 60-80%.

Implement S3 Intelligent-Tiering

If you store more than 128 KB objects in S3 that have unpredictable access patterns, S3 Intelligent-Tiering automatically moves objects between access tiers with zero retrieval fees:

aws s3api put-bucket-lifecycle-configuration \ --bucket my-data-bucket \ --lifecycle-configuration '{ "Rules": [{ "ID": "IntelligentTiering", "Status": "Enabled", "Filter": {"Prefix": ""}, "Transitions": [{ "Days": 0, "StorageClass": "INTELLIGENT_TIERING" }] }] }'

For a team storing 10 TB in S3 Standard ($0.023/GB/month = $230/month), Intelligent-Tiering can reduce costs to $0.004/GB/month for infrequently accessed data — saving up to 80% on cold objects with zero operational overhead.

Replace NAT Gateway with VPC Endpoints

As mentioned in Step 1, this is one of the highest-ROI optimizations. Here is the full implementation:

# Create a Gateway Endpoint for S3 (free) aws ec2 create-vpc-endpoint \ --vpc-id vpc-0abc123 \ --service-name com.amazonaws.us-east-1.s3 \ --route-table-ids rtb-0abc123 # Create a Gateway Endpoint for DynamoDB (free) aws ec2 create-vpc-endpoint \ --vpc-id vpc-0abc123 \ --service-name com.amazonaws.us-east-1.dynamodb \ --route-table-ids rtb-0abc123

For Interface Endpoints (SQS, SNS, Secrets Manager, CloudWatch Logs, etc.):

aws ec2 create-vpc-endpoint \ --vpc-id vpc-0abc123 \ --vpc-endpoint-type Interface \ --service-name com.amazonaws.us-east-1.sqs \ --subnet-ids subnet-0abc123 \ --security-group-ids sg-0abc123

Interface Endpoints cost $0.01/hour ($7.20/month) plus $0.01/GB processed. If your NAT Gateway processes more than 160 GB/month for a given AWS service, the Interface Endpoint is cheaper.

Expected savings from this step: 3-8% of total bill, depending on data transfer volume.

Step 5: Automate with Scheduling and Scaling

Human-driven optimization is a one-time event. Automation makes it permanent.

Schedule Non-Production Resources

Development, staging, and QA environments rarely need to run 24/7. Running them only during business hours (10 hours/day, 5 days/week) reduces their cost by 70%.

Using AWS Instance Scheduler (a CloudFormation-based solution from AWS):

# Tag instances for scheduling aws ec2 create-tags \ --resources i-0abc123def456 \ --tags Key=Schedule,Value=office-hours

Or use a simple EventBridge + Lambda approach:

# Create an EventBridge rule to stop instances at 7 PM aws events put-rule \ --name "StopDevInstances" \ --schedule-expression "cron(0 19 ? * MON-FRI *)" \ --state ENABLED

Key Insight: Scheduling dev/staging environments is the single easiest automation to implement and typically saves $2,000-$5,000/month for teams with 10-20 non-production instances. If you do nothing else from this step, do this.

Implement Proper Auto Scaling

Many teams deploy Auto Scaling Groups but configure them with min=desired=max, which defeats the entire purpose. Proper scaling configuration:

aws autoscaling update-auto-scaling-group \ --auto-scaling-group-name production-api \ --min-size 2 \ --max-size 10 \ --desired-capacity 3 # Add target tracking policy (maintains 60% CPU) aws autoscaling put-scaling-policy \ --auto-scaling-group-name production-api \ --policy-name cpu-target-tracking \ --policy-type TargetTrackingScaling \ --target-tracking-configuration '{ "PredefinedMetricSpecification": { "PredefinedMetricType": "ASGAverageCPUUtilization" }, "TargetValue": 60.0, "ScaleInCooldown": 300, "ScaleOutCooldown": 60 }'

Use Spot Instances for Fault-Tolerant Workloads

Spot Instances cost 60-90% less than on-demand. They are interrupted with 2 minutes notice, which makes them unsuitable for stateful databases but excellent for:

  • Batch processing jobs
  • CI/CD build agents
  • Data pipeline workers
  • Test environments
  • Stateless API servers behind a load balancer (with on-demand fallback)

A mixed instances policy in your ASG:

aws autoscaling create-auto-scaling-group \ --auto-scaling-group-name batch-workers \ --mixed-instances-policy '{ "LaunchTemplate": { "LaunchTemplateSpecification": { "LaunchTemplateId": "lt-0abc123", "Version": "$Latest" }, "Overrides": [ {"InstanceType": "m5.xlarge"}, {"InstanceType": "m5a.xlarge"}, {"InstanceType": "m5d.xlarge"}, {"InstanceType": "m6i.xlarge"} ] }, "InstancesDistribution": { "OnDemandBaseCapacity": 1, "OnDemandPercentageAboveBaseCapacity": 20, "SpotAllocationStrategy": "capacity-optimized" } }' \ --min-size 1 --max-size 20

This ensures 1 on-demand instance as a baseline and fills 80% of additional capacity with Spot, diversifying across 4 instance types to reduce interruption probability.

Lambda Cost Optimization

If you run Lambda functions, two quick wins:

  1. Right-size memory allocation. Lambda charges per GB-second. A function allocated 1024 MB that only uses 200 MB is wasting 80% of its compute cost. Use AWS Lambda Power Tuning to find the optimal memory setting.

  2. Use Graviton (arm64) for Lambda. Graviton-based Lambda functions cost 20% less and execute up to 34% faster for many workloads:

aws lambda update-function-configuration \ --function-name my-function \ --architectures arm64

Expected savings from this step: 5-15% of total bill.

Step 6: Set Up Ongoing Monitoring and Governance

Optimization without monitoring is a one-time event. Within 3-6 months, costs drift back to previous levels as new resources accumulate and configurations shift.

AWS Budgets with Action Alerts

Set up budget alerts that trigger before you overspend:

aws budgets create-budget \ --account-id 123456789012 \ --budget '{ "BudgetName": "Monthly-Total", "BudgetLimit": {"Amount": "50000", "Unit": "USD"}, "TimeUnit": "MONTHLY", "BudgetType": "COST" }' \ --notifications-with-subscribers '[ { "Notification": { "NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": 80 }, "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "platform-team@company.com"}] } ]'

Weekly Cost Anomaly Detection

AWS Cost Anomaly Detection uses machine learning to identify unusual spending patterns. Enable it once and it monitors continuously:

aws ce create-anomaly-monitor \ --anomaly-monitor '{ "MonitorName": "ServiceMonitor", "MonitorType": "DIMENSIONAL", "MonitorDimension": "SERVICE" }' aws ce create-anomaly-subscription \ --anomaly-subscription '{ "SubscriptionName": "WeeklyAlert", "MonitorArnList": ["arn:aws:ce::123456789012:anomalymonitor/abc123"], "Subscribers": [{"Address": "platform-team@company.com", "Type": "EMAIL"}], "Threshold": 100, "Frequency": "WEEKLY" }'

Tagging Strategy for Cost Allocation

Without proper tags, you cannot attribute costs to teams, projects, or environments. Enforce a mandatory tagging policy:

# Create a tag policy in AWS Organizations aws organizations create-policy \ --name "CostAllocationTags" \ --type TAG_POLICY \ --content '{ "tags": { "Environment": {"tag_value": {"@@assign": ["production", "staging", "development"]}}, "Team": {"tag_value": {"@@assign": ["platform", "backend", "data", "ml"]}}, "CostCenter": {"tag_key": {"@@assign": "CostCenter"}} } }'

Activate these as cost allocation tags in the Billing console so they appear in Cost Explorer breakdowns.

Automated Governance with Tools

For teams spending more than $30K/month, manual weekly reviews are not sufficient. Consider implementing automated scanning tools that continuously identify waste:

  • AWS Trusted Advisor (included with Business/Enterprise support) scans for idle resources and provides recommendations in the console.
  • AWS Compute Optimizer (free) provides rightsizing recommendations based on CloudWatch metrics.
  • CloudFinOps automates zombie resource detection, rightsizing analysis, and multi-cloud cost comparisons with AI-driven recommendations that surface savings you might miss in manual reviews.
  • Custom scripts using boto3 can scan for specific patterns (untagged resources, old snapshots, oversized volumes) and alert via SNS or Slack.

The key is making cost visibility a passive, always-on capability rather than an active effort that requires someone to remember to check.

Monthly Cost Review Cadence

Establish a 30-minute monthly review with this agenda:

  1. Total spend vs. budget — Are we on track?
  2. Top 5 cost increases — What grew and why?
  3. Commitment utilization — Are our Savings Plans/RIs being fully used?
  4. New resource audit — What was created this month and is it tagged?
  5. Next optimization action — One specific action item for the coming month

Key Insight: The teams that sustain cost reductions over time are not the ones with the best tooling — they are the ones that made cost review a recurring calendar event that someone owns. Assign a "FinOps champion" on your platform team and give them 2 hours/month dedicated to this.

Expected savings from this step: Prevents 5-10% monthly cost drift that would otherwise accumulate.

Putting It All Together: The 30% Target

Here is how a team spending $50,000/month on AWS might reach 30% savings:

StepActionMonthly Savings% Reduction
1Eliminate idle resources$4,5009%
2Rightsize instances$5,00010%
3Savings Plans (on reduced baseline)$5,40010.8%
4Data transfer optimization$1,5003%
5Scheduling + Spot$2,1004.2%
Total$18,50037%

The math works because each step builds on the previous one. You rightsize first so your commitment purchases are based on actual needs, not inflated baselines. You automate last so the savings are sustained.

Timeline for implementation:

  • Week 1: Complete Steps 1-2 (audit + rightsize) — immediate savings
  • Week 2-3: Implement Step 4 (data transfer) + Step 5 (scheduling)
  • Week 4: Purchase Savings Plans (Step 3) based on new baseline
  • Ongoing: Step 6 monitoring prevents regression

This is not a one-time project. It is a permanent operational capability. But the initial effort is concentrated in 2-4 weeks, and the ongoing maintenance is 2-3 hours per month once the automation and monitoring are in place.

FAQ

Can I rightsize an EC2 instance without any downtime?

For standalone instances, a stop-start is required to change the instance type, which means a brief interruption (typically 2-5 minutes). For zero-downtime rightsizing, place your instances behind an Auto Scaling Group or load balancer. Launch new correctly-sized instances, verify health checks pass, then terminate the old ones. RDS Multi-AZ instances can be resized with under 30 seconds of downtime during the failover.

How do I know if I should buy Savings Plans or Reserved Instances?

If your workloads might change instance families or regions within the commitment period, choose Compute Savings Plans for maximum flexibility. If you run a stable database or application server that will not change instance type for 1-3 years, Reserved Instances offer deeper discounts (up to 72% vs. 55% for Savings Plans on a 3-year all-upfront term). Most teams should start with Compute Savings Plans and only use Reserved Instances for databases and other highly predictable workloads.

What if my usage is too variable to commit to Savings Plans?

Commit only to your minimum sustained baseline — the lowest hourly spend you see across a 60-day period. Even committing to 50% of your average usage still yields significant savings with near-zero risk. For the variable portion above your baseline, use Spot Instances for fault-tolerant workloads and on-demand for everything else. You can always add more commitment later as your patterns stabilize.

How often should I review and re-optimize?

Perform a full optimization review quarterly. Run automated scans for idle resources weekly (or continuously with tools like CloudFinOps or AWS Trusted Advisor). Review Savings Plan utilization monthly to ensure you are not over-committed. Major architecture changes (migrating to containers, adopting serverless, or changing regions) should trigger an immediate re-evaluation of all commitments.

Is it worth optimizing if we are only spending $10K/month?

Absolutely. At $10K/month, a 30% reduction saves $36,000/year — enough to fund additional engineering capacity or extend your runway. The steps in this guide scale down to any budget. In fact, smaller bills are often faster to optimize because there are fewer stakeholders and shorter approval chains. The only step that becomes less impactful at lower spend is Savings Plans, where the minimum commitment granularity ($0.001/hour) means you can still participate, but the absolute dollar savings are smaller.

Related articles