DEV Community

Hive80-lab
Hive80-lab

Posted on

How I Cut Our Cloud Bill by 40% Without Changing a Single Line of Application Code

How I Cut Our Cloud Bill by 40% Without Changing a Single Line of Application Code

The easiest way to reduce your cloud bill isn't rewriting code — it's cleaning up what you forgot you were paying for.

I inherited an AWS account that was spending $4,200/month. After 3 hours of cleanup — no code changes, no architecture redesign — the bill dropped to $2,520/month. That's $1,680/month saved, $20,160/year.

Here's exactly what I did.

Step 1: Find the Orphaned Resources (30 minutes)

The biggest waste isn't what you're using — it's what you forgot to turn off.

#!/bin/bash
# cleanup_audit.sh - Find wasted cloud spend

echo "=== ORPHANED EIPs ==="
aws ec2 describe-addresses --query 'Addresses[?AssociationId==null]' --output table

echo "=== UNATTACHED EBS VOLUMES ==="
aws ec2 describe-volumes --query 'Volumes[?State==`available`]' --output table

echo "=== OLD SNAPSHOTS (>90 days) ==="
aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[?StartTime<`2026-01-01`]' --output table

echo "=== STOPPED EC2 INSTANCES ==="
aws ec2 describe-instances --query 'Reservations[*].Instances[?State.Name==`stopped`]' --output table

echo "=== IDLE LOAD BALANCERS ==="
aws elb describe-load-balancers --query 'LoadBalancerDescriptions[?Instances==[]]' --output table

echo "=== UNUSED SECURITY GROUPS ==="
aws ec2 describe-security-groups --query 'SecurityGroups[?GroupId!=`default`]' --output table
Enter fullscreen mode Exit fullscreen mode

What I found:

  • 3 orphaned Elastic IPs ($108/month)
  • 14 unattached EBS volumes ($280/month)
  • 87 old snapshots ($45/month)
  • 2 stopped instances that nobody remembered ($340/month)
  • 1 idle load balancer ($90/month)
  • 23 unused security groups ($0, but a security risk)

Total found in orphaned resources: $863/month

Step 2: Right-Size Your Instances (45 minutes)

Most instances are over-provisioned. A t3.xlarge that averages 5% CPU doesn't need 4 cores.

#!/usr/bin/env python3
"""rightsize.py - Analyze and recommend instance right-sizing"""
import boto3
import json
from datetime import datetime, timedelta

def get_cpu_utilization(instance_id, days=14):
    cloudwatch = boto3.client('cloudwatch')
    response = cloudwatch.get_metric_statistics(
        Namespace='AWS/EC2',
        MetricName='CPUUtilization',
        Dimensions=[{'Name': 'InstanceId', 'Value': instance_id}],
        StartTime=datetime.now() - timedelta(days=days),
        EndTime=datetime.now(),
        Period=3600,
        Statistics=['Average', 'Maximum']
    )

    datapoints = response['Datapoints']
    if not datapoints:
        return None

    avg = sum(d['Average'] for d in datapoints) / len(datapoints)
    max_cpu = max(d['Maximum'] for d in datapoints)

    return {'avg': avg, 'max': max_cpu}

def recommend_size(instance_type, avg_cpu, max_cpu):
    # If average < 15% and max < 40%, recommend downsizing
    if avg_cpu < 15 and max_cpu < 40:
        return 'DOWNSIZE - avg {:.1f}%, max {:.1f}%'.format(avg_cpu, max_cpu)
    elif avg_cpu < 30 and max_cpu < 60:
        return 'CONSIDER - avg {:.1f}%, max {:.1f}%'.format(avg_cpu, max_cpu)
    else:
        return 'OK - avg {:.1f}%, max {:.1f}%'.format(avg_cpu, max_cpu)

def audit_instances():
    ec2 = boto3.client('ec2')
    reservations = ec2.describe_instances(
        Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]
    )

    recommendations = []
    for res in reservations['Reservations']:
        for inst in res['Instances']:
            cpu = get_cpu_utilization(inst['InstanceId'])
            if cpu:
                rec = recommend_size(
                    inst['InstanceType'],
                    cpu['avg'], cpu['max']
                )
                recommendations.append({
                    'instance_id': inst['InstanceId'],
                    'type': inst['InstanceType'],
                    'cpu_avg': cpu['avg'],
                    'cpu_max': cpu['max'],
                    'recommendation': rec
                })
    return recommendations
Enter fullscreen mode Exit fullscreen mode

What I found:

  • 3 instances averaging <10% CPU → downgraded from t3.xlarge to t3.medium
  • 1 instance averaging <20% CPU → downgraded from m5.large to t3.large
  • Savings: $420/month

Step 3: Switch to Spot Instances for Non-Critical Workloads (30 minutes)

If you have batch jobs, CI/CD runners, or development environments, they don't need on-demand instances.

#!/usr/bin/env python3
"""spot_migration.py - Identify spot-eligible workloads"""

def identify_spot_candidates():
    candidates = []

    # CI/CD runners
    if has_tag('Role', 'ci-runner'):
        candidates.append({
            'workload': 'CI Runner',
            'current_cost': on_demand_price,
            'spot_cost': spot_price,
            'savings': on_demand_price - spot_price,
            'interruption_risk': 'LOW - jobs are retryable'
        })

    # Batch processing
    if has_tag('Role', 'batch'):
        candidates.append({
            'workload': 'Batch Processing',
            'current_cost': on_demand_price,
            'spot_cost': spot_price,
            'savings': on_demand_price - spot_price,
            'interruption_risk': 'LOW - checkpoint and resume'
        })

    # Dev environments
    if has_tag('Environment', 'dev'):
        candidates.append({
            'workload': 'Dev Environment',
            'current_cost': on_demand_price,
            'spot_cost': spot_price,
            'savings': on_demand_price - spot_price,
            'interruption_risk': 'ACCEPTABLE - devs can reconnect'
        })

    return candidates
Enter fullscreen mode Exit fullscreen mode

What I found:

  • 2 CI/CD runners → moved to spot (70% savings on those instances)
  • 1 dev environment → moved to spot
  • Savings: $180/month

Step 4: Clean Up Old Data (30 minutes)

#!/usr/bin/env python3
"""data_cleanup.py - Find and clean old data"""

def audit_s3():
    s3 = boto3.client('s3')

    # Find old objects in all buckets
    for bucket in s3.list_buckets()['Buckets']:
        objects = s3.list_objects_v2(Bucket=bucket['Name'])

        old_objects = []
        large_objects = []

        for obj in objects.get('Contents', []):
            age = (datetime.now() - obj['LastModified'].replace(tzinfo=None)).days

            if age > 365:
                old_objects.append(obj)
            if obj['Size'] > 100 * 1024 * 1024:  # > 100MB
                large_objects.append(obj)

        # Recommend lifecycle policies
        if old_objects:
            print(f'{bucket["Name"]}: {len(old_objects)} objects > 1 year old')
            # Move to Glacier/Deep Archive

        if large_objects:
            print(f'{bucket["Name"]}: {len(large_objects)} objects > 100MB')
Enter fullscreen mode Exit fullscreen mode

What I found:

  • 340GB of logs older than 1 year → moved to Glacier Deep Archive
  • 12GB of old database backups → deleted (we had newer ones)
  • Savings: $95/month

Step 5: Review Reserved Instances and Savings Plans (15 minutes)

If you have steady-state workloads, you should have Reserved Instances or Savings Plans.

# Check your current RI coverage
aws ce get-reservation-coverage \
  --time-period Start=2026-09-01,End=2026-09-30 \
  --granularity MONTHLY
Enter fullscreen mode Exit fullscreen mode

What I found:

  • 0% RI coverage → bought 1-year no-upfront RIs for 4 steady instances
  • Savings: $122/month

The Final Tally

Optimization Monthly Savings Annual Savings
Orphaned resources $863 $10,356
Right-sizing $420 $5,040
Spot instances $180 $2,160
Data cleanup $95 $1,140
Reserved instances $122 $1,464
Total $1,680 $20,160

From $4,200/month to $2,520/month. A 40% reduction. Zero code changes.

The Ongoing Audit

Set up a monthly cost audit:

#!/bin/bash
# monthly_cost_audit.sh
# Run on the 1st of every month

python3 cleanup_audit.py > /tmp/audit_$(date +%Y%m).txt
python3 rightsize.py >> /tmp/audit_$(date +%Y%m).txt
python3 data_cleanup.py >> /tmp/audit_$(date +%Y%m).txt

# Email summary
mail -s "Monthly Cloud Cost Audit" ops@company.com < /tmp/audit_$(date +%Y%m).txt
Enter fullscreen mode Exit fullscreen mode

Want the complete cloud cost optimization toolkit? The Ops Starter Kit includes all the audit scripts, right-sizing recommendations, spot migration templates, and cost monitoring dashboards.

When was the last time you audited your cloud bill?

Top comments (0)