Article

The Five AWS Line Items That Quietly Eat Your Budget

·6 min read min read·👁 0
Dharmendra Singh Yadav

Dharmendra Singh Yadav

AI Full-Stack Engineer

Illustration of an AWS cost breakdown highlighting NAT gateway, data transfer and storage line items.

Nobody blows their AWS budget on EC2. Compute is the line item everyone watches, discusses in planning, and right-sizes when it grows. The overspend almost always comes from somewhere else — a handful of services that grow quietly because no single person ever decided they should.

Here are the five I see most often, why each grows unnoticed, and what to actually do.

1. NAT Gateways

A NAT gateway lets resources in a private subnet reach the internet. It bills two ways: an hourly charge simply for existing, and a per-gigabyte charge for every byte processed.

The hourly charge is small and predictable. The data processing charge is where budgets go to die, because everything leaving a private subnet passes through it:

  • Container image pulls on every deployment and every autoscaling event
  • Package downloads during CI builds running inside the VPC
  • Traffic to AWS services like S3 and DynamoDB that could have used a VPC endpoint and bypassed NAT entirely
  • Operating system updates across every instance

The fix is mostly VPC endpoints. A gateway endpoint for S3 and DynamoDB costs nothing and removes that traffic from your NAT path entirely. Interface endpoints for other services have their own hourly cost but frequently come out well ahead if the traffic volume is meaningful.

The second fix is architectural: question whether a workload genuinely needs to be in a private subnet reaching out through NAT, or whether it was placed there by a default template nobody revisited.

2. Data Transfer

This is the one that never appears in an architecture diagram, which is exactly why it surprises people.

AWS charges for data movement in several directions: between availability zones within a region, out to the internet, and between regions. Traffic within a single availability zone is generally free, which creates a specific trap.

You deploy across two availability zones for resilience — correct, standard practice. But now your application servers in one zone are talking to your database in another, and every query and every result crosses a billable boundary. A chatty microservice architecture can generate substantial charges purely from internal communication.

What to do:

  • Keep tightly-coupled, high-traffic components in the same availability zone, and use zone redundancy at the tier level rather than making every call cross zones
  • Put a CDN in front of anything serving significant volume to the internet — CDN egress is typically cheaper than direct egress, and you get caching as a bonus
  • Be deliberate about cross-region replication; it is easy to enable and easy to forget you enabled

3. Orphaned Storage

Storage waste accumulates because deletion is manual and nobody owns it.

Three specific offenders:

  • Unattached EBS volumes. You terminate an instance, the volume was not set to delete on termination, and it persists — billing monthly, attached to nothing, named after a project that shipped a year ago.
  • Snapshots outliving their volumes. Snapshots are independent resources. Automated backup policies create them on a schedule, and unless the policy also expires them, they accumulate indefinitely. This is the version of storage cost that is hardest to trace, because the volume it relates to no longer exists.
  • S3 without lifecycle rules. Logs, build artefacts, user uploads and database dumps all land in S3 and stay in the standard storage class forever. Objects nobody has read in two years cost the same as objects read constantly.

The fixes are unglamorous and effective: a lifecycle policy on every bucket that transitions old objects to cheaper storage classes and expires what should not persist; an expiry rule on every snapshot policy; and a scheduled job that reports unattached volumes so somebody has to look at the list.

4. CloudWatch Logs

You pay to ingest logs and you pay to retain them. Both charges are modest per gigabyte, which is precisely why nobody notices until the line item is significant.

The pattern is consistent. There is an incident. Someone raises the log level to debug to diagnose it. The incident resolves. The log level stays. Meanwhile the log group's retention setting was never configured, so the default is to keep everything forever.

Two years later you are paying to store verbose debug output from an outage nobody remembers.

What to do:

  • Set an explicit retention period on every log group — thirty days covers the overwhelming majority of debugging needs
  • Export anything needed for compliance to S3, where long-term storage is dramatically cheaper than CloudWatch
  • Audit log levels in production and treat "we raised it during an incident" as a temporary state with an owner
  • Check what your application logs per request; a stack trace on every handled error at high traffic adds up quickly

5. Idle Load Balancers and Forgotten Environments

Every load balancer bills hourly whether or not it has healthy targets behind it. So does every NAT gateway, every Elastic IP not attached to a running instance, and every provisioned database.

These accumulate through entirely reasonable behaviour. A staging environment for a feature that shipped. A load balancer for a service that was consolidated. A proof-of-concept from a client project that concluded. Each was created deliberately and none was deliberately kept.

The practical answer is a recurring audit rather than a clever tool. Once a quarter, list load balancers with zero healthy targets, unattached Elastic IPs, and databases with no connections over the last thirty days. The list is usually short and the savings are usually immediate.

Tagging helps enormously here, but only if enforced. A tag policy requiring an owner and an environment on every resource turns "what is this and can we delete it" from an archaeology exercise into a lookup.

How to Approach an Audit

If you have inherited an account and want to know where you stand:

  • Open Cost Explorer, group by service, view the last six months. You are looking for lines that trend upward without corresponding growth in traffic or customers. That divergence is waste, not scale.
  • Check the three usual suspects — load balancers with no healthy targets, unattached EBS volumes, log groups with no retention policy. These take fifteen minutes and frequently pay for the whole exercise.
  • Set a billing alarm at a threshold that would concern you, so the next surprise arrives as an alert rather than an invoice.
  • Then look at compute. By all means right-size instances and consider savings plans — but do it after the items above, because those are the ones growing without anyone's decision.

The Underlying Point

Cloud cost problems are rarely the result of a bad decision. They are the result of no decision at all — defaults that were never revisited, resources that outlived their purpose, retention settings nobody chose.

Which is good news, because it means the fix is usually a quarterly hour of attention rather than an architectural change.

Want a second pair of eyes on your infrastructure spend? See how I approach cloud work or get in touch.

Frequently Asked Questions

Quick answers to the questions readers ask most.

A NAT gateway charges both an hourly rate for existing and a per-gigabyte rate for everything passing through it. The hourly charge is predictable, but the data processing charge catches people out because all traffic from private subnets to the internet flows through it — including container image pulls, package downloads during builds, and traffic to AWS services that could have used a VPC endpoint instead. Teams running CI in a private subnet often pay more in NAT processing than in compute.

Data transfer, by a wide margin. Compute and storage are quoted per hour and per gigabyte and people plan for them. Data transfer is charged on movement between availability zones, out to the internet, and between regions, and none of that appears in an architecture diagram. A chatty service split across two availability zones can generate a substantial bill purely from talking to itself.

Yes. Snapshots are independent of the volume they came from. Deleting an EC2 instance removes the instance and possibly the volume, but any snapshots you or an automated backup policy created remain and continue to bill. This is one of the most common sources of storage cost that nobody can account for, because the resource it relates to no longer exists.

It can be, and it is usually invisible until it is large. You pay to ingest logs, then pay again to store them for as long as you retain them. The default retention on many log groups is 'never expire'. Debug logging left on in production after an incident, combined with indefinite retention, produces a line item that grows every month and that nobody attributes to a decision anyone made.

Open Cost Explorer, group by service, and set the period to the last six months. Look for any line that trends upward without a corresponding growth in traffic or customers. That divergence is almost always waste rather than scale. Then check three specific things: load balancers with no healthy targets, unattached EBS volumes, and log groups with no retention policy.

Let's talk.

Building production-grade SaaS, AI agents and mobile apps end-to-end.

Hiring for a senior role or have an interesting problem to solve? Drop a note — I read every message.