💸 Cloud Cost Control: Stop Surprises Before They Start

Why a cloud bill surprises people, how late every cost alert really is (with the numbers from each provider's documentation), what an alert can and cannot stop, the hour arithmetic behind forgotten resources, and a routine for before, during and after an experiment

💡 How this page was made. The arithmetic was run for real as small Python programs on 6 October 2026: hours in a month, the cost of a forgotten public IP address, and how much money accrues during each alert delay. The delays and limits are from the providers' documentation, read on 6 October 2026 (AWS Budgets and Cost Anomaly Detection, Azure Cost Management, Google Cloud budget notifications, Oracle Cost Analysis and budgets). The hourly rate used in the delay table (3 per hour) is a made-up example, not a real price, and the only real price on the page is AWS's public IPv4 charge of $0.005 per hour, which AWS publishes. Nothing was run on a cloud account. The traps here are collected from the earlier pages of this series: the services maps (Azure, AWS, Google Cloud, Oracle), the networking, containers, Kubernetes and EC2 pages, and the free VM guides.

Why bills surprise people

Almost every surprising cloud bill comes from one of three things:

  1. Something still exists. A server that was stopped but not deleted, a disk, a snapshot, a public IP address, a load balancer, a NAT gateway, a database, a Kubernetes cluster. Most cloud resources bill for existing, not for being used.
  2. Data moved. Traffic out of the cloud, and between regions, is billed. A test that copies a lot of data somewhere can cost more than the servers.
  3. Something scaled by itself. Autoscaling, a loop that creates resources, a job that never stops, an application that logs without limit.

And there is a fourth thing that makes the other three worse: you find out late. The next section has the numbers.

Every alert is late

Cost data is not live. It arrives in batches, and the alerts run on the batches. From the providers' documentation (read on 6 October 2026):

Conclusion: there is a blind spot of half a day to two days on every cloud. What can accrue in it depends on the rate. An illustration with a made-up rate of 3 per hour (a larger machine, a gateway, a database; not a real price), using the delays above (as run):

>>> delays = {'AWS Budgets (updates up to 3 times a day)': 12, 'Azure cost data (8 to 24 hours)': 24, 'Cost Anomaly Detection (up to 24 hours)': 24, 'Oracle Cost Analysis (up to 48 hours)': 48}
>>> example_rate = 3.00
>>> for name, hours in delays.items():
...     print('%-46s %2d h -> %6.2f accrues unseen at %.2f/hour' % (name, hours, hours * example_rate, example_rate))
... 
AWS Budgets (updates up to 3 times a day)      12 h ->  36.00 accrues unseen at 3.00/hour
Azure cost data (8 to 24 hours)                24 h ->  72.00 accrues unseen at 3.00/hour
Cost Anomaly Detection (up to 24 hours)        24 h ->  72.00 accrues unseen at 3.00/hour
Oracle Cost Analysis (up to 48 hours)          48 h -> 144.00 accrues unseen at 3.00/hour
>>> print('a resource that runs for a weekend (60 hours):', 60 * example_rate)
a resource that runs for a weekend (60 hours): 180.0
>>> print('and a month (730 hours):', 730 * example_rate)
and a month (730 hours): 2190.0

An alert is not a limit

Every budget on every cloud warns. From the documentation: AWS Budgets notifies you and can optionally act (below); Azure says "Resources aren't affected, and your consumption isn't stopped"; Google says an alerts-only budget "doesn't automatically cap" usage; Oracle calls budgets "soft limits" that "do not enforce spending limits". What each cloud lets you add on top:

What actually caps spending: the free account types, by their design: the AWS free plan (no charges until you upgrade, and the account closes when it ends), the Azure free account (the subscription is disabled when the credit ends), the Google Cloud trial (resources stop when it ends) and the Oracle trial (paid resources are reclaimed). The account pages (Azure, AWS, Google Cloud, Oracle) have the details. On a normal paid account, in the documentation I read, there is no built-in hard cap.

The arithmetic of hours

Most cloud compute bills by the hour (or the second), so the question is how many hours. As run:

>>> import calendar
>>> [calendar.monthrange(2026, m)[1] * 24 for m in (2, 4, 10)]
[672, 720, 744]
>>> round(365 * 24 / 12, 1)
730.0
>>> one_vm = 31 * 24
>>> one_vm
744
>>> 2 * one_vm
1488
>>> free_hours = 750
>>> free_hours - one_vm
6
>>> round(free_hours / 24, 1)
31.2
>>> office_hours = 10 * 5 * 52 / 12
>>> round(office_hours)
217
>>> round(100 * office_hours / (365 * 24 / 12))
30

A forgotten public IPv4 address on AWS is billed at $0.005 per hour (AWS's announcement, effective February 1, 2024), whether it is in use or idle:

>>> ipv4_per_hour = 0.005
>>> for hours in (730, 744):
...     print(hours, 'hours ->', round(ipv4_per_hour * hours, 2), 'per address per month')
... 
730 hours -> 3.65 per address per month
744 hours -> 3.72 per address per month
>>> print('five forgotten addresses:', round(5 * ipv4_per_hour * 730, 2), 'per month')
five forgotten addresses: 18.25 per month
>>> print('one forgotten address for a year:', round(ipv4_per_hour * 24 * 365, 2))
one forgotten address for a year: 43.8

That is 3.65 to 3.72 a month per address, 43.80 for a year for one address nobody remembers, and 18.25 a month for five. Small, but nothing stops it: it is the typical "I only deleted the machine" leftover.

The usual suspects, in one list

Collected from this series. Things that bill while they exist, even if idle:

Data that moves: traffic leaving the cloud, traffic between regions, and in some cases between zones (check each provider's networking pricing page: the networking page says what is documented).

Finding who and what: attribution

When the number is wrong, you need to answer "which resource, and whose?" Set that up before you need it:

A routine

Before you create anything

  1. A budget with an email you read, and an anomaly alert if the cloud has one (AWS Cost Anomaly Detection, Azure anomaly alerts). Remember the delays.
  2. A calendar entry for the delete date, and a tag that says it.
  3. Infrastructure as code so that the clean-up is one command (Terraform: terraform destroy).
  4. A separate account, subscription, project or compartment for the experiment.
  5. Free-tier limits written down (the hours, the sizes, the regions). A free tier is a list of exceptions: anything outside it bills.

While it runs

  1. Look at the cost page yourself at the end of each day, not only the alert. Look for new line items.
  2. Stop what you are not using, correctly (deallocate on Azure).

When you finish

  1. Delete the whole container (resource group, project, compartment, stack) rather than resources one by one.
  2. List what remains in every region: disks, snapshots, IPs, load balancers, buckets, registries. (AWS: EC2 Global View.)
  3. Check the bill the next day and again three days later, because of the delays above.
  4. Remove the budget and the alert rules only after that, not before.

When the number is already wrong

  1. Stop the meter first: stop or delete the biggest resources (find them in the cost analysis tool, grouped by resource), even before you understand why.
  2. Find the cause: new resource, changed size, more usage, more data out? The three types of change in Azure's guide are new costs, removed costs and changed costs.
  3. Check the access: a surprise resource you did not create may mean leaked credentials (see the IAM page): rotate keys, remove unknown identities, and review the audit logs.
  4. Contact the provider's support with the dates and the resource IDs. I did not read any provider's policy on refunds for mistakes, so this page makes no promise about them.

Common mistakes

What this page did not cover

Reserved capacity and savings plans, spot and preemptible machines, right-sizing, showback and chargeback in companies, billing exports and dashboards, and provider pricing in detail: read each provider's pricing pages and calculators, since prices change and differ by region.

The cheat sheet

← EC2 From Scratch: A Server You Can Trust · ↑ All tutorials · First Boot on Every Cloud: cloud-init and User Data →