Back to blog
Cloud & DevOps

Your AWS Bill Isn't High Because AWS Is Expensive. It's High Because Your Architecture Is.

Overprovisioned instances, chatty services, and data transfer traps: where cloud money actually leaks, and the architecture fixes that cut bills 30 to 60 percent.

A high AWS bill feels like a pricing problem. So teams go hunting for discounts: Savings Plans, Reserved Instances, a call with the account manager. Those help at the margins. But the bill is mostly a mirror. It reflects decisions made in the architecture, most of them months or years before anyone opened Cost Explorer.

The good news: the biggest line items are usually the most fixable, and the fixes are engineering decisions, not procurement ones. Below are the places cloud money actually leaks, roughly in the order we tend to find them, and what to change.

Read the bill before you touch anything

Most cost work starts with a guess. Someone remembers a big instance and kills it. Start with data instead. Cost Explorer grouped by service, then by usage type, tells you within an hour where 80 percent of the money goes. The usual blind spot is untagged resources: if half your spend has no owner tag, you cannot attribute it, and unattributed spend is exactly where waste hides.

  • Group Cost Explorer by service, then usage type; find the top five line items before optimizing anything
  • Enforce a tagging policy (team, environment, service) and treat an untagged production resource as a bug
  • Turn on the Cost and Usage Report if you need per-resource detail; Cost Explorer alone rounds too much

Right-sizing: most instances run near idle

The most common leak is compute provisioned for a peak that rarely arrives. Average CPU utilization across a lot of fleets sits at 10 to 20 percent. Teams size for the worst Black Friday minute and pay for it every minute of the year. Compute Optimizer flags the over-provisioned instances for free; the hard part is trusting it enough to act.

  • Check CloudWatch for sustained CPU and memory; anything under ~40 percent average is a candidate to shrink
  • Move steady x86 workloads to Graviton (ARM) instances for roughly 20 percent better price-performance
  • Use burstable T-family instances for spiky, low-average workloads instead of fixed M/C families
  • Autoscale on real signals so you size for the average and scale into the peak, not the reverse

Data transfer: the charges nobody models

Compute is on the invoice in bold. Data transfer hides in the fine print, and on chatty systems it can be 20 to 30 percent of the bill. The three classics: traffic crossing Availability Zones (billed in both directions), NAT Gateway processing charges, and egress to the internet. None of these show up in a napkin estimate, which is exactly why they surprise people.

  • Cross-AZ traffic is billed per GB each way; keep chatty services and their databases in the same AZ where availability allows
  • NAT Gateway charges per GB processed; route S3 and DynamoDB through VPC Gateway Endpoints (free) instead of the NAT
  • Put a CDN in front of internet egress; CloudFront egress is cheaper than raw EC2 egress and cache hits cost nothing
  • Avoid casual cross-region replication; inter-region transfer is the most expensive tier

Chatty services multiply everything

Microservices split a monolith's function calls into network calls. Each hop now costs latency, a load balancer request, and often a cross-AZ transfer. An N+1 query that was invisible inside a process becomes thousands of billable round trips across the wire. The architecture didn't get more expensive per request; it started generating more requests.

This is where the service diagram and the bill start to agree. If two services talk on every request, they probably shouldn't be two services, or they should at least be colocated. The cost signal is real information about where your boundaries are drawn wrong.

Storage: cheap per GB, expensive by neglect

Storage rarely dominates a bill, but when it leaks it's nearly pure waste, because nobody is watching it. Orphaned EBS volumes from terminated instances, snapshots kept forever, gp2 volumes that should be gp3, and S3 buckets with no lifecycle policy all bleed slowly and permanently.

  • Migrate gp2 EBS volumes to gp3: about 20 percent cheaper, with configurable IOPS decoupled from size
  • Delete unattached volumes and stale snapshots; automate it so it doesn't depend on anyone's memory
  • Set S3 lifecycle rules to move cold data to Infrequent Access or Glacier, and expire logs you'll never read
  • Turn on S3 Intelligent-Tiering for buckets with unpredictable access patterns

Serverless and managed aren't automatically cheaper

Lambda and Fargate remove idle cost, which is excellent for spiky traffic and poor for steady, high-throughput work, where a reserved EC2 or ECS cluster is far cheaper per unit. RDS is easy to over-provision because resizing feels risky. And commitment discounts only pay off after you've right-sized: buy a Savings Plan against a bloated baseline and you've just locked in the waste for one to three years.

The mental checklist

Before you negotiate a discount, walk the architecture. Where is compute sitting idle? What crosses an AZ or a region on every request? What storage has no owner and no lifecycle? What did you make serverless that runs all day anyway? Right-size first, commit second. A 30 to 60 percent reduction is a normal outcome of that order of operations, and it comes from engineering, not from the invoice. When the architecture is honest about the load, the bill usually is too.

Want help applying this to your system?

Book a call