Skip to main content
Guide

Why did my AWS bill go up?

Most AWS bill increases come from a short list of causes. The quickest way to find yours is not to work down the list, but to narrow the change first: which service moved, what kind of charge it was, from which day, and whose it is. By then the cause usually names itself.

Updated

Narrow the change down before you guess

A list of common causes helps only once you know where to look. These five questions, asked in this order, take most investigations from “the bill went up” to a specific resource and the team that owns it. Each one uses data you already have in AWS Cost Explorer or in the Cost and Usage Report.

  1. 1Compare like with like. Put two windows of the same length side by side — the last 14 days against the 14 before, or a full month against the previous one — on the same cost basis. If you buy Savings Plans or Reserved Instances, compare amortized cost, so an upfront payment does not look like a spike; and leave credits and refunds out (Cost Explorer’s charge-type filter), so their timing does not hide the change.
  2. 2Find the service that moved. Group by service and sort by the difference, not by the total. The largest service on your bill is rarely the one that changed.
  3. 3Find the kind of charge. Within that service, group by usage type. The usage type says what you are paying for — hours of an instance, gigabytes moved between Availability Zones, gigabytes through a NAT gateway, snapshot storage — and most of the causes below have a usage type of their own.
  4. 4Find the day. Switch to daily granularity and look at the shape of the line (the next section reads it). A step on one day points to a change someone made; a slope points to growth.
  5. 5Find the owner. Group the changed cost by linked account, then by your cost allocation tags — team, service, environment. Cost with no tag is an answer too: keep it as its own line instead of spreading it across teams, so nobody mistakes an unowned cost for a shared one.

Step, slope or spike: what the shape of the line tells you

  • A step — the cost jumps on one day and stays up. Look for a deploy, a configuration change, a new resource, a commitment that ended or an extended-support charge that started that day. Your release history and AWS CloudTrail usually name it.
  • A slope — the cost climbs a little every day. That is growth: traffic, data that accumulates, logs and snapshots that are never deleted.
  • A spike that falls back — a batch job, a load test, a backfill or a one-off data transfer. Worth knowing about; rarely worth a fix.
  • A jump on the first of the month — some charges post once a month, so a first-day jump is often a monthly fee rather than a change in usage.

Nine common causes, and how to confirm each

Once you know the service, the usage type and the day, match them against these. Each entry says what to look for in Cost Explorer, so you can confirm the cause instead of assuming it.

1. Data transfer between Availability Zones

Traffic between Availability Zones in the same Region is charged per gigabyte, in each direction, under the usage type DataTransfer-Regional-Bytes. The usual trigger is a change that put two chatty services — or a service and its database — in different zones, or a replica that started serving reads from another zone.

2. NAT gateway processing

A NAT gateway is billed for each hour it runs and for each gigabyte it processes (NatGateway-Hours and NatGateway-Bytes). When private workloads pull more from the internet, or reach Amazon S3 or DynamoDB through the NAT gateway instead of a gateway VPC endpoint, the per-gigabyte line grows with them. VPC Flow Logs show which sources send the traffic.

3. A Savings Plan or reservation that ended

When a Savings Plan or a Reserved Instance reaches the end of its term, the same usage is billed at On-Demand rates from then on, with nothing changed in your infrastructure. In Cost Explorer, a drop in Savings Plans or reservation coverage on the day the cost stepped up is the signature.

4. Free Tier or credits running out

Usage that the AWS Free Tier or promotional credits used to cover becomes billable when the offer expires or the credit balance reaches zero. Filter Cost Explorer by charge type: if credits were offsetting the usage before, the “increase” is the same usage, no longer covered.

5. Logs and metrics that keep growing

Amazon CloudWatch charges for the log data you ingest and for the log data you keep. Log groups left at the default never-expire retention, debug logging left on after an incident, or a new high-volume source grow the bill steadily. Under CloudWatch, look for DataProcessing-Bytes (ingestion) and TimedStorage-ByteHrs (storage).

6. Storage that is never cleaned up

EBS snapshots, volumes no longer attached to anything, old machine images and S3 buckets without lifecycle rules only ever accumulate, so their line is a slope, not a step. They appear as EBS:SnapshotUsage and EBS:VolumeUsage under EC2, and as TimedStorage-ByteHrs under S3.

7. More capacity than before — on purpose or not

A raised Auto Scaling minimum, a larger instance type, more Fargate tasks after a deploy, or a Kubernetes node group that scaled up for a load test and never scaled back: compute is the most common step change. The day of the step usually matches a deploy or a configuration change you can find in your release history or in AWS CloudTrail.

8. Public IPv4 addresses

Since February 2024, AWS charges by the hour for every public IPv4 address, whether it is attached to a running resource or sitting idle. Tasks or nodes that each get a public address, and Elastic IPs that were never released, appear under Amazon VPC as PublicIPv4:InUseAddress and PublicIPv4:IdleAddress.

9. Extended support for older versions

Running an Amazon EKS Kubernetes version past the end of its standard support, or an Amazon RDS for MySQL or PostgreSQL major version past its end of standard support, adds an extended-support charge on top of the usual price. Nothing in your workload changes on the day it starts — which is exactly why it is easy to miss.

The AWS tools that help, most of them free

  • AWS Cost Explorer — the comparison and grouping above. The console is free of charge; its API is charged per request.
  • AWS Cost Anomaly Detection — free. It learns your spending pattern, alerts on unusual spend, and lists the services, accounts, Regions and usage types that contributed most. A good first alarm.
  • AWS Budgets — alerts when actual or forecast cost crosses a threshold you set.
  • The Cost and Usage Report, delivered through AWS Data Exports — line-item detail, including resource IDs when you turn them on, for when the usage type is not specific enough.

What these tools leave to you is the explanation: putting the service, the usage type, the day and the owner together into one sentence someone can act on.

How CloudJaeger answers the same question

CloudJaeger is built around this question. It connects to AWS through a read-only IAM role whose policy is published before you connect, syncs the bill, and explains a change in the same order as above: which service moved and by how much, from which day, and which team owns it — with untagged spend kept as its own line. Every figure names its source and how fresh it is, and any saving it suggests stays labelled projected until your next bill confirms it.

How one change reads, on a fictional estateIllustrative example — not customer data
  1. 1The change. Spend rose $4,420 (+8.1%) in 14 days against the 14 days before, on billed cost.
  2. 2What moved. checkout-api Fargate compute +$3,720, analytics-warehouse cross-AZ transfer +$1,060, log-archive S3 storage −$360 — three changes that add up to the cent.
  3. 3When. checkout-api stepped up by about $620 a day from Sep 9: a step, so a change someone made, not growth.
  4. 4Next. Ask the team behind checkout-api what shipped on Sep 9, and whether the cross-AZ traffic is a design choice or an accident.

See a bill explained, end to end

The live demo walks one cost change from the bill to the team that owns it, on a fictional estate. No account needed.