Your AWS bill is a direct consequence of how you build your system. And it can surprise you unpleasantly: forgot to turn off a test machine, picked an instance "with headroom", pushed a lot of traffic outward — and at the end of the month the number is bigger than you expected. The good news: almost all of this is manageable if you understand what exactly AWS charges you for.
Cost optimization is not a one-off "clean-up" before the end of the quarter, but a habit: choose the pricing model that fits the shape of the workload, don't pay for excess, and keep spending in plain sight. This article is an introductory overview for those just starting to get to grips with the cloud. You don't need to be a financier here: it's enough to understand a few basic levers.
Compute pricing models
By "compute" we mean the servers your code runs on — in AWS that's primarily EC2 (virtual machines). Here's the interesting part: AWS sells the same capacity at different prices depending on what commitment you're willing to make.
On-demand — you pay as you go, per second or per hour, with no commitment. Turn a machine on and the meter starts running; turn it off and it stops. This is the most expensive but also the most flexible option. Analogy: a metered taxi — convenient, you promise nothing in advance, but you pay the most per kilometer. Good for getting started, when the load is unpredictable, and for short experiments.
Reserved Instances and Savings Plans — you promise AWS in advance that you'll use compute for 1 or 3 years, and in return you get a large discount (up to roughly 70%). Analogy: an annual gym membership — cheaper than pay-per-visit if you're definitely going to show up. The difference between the two options is flexibility: Reserved Instances are tied to a specific machine type, while Savings Plans are a commitment to spend a certain amount per hour on compute, and the discount is applied automatically regardless of which instance you actually launch (and Compute Savings Plans extend to other compute services as well — Fargate, Lambda). You take these for a stable baseline load that will definitely run around the clock.
Spot — this is AWS capacity that's free right now, which it hands out at a huge discount (up to roughly 90%). The catch: AWS can take it back with just a 2-minute warning. Analogy: last-minute plane tickets — very cheap, but the flight can be canceled. Good for tasks that are safe to interrupt and restart later: batch data processing, working through queues, stateless servers that are easy to replace. Important: Spot is not covered by Savings Plans — it's a separate discount mechanism.
A typical strategy combines all three: constant baseline load runs on reserved/savings plans, unexpected peaks are topped up with on-demand, and interruptible background work is handed to spot.
Right-sizing: pay for what you need, not for headroom
Right-sizing is bringing resource capacity in line with actual demand. The most common overpayment in the cloud is paying for things that go unused.
Typical sources of wasted money:
- Machines "with headroom". You took an instance twice as powerful as you really need, "just in case" — and you pay for unused cores and memory around the clock.
- Forgotten resources. You spun something up for an experiment, closed the project, and the machine, disk, or database kept running and dripping into the bill.
- Test environments at night. A development environment runs 24/7 even though people work 8 hours a day — for two thirds of the time it's just burning money.
What to do about it: scale over-provisioned resources down to actual demand, turn off what isn't needed on a schedule (for example, test environments at night and on weekends), and delete what's been abandoned. Auto-scaling belongs here too — it adds servers under load on its own and removes them when demand falls, so you pay for current demand rather than for a peak configuration all the time.
Storage and traffic
Data costs money too, and it also costs differently depending on how often you access it and where you move it.
Storage classes. In object storage S3 you can keep data in different "classes": for frequent access — more expensive to store but cheap to read; for archival — the opposite, pennies to store but slow to retrieve and with a surcharge. The logic is simple: hot data (read constantly) is kept in the regular class, while cold data (year-old logs, backups you're unlikely to need) is moved to the cheap archival class. You don't have to do this by hand — you set up lifecycle rules that move objects into cold classes by age automatically.
Here's what a simple rule looks like: anything older than 90 days moves to the cheap archival class.
{
"Rules": [
{
"ID": "archive-old-logs",
"Status": "Enabled",
"Filter": { "Prefix": "logs/" },
"Transitions": [
{ "Days": 90, "StorageClass": "GLACIER" }
]
}
]
}
Egress traffic. Egress is data leaving AWS outward (for example, your service's responses to users on the internet). AWS charges for this outbound traffic, and at large volumes it's a noticeable line item on the bill. Inbound traffic (ingress) is usually free — you pay specifically for what you send out. The tricky part is that this also includes traffic between availability zones, and even more so between regions: an architecture that constantly moves data across these boundaries gets expensive without you noticing, because in the code it looks like an ordinary network call. More on boundaries and zones — in the article on networking in AWS.
Cost control
You can't optimize what you can't see. Basic hygiene keeps spending transparent.
Tags. A tag is a key-value pair you attach to a resource (for example, team: payments or env: prod). When resources are tagged, the bill can be broken down by teams, services, or environments so you can understand who pays for what. Without tags the bill is a faceless pile of rows. A subtlety for beginners: for a tag to appear as a breakdown axis in reports, you first have to explicitly enable it as a Cost Allocation Tag in the billing settings — simply attaching a tag isn't enough. Once enabled, the breakdown by default starts from the moment of activation; and if a resource was tagged earlier, the management account can request a backfill retroactively for up to 12 months.
Budgets with alerts. The AWS Budgets service lets you set a spending threshold and receive a notification when spending approaches or exceeds it. The point is to learn about a spike right away, by email, rather than a month later when you open the final bill.
Regular review. Cost Explorer is a built-in tool that shows spending over time, by service and by tag. It's worth looking at regularly, not only when the bill scared you. The goal is simple: that every significant line be explainable — you can see what costs money and why, and where there's overpayment.
Example: check the current spending by service for June via the AWS CLI. The End parameter in Cost Explorer is exclusive — to include all of June, we set it to 2026-07-01.
aws ce get-cost-and-usage \
--time-period Start=2026-06-01,End=2026-07-01 \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--group-by Type=DIMENSION,Key=SERVICE
And a forecast of future spending is a separate command, get-cost-forecast: it predicts costs for a given period ahead based on your history.
aws ce get-cost-forecast \
--time-period Start=2026-07-01,End=2026-08-01 \
--metric UNBLENDED_COST \
--granularity MONTHLY
And this is how you define a budget with an alert (JSON format, understood by both the AWS CLI and infrastructure-as-code tools).
{
"BudgetName": "monthly-prod",
"BudgetLimit": { "Amount": "500", "Unit": "USD" },
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}
Where this applies
Cost optimization is a day-to-day engineering task, not a separate "accounting" job. It shows up at every stage: when choosing a server type, when configuring data storage, when designing the network, when deploying environments in the delivery pipeline. Cost management is one of the pillars of the Well-Architected Framework, the methodology AWS recommends for designing systems.
Typical beginner mistakes:
- Everything on on-demand forever. Convenient at the start, but for constant load it's an overpayment by several times — you should move the baseline to savings plans as soon as the load becomes predictable.
- An instance "with headroom". People pick a bigger size "to be safe" and then pay for idle capacity for years. Better to start modestly and scale up if needed.
- Forgotten resources. The experiment was closed, but the disks, snapshots, and databases stayed. Tags and regular review in Cost Explorer help here.
- Invisible egress. The architecture moves data between regions or outward, and the bill grows for no visible reason. It helps to understand network boundaries.
What to learn next: get to grips with the basic building blocks — AWS fundamentals and compute services — so you understand what exactly the bill is for. Then scaling and availability, where cost and reliability meet head-on, and infrastructure as code: when resources are described in Terraform or CloudFormation, they're easier to review, tag, and tear down when forgotten.