Back to Insights
Cloud & Reliability · 2026

Why Your AWS Bill Grew 40% Last Quarter - And What To Do About It

Many organizations migrated to cloud infrastructure without migrating to cloud thinking. The result is a generation of estates that carry the cost of cloud without the flexibility it was designed to deliver.

This has been coming up too often to ignore.

The most common thing I hear from engineering leaders in 2026 is not about AI adoption or a massive platform migration. It is much more mundane, and therefore much more annoying:

“Our AWS bill jumped last quarter and nobody on the team can fully explain why.”

That sentence is doing a lot of work.

Sometimes the bill went up because the business grew. Fair enough. If traffic doubled, customers increased, batch jobs became heavier, or a new product line started using the platform properly, the AWS bill should move. That is not necessarily a problem.

But quite often, that is not what happened. The bill went up because the account accumulated small pieces of operational drift. A volume left behind here. A staging environment running all weekend there. A NAT Gateway quietly processing traffic because of a networking decision made three years ago and never revisited.

No single item looks dramatic on its own. Together, they create the 40 percent surprise.

The uncomfortable bit is this: most cloud cost problems are not caused by one foolish decision. They are caused by reasonable decisions that stayed in place after the context changed.

The 5 Reasons Your Cloud Bill Spiked

1. Unattached EBS volumes accumulating quietly

Every time an EC2 instance is terminated without its storage being explicitly deleted, the EBS volume can stay behind. It is not doing anything useful. It is not serving traffic. It is just sitting there, billing you.

This is especially common in teams that create and destroy environments frequently: test machines, staging experiments, feature branch infrastructure, one-off migrations, debugging boxes, and the occasional “we will clean this up later” instance.

Later, as we all know, is a dangerous place.

A team that has been running on AWS for three or four years will often find hundreds of these leftovers. At common EBS storage rates, a few thousand gigabytes of forgotten storage can easily become a recurring few hundred dollars a month. The more irritating part is that this spend appears nowhere in architecture diagrams and nowhere useful in cost allocation.

What to do: Start with a boring inventory. In the EC2 console, go to Volumes and filter by volume state. AWS marks a volume as available when it is not attached to an instance. Do not immediately delete everything. Sort by age and size, check tags and snapshots, confirm with the owning team, then delete the obvious waste. If a volume might matter, snapshot it first with a short retention period rather than preserving the full live volume forever. AWS also has a managed Config rule, EC2_VOLUME_INUSE_CHECK, that flags volumes not attached to instances, which is a useful guardrail once the initial cleanup is done.

The point is not a heroic cleanup project. It is making orphaned storage visible every week, before it becomes folklore.

2. Dev and staging environments running around the clock

Production needs to run 24 hours a day. Your developer’s personal sandbox does not.

Neither does the staging environment that QA uses for four hours on Tuesday and Thursday. Neither does the demo environment that was important before the customer call and then quietly became a museum exhibit.

The default behaviour in most engineering teams is simple: leave everything running. Shutting things down requires effort. Turning them back on requires more effort. Remembering what depends on what requires even more. So the system settles into the most expensive possible default: everything stays alive.

That is how you end up paying full EC2 and RDS rates for environments idle 70 to 80 percent of the time. For a team with a handful of developers and a modest staging setup, this can become a few thousand dollars a month in avoidable spend.

What to do: Pick one non-production environment and make scheduling boring. Tag resources with something simple like Environment=staging, Owner=team-name, and Schedule=business-hours. Then use a scheduler to stop what does not need to run at night or on weekends. AWS provides Instance Scheduler on AWS for automating start and stop schedules for EC2 and RDS instances using tags, and Systems Manager has its own Resource Scheduler for the same purpose.

Do not start by designing the perfect global policy. That becomes a governance discussion and everyone loses the will to live. Start with the obvious cases: developer sandboxes, QA environments, demo stacks, old testing clusters. Keep an exception path for anything that genuinely needs to stay up.

The important bit: the safe path should be the easiest path. If shutting down non-production requires a ticket, a meeting, and one person who remembers the Terraform module, it will not happen.

3. Data transfer costs that nobody is watching

Data transfer is one of those costs that feels small until it is not.

AWS charges for several kinds of data movement, including traffic between Availability Zones, traffic between Regions, and traffic out to the internet. These charges are hard to reason about because they rarely show up as one neat line item. In detailed usage data, you often have to look across usage types, services, Regions, and direction of transfer to understand what is happening. Cost Explorer can filter and group by service, Availability Zone, Region, tag, usage type, and usage type group, and the Cost and Usage Report identifies transfer charges through lineItem/UsageType.

Modern architectures move a lot of data. Microservices call each other across Availability Zones. Logs move to a central observability system. Backups cross Regions. Analytics pipelines pull data out of operational stores. A SaaS platform doing any meaningful volume will often discover that data transfer is no longer background noise. It is part of the bill now, and deserves to be treated like part of the architecture.

What to do: Do a 90-day transfer review. In Cost Explorer, group by Usage type and Usage type group, then filter by the services that commonly move data in your account. Look for usage types related to regional transfer, inter-Region transfer, internet egress, NAT processing, and service-specific transfer. Then map the top three items back to architecture decisions: which services are talking, across which zones or Regions, and why?

The fix is not always “move everything into one Availability Zone.” That may be cheaper but also less resilient. The useful question is more specific: are we paying for cross-zone or cross-region movement that gives us no real reliability, performance, or product benefit? If yes, fix that first.

4. RDS instances sized for a peak that never came

When the database was provisioned two years ago, the decision probably made sense. You sized it for where you expected the business to be, not where it was. Nobody wants the database to be the first thing that falls over during growth.

But expected peaks misbehave. Sometimes they never arrive. Sometimes they arrive and level off. Sometimes the team adds caching, query improvements, read replicas, or a queue, and the original database size is no longer the right answer.

So the db.r5.2xlarge that looked responsible in the planning document is now sitting at 15 percent CPU utilisation most of the month. It is not bad engineering. It is an old decision that has not been revalidated.

Most teams have one or two of these. Larger teams have more. Rightsizing a single overprovisioned RDS instance can save serious monthly money, especially when the pattern repeats across staging, reporting, analytics, and production replicas.

What to do: Do not resize from a hunch. Pull the last 30 to 90 days of utilisation: CPU, memory pressure, connections, storage throughput, IOPS, read and write latency, and the actual shape of peak traffic. Compare that against AWS Compute Optimizer recommendations and the team’s own knowledge of upcoming load. Compute Optimizer supports EC2 instances, EBS volumes, RDS DB instances, and idle resources, with rightsizing preferences that allow longer lookback periods, including a 93-day option when enhanced infrastructure metrics are enabled.

Treat the recommendation as a starting point, not a commandment. A quiet CPU graph can hide lock contention, bad queries, or memory issues. But if an expensive database has been underused for three months and nobody can explain why it must remain that size, you have found a conversation worth having.

My preferred operating model is simple: review RDS sizes quarterly, make changes during a planned window, and keep a rollback path. Fancy can come later. First, stop paying for imaginary traffic.

5. NAT Gateway costs from architecture decisions made years ago

NAT Gateways are one of the most consistently underestimated cost sources in AWS accounts.

They solve a real problem: instances in private subnets often need to reach the internet for updates, dependencies, APIs, and third-party services, without being publicly reachable themselves. A NAT Gateway makes that possible.

The bill arrives later. AWS charges for each hour a NAT Gateway is available and for each gigabyte of data it processes, and that combination compounds quickly in accounts where private subnet resources make frequent external calls or route large volumes of AWS-service traffic through NAT unnecessarily. AWS’s own NAT Gateway pricing guidance calls out two practical checks: keep high-volume resources in the same Availability Zone as the NAT Gateway where appropriate, and consider interface or gateway endpoints when most NAT traffic is going to AWS services that support them.

This is common in accounts where the VPC architecture was created early, copied into multiple environments, and left alone because it worked. It did work. It just became expensive.

What to do: Start with the NAT bill broken down by NAT Gateway, Availability Zone, and data processed, then identify what is actually flowing through it. For many teams, the first discovery is that private resources are reaching AWS services through the NAT Gateway when a VPC endpoint would do the job directly. The second is cross-zone routing, where resources in one Availability Zone send traffic through a NAT Gateway in another. Neither pattern is automatically wrong, but both deserve scrutiny.

The fix may be adding gateway endpoints for S3 or DynamoDB, interface endpoints for heavily used AWS services, placing NAT Gateways in the right Availability Zones, or changing noisy jobs so they stop repeatedly pulling the same external dependencies.

A NAT Gateway is not bad. An unexamined NAT Gateway that became the default path for half your account is the problem.

The 3 Questions To Ask Your Team This Week

The first question is:

“Can someone show me a breakdown of our AWS spend by service and by environment for the last 90 days?”

If your team cannot produce this in under an hour, you do not have a cost problem yet. You have a visibility problem, and the cost problem is coming. This usually means the tagging model is missing, inconsistent, or decorative. Tags are useful only once they are applied, activated for cost allocation, and used in the actual cost review process.

The second question is:

“Which non-production environments are running right now that are not being actively used?”

Ask this on a Monday morning. The answer will probably be uncomfortable, but it is also one of the most fixable sources of waste. You do not need a six-month FinOps transformation to stop a sandbox at night.

The third question is:

“When did we last review our RDS and EC2 instance sizes against actual utilisation?”

If the answer is “when we provisioned them,” you are almost certainly paying for capacity you are not using. That does not mean every large instance is waste; sometimes headroom is a deliberate reliability decision. But deliberate headroom should have an owner, a reason, and a review date. Otherwise it is not engineering judgement. It is inertia with a monthly invoice.

Visibility Is Not The Same As Optimisation

There is an important distinction that gets lost in most cloud cost conversations.

Visibility means knowing what you are spending and why. Optimisation means making structural changes to reduce that spend without compromising reliability, performance, or delivery speed.

Most teams that have a cost problem actually have a visibility problem first. They are not careless, they simply do not have a clear, current picture of where the money is going. The bill is visible. The reason behind the bill is not.

Once that picture exists, the optimisation decisions are usually straightforward: delete unattached storage after a retention check, schedule the environments that do not need to run all week, review database sizes against actual utilisation, look at where data is moving, and revisit the NAT path nobody has questioned since the first VPC was created.

None of this is glamorous. That is probably why it works.

The invoice should not be the first monitoring system that tells you your architecture has drifted.

Cloud cost control is not a one-time cleanup. It is a review habit. The hard part is not finding one big saving, it is building the practice of looking regularly, while the numbers are still small enough to fix without drama.

Frequently Asked Questions

Why did my AWS bill increase 40% in one quarter?

Usually not one big mistake, but an accumulation of small drift. Most common ones are unattached EBS volumes, non-production environments running 24/7, untracked data transfer costs, oversized RDS instances, and NAT Gateway costs from old architecture decisions. Individually small, together significant.

What are the most common causes of unexpected AWS cost spikes?

The five most common are unattached EBS volumes, dev or staging environments left running continuously, data transfer charges across Availability Zones and Regions, RDS instances sized for traffic that never materialized, and NAT Gateway costs from unreviewed VPC architecture.

How do I find unattached EBS volumes in AWS?

In the EC2 console, go to Volumes and filter by volume state. AWS marks unattached volumes as “available.” Sort by age and size, check for snapshots, and confirm with the owning team before deleting. AWS Config’s EC2_VOLUME_INUSE_CHECK rule can flag these automatically going forward.

What is the difference between cloud cost visibility and cloud cost optimization?

Visibility means knowing what you are spending and why, a clear breakdown by service and environment. Optimization means making structural changes to reduce that spend without compromising reliability. Most teams have a visibility problem first; once spending is visible, optimization decisions become straightforward.

How often should engineering teams review AWS instance sizing?

Quarterly is a reasonable baseline. Pull 30 to 90 days of utilisation data (CPU, memory, IOPS, connections) and compare against AWS Compute Optimizer recommendations before resizing, not on a hunch.

If your infrastructure costs feel disconnected from your actual usage, we’d be glad to talk.

Schedule a Discovery Call