Why cloud bills surprise people

A server in your own server room has a known price: you bought it. In the cloud, anyone with the right permissions can start resources in seconds, every resource has its own meter, and the bill arrives after the month is over. That is the point of the cloud — you pay only for what you use, without buying hardware first (see What Is Cloud Computing?) — but it also means spending is decided by many people, in small steps, often without seeing the price.

Typical stories behind an unexpected bill:

  • A test environment that was "only needed for the demo" and ran for six months.
  • A popular download or video served straight from storage, paying for every gigabyte sent to the internet.
  • Debug logging left on in production, ingesting gigabytes per hour.
  • An access key leaked in a public code repository, used by someone else to run expensive machines.
  • An autoscaling group or serverless function in a loop that kept scaling out.

What you pay for

Almost every cloud charge falls into one of four groups. Each service combines them differently, which is why its pricing page is worth reading before you build on it.

What Measured as Examples
Compute Time a resource runs (per second or hour), times its size Virtual machines, containers, managed databases; serverless functions per invocation and duration
Storage Gigabytes stored per month, per storage class Disks (billed on provisioned size, even if empty), object storage, snapshots, backups, logs
Requests and operations Per thousand or million operations Reading and writing objects, application programming interface (API) calls, database reads and writes, messages in a queue
Data transfer Gigabytes moved, depending on direction and destination Traffic to the internet, between regions, sometimes between availability zones

On top of that come fixed monthly fees for some services (a managed firewall, a support plan, a dedicated connection) and software licenses that may be included in the hourly price, such as Windows Server on a virtual machine.

Data transfer and egress

Data transfer is where the four groups surprise people most. Egress cost is the charge for data leaving a provider's network — to the internet, to another region, or back to your own data center. Data coming in (ingress) is generally free. This asymmetry is deliberate: moving data in is easy, moving it out again costs money, which makes leaving a provider harder — although since 2024 the large providers waive egress fees for customers who move their data away, and the European Union (EU) Data Act is phasing out such switching charges (see Deployment Models on vendor lock-in).

Egress is usually priced per gigabyte in tiers, with a small free allowance and a lower price per gigabyte as volume grows. A calculation with made-up tiers:

# Fictitious tiered egress prices, per GB sent to the internet per month.
FREE_GB = 100
TIERS = [            # (up to this many GB, price per GB)
    (10_240, 0.09),
    (51_200, 0.07),
    (None, 0.05),    # everything above
]


def egress_cost(gb):
    cost, lower = 0.0, FREE_GB
    for upper, price in TIERS:
        if gb <= lower:
            break
        top = gb if upper is None else min(gb, upper)
        cost += (top - lower) * price
        lower = top
    return cost


for tb in (0.05, 2, 20):
    gb = tb * 1024
    print(f"{tb:>5} TB out per month: € {egress_cost(gb):>8,.2f}")
 0.05 TB out per month: €     0.00
    2 TB out per month: €   175.32
   20 TB out per month: € 1,629.40

Two terabytes sounds like a lot, but a 200 MB installer downloaded 10,000 times is about that. A content delivery network (CDN) in front of the storage often lowers both the price per gigabyte and the load on the origin (see Regions and Availability Zones).

Pricing models

For compute and some databases, you can buy the same capacity in several ways. The trade-off is always flexibility against price.

  • Pay-as-you-go pricing (Amazon Web Services, or AWS, calls it On-Demand): you pay a fixed rate per second or hour while a resource runs, with no commitment. You can stop at any moment, which makes it right for new, short-lived or unpredictable workloads — and the most expensive rate per hour.
  • Reserved capacity: you commit to a specific kind of resource (for example a certain instance family in one region) for one or three years, in exchange for a large discount. You pay for every hour of the term, whether the resource runs or not. Paying some or all of it up front usually lowers the price further.
  • A savings plan is a commitment to spend a fixed amount per hour on compute for one or three years. Usage up to that amount gets the discounted rate, usage above it is billed at pay-as-you-go rates. It is more flexible than a reservation: the discount follows you when you change instance size, family or (for the broadest plans) region.
  • Spot capacity: the provider's unused capacity at a deep discount, which the provider can take back at short notice. Good for work that can be interrupted and restarted; see Compute in the Cloud.
Model AWS Microsoft Azure Google Cloud
No commitment On-Demand Pay-as-you-go On-demand
Commitment to specific resources Reserved Instances Azure Reservations Resource-based committed use discounts
Commitment to a spend per hour Savings Plans Azure savings plan for compute Spend-based (flexible) committed use discounts
Interruptible spare capacity Spot Instances Azure Spot Virtual Machines Spot VMs (successor to preemptible VMs)
Automatic discount for steady use — — Sustained use discounts (some machine types)

Comparing the models for one server

One server of the same size, priced four ways. The first run is a steady workload that runs all year; the second runs only half the time.

# Fictitious round prices for one server of the same size, per hour.
# Real prices differ per provider, region, size and month.
PRICE_PER_HOUR = {
    "Pay-as-you-go": 0.100,
    "1-year commitment": 0.065,
    "3-year commitment": 0.045,
    "Spot (if not interrupted)": 0.030,
}
HOURS_PER_YEAR = 24 * 365


def yearly_cost(model, hours_used):
    if "commitment" in model:
        return PRICE_PER_HOUR[model] * HOURS_PER_YEAR  # you pay for every hour
    return PRICE_PER_HOUR[model] * hours_used          # you pay for what runs


for usage in (1.00, 0.50):
    hours = HOURS_PER_YEAR * usage
    print(f"Server runs {usage:.0%} of the year ({hours:,.0f} hours)")
    baseline = yearly_cost("Pay-as-you-go", hours)
    for model in PRICE_PER_HOUR:
        cost = yearly_cost(model, hours)
        print(f"  {model:<27} € {cost:>6,.0f}  ({cost / baseline - 1:+.0%})")

break_even = PRICE_PER_HOUR["1-year commitment"] / PRICE_PER_HOUR["Pay-as-you-go"]
print(f"A 1-year commitment pays off above {break_even:.0%} usage")
Server runs 100% of the year (8,760 hours)
  Pay-as-you-go               €    876  (+0%)
  1-year commitment           €    569  (-35%)
  3-year commitment           €    394  (-55%)
  Spot (if not interrupted)   €    263  (-70%)
Server runs 50% of the year (4,380 hours)
  Pay-as-you-go               €    438  (+0%)
  1-year commitment           €    569  (+30%)
  3-year commitment           €    394  (-10%)
  Spot (if not interrupted)   €    131  (-70%)
A 1-year commitment pays off above 65% usage

For the steady workload, commitment clearly wins. At 50% usage the 1-year commitment is 30% more expensive than simply paying as you go, because you pay for hours you do not use. The break-even is the ratio of the two prices: here 65% usage. Spot is cheapest on paper, but a steady production server that can be taken away at short notice is not what spot is for.

Hidden costs

Some charges are easy to overlook because nobody "uses" the resource. The details differ per provider and change over time, so check the pricing page of each service you use; common examples at the time of writing:

  • Egress to the internet and between regions, as above.
  • Network address translation (NAT) gateways, which let private subnets reach the internet: they are charged per hour and per gigabyte processed, so a private server that downloads large updates through one pays twice (see Cloud Networking).
  • Traffic between availability zones, which some providers charge per gigabyte in each direction. A chatty application spread over three zones can build up a noticeable bill.
  • Idle resources: stopped virtual machines no longer pay for compute, but their disks and reserved public IP addresses still cost money. AWS, for example, charges for every public IPv4 address, in use or not.
  • Forgotten snapshots and backups that outlive the server they came from. Lifecycle rules and retention policies clean them up automatically (see Cloud Storage).
  • Log ingestion and retention, especially logs kept forever by default (see Monitoring, Logging and Observability).
  • Load balancers, managed firewalls and dedicated connections with a fixed hourly price, left running in test environments.

Cost visibility: tags, budgets and cost tools

Tags and cost allocation

A cloud bill for a whole organization is one big number. To know which team, product or customer causes which part of it, you label resources. A cost allocation tag is a key-value label on a resource, such as project=webshop or environment=test, that the billing tools use to group and filter costs. AWS and Azure call them tags, Google Cloud calls them labels.

Tags only help if they are consistent: agree on a few required keys (for example project, environment, owner, cost-center), write them in one spelling, and enforce them with policies. In infrastructure as code, set them once for everything a configuration creates:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

provider "aws" {
  region = "eu-central-1"

  default_tags {
    tags = {
      project     = "webshop"
      environment = "production"
      owner       = "team-shop"
      cost-center = "cc-4120"
    }
  }
}

Budgets and alerts

A budget is an amount you expect to spend per month (or quarter, or year), for the whole account or for a tag such as one project. The provider sends an alert when actual or forecast spending crosses a percentage of it — for example at 50%, 80% and 100%. The forecast alert is the valuable one: it warns you in week one that the month is heading for twice the budget.

A budget alert warns; by itself it does not stop anything. You can connect it to automation (stop test machines, remove permissions), but most organizations prefer a person to decide. Cost anomaly detection goes a step further and flags unusual spending patterns without a fixed threshold.

Cost tools by provider

Purpose AWS Microsoft Azure Google Cloud
Estimate before you build AWS Pricing Calculator Azure pricing calculator, Azure Migrate business case Google Cloud Pricing Calculator
Analyze actual costs AWS Cost Explorer Microsoft Cost Management (cost analysis) Cloud Billing reports
Budgets and alerts AWS Budgets, Cost Anomaly Detection Cost Management budgets, anomaly alerts Cloud Billing budgets and alerts
Savings recommendations AWS Compute Optimizer, Trusted Advisor Azure Advisor Recommender (Active Assist)

The same data is available from the command line and APIs, for example last month's costs per project tag with the AWS CLI:

$ aws ce get-cost-and-usage \
    --time-period Start=2026-09-01,End=2026-10-01 \
    --granularity MONTHLY --metrics UnblendedCost \
    --group-by Type=TAG,Key=project
{
    "GroupDefinitions": [
        {
            "Type": "TAG",
            "Key": "project"
        }
    ],
    "ResultsByTime": [
        {
            "TimePeriod": {
                "Start": "2026-09-01",
                "End": "2026-10-01"
            },
            "Total": {},
            "Groups": [
                {
                    "Keys": [
                        "project$"
                    ],
                    "Metrics": {
                        "UnblendedCost": {
                            "Amount": "212.4",
                            "Unit": "USD"
                        }
                    }
                },
                {
                    "Keys": [
                        "project$intranet"
                    ],
                    "Metrics": {
                        "UnblendedCost": {
                            "Amount": "96.1",
                            "Unit": "USD"
                        }
                    }
                },
                {
                    "Keys": [
                        "project$webshop"
                    ],
                    "Metrics": {
                        "UnblendedCost": {
                            "Amount": "1843.7",
                            "Unit": "USD"
                        }
                    }
                }
            ],
            "Estimated": false
        }
    ],
    "DimensionValueAttributes": []
}

The group project$ with an empty value is spending on resources without a project tag: here almost 10% of the bill that nobody owns. Shrinking that group is often the first FinOps goal.

Rightsizing and scheduling

Rightsizing means matching the size of a resource to what the workload actually uses. Servers are often chosen generously "to be safe" and then run at 5–10% central processing unit (CPU) load for years. If monitoring shows a virtual machine never exceeds 20% CPU and half its memory, the next size down is likely enough — and each size step down roughly halves the price. The provider's recommendation tools (see the table above) suggest candidates; check them against peak periods such as month-end before you resize.

Scheduling switches off what nobody uses at certain times. Development and test environments are typically used during office hours only, yet often run 24 hours a day, seven days a week:

# A development environment: 6 servers at a fictitious € 0.20 per hour each.
SERVERS = 6
PRICE_PER_HOUR = 0.20
WEEKS_PER_YEAR = 52

always_on = 24 * 7                  # hours per week
office_hours = (19 - 7) * 5         # 07:00-19:00, Monday to Friday

for label, hours in (("Always on", always_on), ("Office hours only", office_hours)):
    yearly = SERVERS * PRICE_PER_HOUR * hours * WEEKS_PER_YEAR
    print(f"{label:<18} {hours:>3} h/week  € {yearly:>8,.2f} per year")

saved = 1 - office_hours / always_on
print(f"Schedule saves {saved:.0%} of the compute cost")
Always on          168 h/week  € 10,483.20 per year
Office hours only   60 h/week  € 3,744.00 per year
Schedule saves 64% of the compute cost

The saving comes from arithmetic, not discounts: a week has 168 hours and office hours are only 60 of them. Stopped machines still pay for their disks, so the real saving is slightly lower. Tools such as Instance Scheduler on AWS, Azure Start/Stop VMs v2 and instance schedules in Google Compute Engine do the switching.

Other quick wins in the same spirit: delete unattached disks and old snapshots, move rarely read data to a cheaper storage class with a lifecycle policy, use autoscaling instead of sizing for the peak, and consider managed or serverless services for workloads that are idle most of the time.

FinOps as a practice

FinOps (a blend of "finance" and "DevOps") is the practice of managing cloud spending as a shared responsibility of engineering, finance and the business, so that each euro or dollar spent delivers value. The FinOps Foundation, a project of the Linux Foundation, maintains the FinOps Framework and a certification program.

The key idea is accountability. In a traditional data center, the purchasing department controlled spending. In the cloud, engineers decide spending every time they choose an instance size or a storage class. FinOps gives them the cost data to make those choices well, and gives finance the context to understand why the bill changes.

The FinOps lifecycle: Inform leads to Optimize, Optimize leads to Operate, and Operate leads back to Inform in a continuous loop. Inform see and allocate costs Optimize rightsize, commit, clean up Operate budgets, policies, habits continuous, not a one-time project
The three phases of the FinOps lifecycle. Each round makes costs more visible, spending more efficient and good habits more routine.
  • Inform: make costs visible and allocate them to teams and products with tags and accounts. Show each team its own costs (showback) or actually charge them to its budget (chargeback). Relate costs to business value with unit costs, such as cost per order or per customer.
  • Optimize: act on what you see — rightsizing, scheduling, cleanup, choosing commitments for the steady base load, better architecture.
  • Operate: make it routine — budgets and alerts, tagging policies, cost reviews in team meetings, cost as a criterion in design decisions.

FinOps is not only about spending less. Spending more on a product that grows faster can be the right decision; the goal is that the decision is made knowingly.

Comparing with on-premises: total cost of ownership

"Is the cloud cheaper?" has no general answer. A fair comparison looks at the total cost of ownership (TCO) of both options over several years, not at the price of one server versus one virtual machine. On-premises costs include hardware and its replacement every few years, spare capacity for peaks and growth, data center space, power and cooling, network connections, software licenses, and the staff time to run all of it. Cloud costs include the monthly bills, egress, support plans, and staff time too: someone still has to design, secure and operate the environment.

Broadly, the cloud tends to win for variable or unpredictable workloads, for fast experiments and for organizations without their own data center skills. Large, steady and predictable workloads can be cheaper on your own hardware, which is why some organizations move specific workloads back (see What Is Cloud Computing?). The result depends heavily on the organization; treat any calculator's outcome as a starting point for discussion, not as a verdict.

Checklist for a new cloud account

  1. Create a budget with alerts first — even a small one, on actual and forecast spending, sent to a shared team address rather than one person's mailbox.
  2. Protect the account: multi-factor authentication on the root or global administrator account, no access keys for it (see Identity and Access Management). A leaked key is also a cost risk.
  3. Agree on required tags and enforce them with policies; activate them for cost reporting.
  4. Turn on cost anomaly detection where the provider offers it.
  5. Restrict which regions and expensive resource types may be used, where your organization's policies allow it.
  6. Set retention periods on logs, and lifecycle rules on storage, snapshots and backups.
  7. Schedule development and test environments to stop outside office hours.
  8. Review costs weekly at first, then monthly; commit to reservations or savings plans only once usage is stable.