AWS Services and Settings That Quietly Leak Money — a Standing Checklist

5 min read

The previous post hunted charges that suddenly jumped; this one covers the opposite kind — waste that quietly raises the baseline without ever drawing a step on the graph. Spikes get noticed and fixed. This kind hides behind the feeling of “it’s always been about this much” and survives for months or years. Here it is as a checklist worth walking once a quarter. Prices are from us-east-1 and vary by region.

Storage — the most certain wins, the most neglected #

Leftover gp2 volumes — gp3 is about 20% cheaper per GB than gp2 (about $0.08 vs $0.10/GB-month) and guarantees its baseline performance (3,000 IOPS, 125MiB/s) regardless of size. Converting gp2 to gp3 happens online, with no downtime, via Elastic Volumes, so there is effectively no reason to keep gp2 around. List volumes filtered by type in the console or CLI and convert in bulk. Choosing between gp3 and io2 gets its own post.

Orphaned volumes and snapshots — volumes whose “delete on termination” flag was off keep billing in the available state after their instances are gone. For snapshots, as covered in the previous post, item 3, lifecycle policy is the answer.

Unused S3 storage classes — for buckets with uncertain access patterns, just moving to Intelligent-Tiering starts saving automatically (monitoring costs a small per-object fee). If rarely accessed data — logs, backups — sits in Standard, push it down to Standard-IA or the Glacier tiers with lifecycle rules. Mind the minimum storage durations (30 days for IA, 90 for Glacier) and retrieval fees; don’t demote data you fetch often.

Network — fixed costs built into the architecture #

Public IPv4 addresses — about $3.6/month per address, used or not. Release unattached Elastic IPs immediately, and replace the habit of giving every instance a public IP with NAT or a load balancer in front. Public IP Insights shows the account-wide address count. A few dozen addresses runs into three figures a month by itself.

Everything routed through NAT Gateway — the previous post covered this as a spike cause, but even without a spike it’s a structural fixed cost. Gateway endpoints for S3 and DynamoDB are free, yet plenty of accounts don’t use them. If private subnets talk to AWS services, check endpoints first. In a dev VPC, removing the NAT Gateway entirely (about $33/month plus processing) in favor of public subnets or a NAT instance is also on the table.

Abandoned load balancers — an ALB bills over $16/month on hours alone, at zero traffic. Find leftovers from experiments and decommissioned services by looking for target groups with zero healthy targets.

Compute — both the rate and the hours #

Old instance generations — newer generations are generally cheaper and faster at the same size. If m4s and m5s are still around, moving to m7g/m8g (Graviton) or the m7i family improves rate and performance at once. Graviton migration gets its own checklist in the next post.

On-demand without RI or Savings Plans — if baseline capacity that will clearly run for a year or more is billed at on-demand rates, a Compute Savings Plan alone cuts tens of percent off that portion. Cost Explorer’s Recommendations propose a commitment size from your actual usage, so there’s no need to guess. Pricing model comparisons are covered in SAA Domain 4-1.

Dev environments running 24/7 — an environment used weekdays 9 to 7 but left running all week wastes about 70% of its hours. Automate off-hours stops with Instance Scheduler or a small Lambda plus EventBridge. Dev RDS instances are stop candidates too (they auto-restart after 7 days; the scheduler stops them again).

Multi-AZ and oversizing in dev — dev RDS instances cloned from the production template often keep Multi-AZ and provisioned IOPS. Single-AZ, burstable instances, and gp3 are enough for most dev environments.

Observability and logs — where the defaults are the waste #

CloudWatch Logs retained forever — log group retention defaults to “never expire.” Separate from ingestion cost (previous post, item 2), storage grows without bound. Set a retention period (say 30–90 days) across all log groups, and export only what needs long-term retention to S3, then down to Glacier. S3 storage is several times cheaper per GB than CloudWatch Logs storage.

High-cardinality custom metrics and unused dashboards — $0.30/month per metric looks small until it’s multiplied per instance and per item. Quarterly, remove metrics attached to dashboards nobody views and alarms that never fire.

Building the routine #

The items here split into one-time fixes (gp2 conversion, retention settings) and recurring ones (orphaned resources, dev sizing). For the recurring kind, two things help.

  • Trusted Advisor / Compute Optimizer — automatic reports of unused resources and downsizing candidates. Use them as the starting point of the quarterly pass.
  • Ownership via tags — enforce team and environment tags on every resource, and “can’t delete it because nobody knows whose it is” disappears. An undeletable resource is waste in its final form.

Summary #

  • Baseline waste, unlike spikes, never shows on the graph. Only a quarterly checklist catches it.
  • Start with storage: gp2 → gp3 is no-downtime and an immediate ~20% — the cheapest win available.
  • Public IPv4, abandoned ALBs, and always-through-NAT designs bill regardless of traffic.
  • Put Savings Plans on the certain baseline, and scheduled stops on dev environments.
  • CloudWatch Logs retention defaults to forever. Setting a period on every log group is the place to start.
X