Filter concepts by levelShowing all levels.

AWS · Section 49

Cost Optimization

Level
intermediate
Read
30 min
Concepts
3

AWS bills on specific dimensions — compute as time times size, storage as bytes times time, requests per operation, provisioned capacity whether used or not, data transfer per GB with direction mattering, and a managed-service premium that buys operational work — and an architecture is really a choice among those dimensions. Making the bill legible starts with cost allocation tags, which apply forward only, then Cost Explorer for exploration and Cost and Usage Reports for the line-item record; AWS Budgets adds alerting on cost and usage, updated up to three times a day with billing delay on top, which makes it a signal rather than a cap unless a budget action is attached. The reduction order matters: clean up idle resources and rightsize before buying any commitment, because a Savings Plan bought over unrightsized capacity locks in the waste for its whole term. Above all of that sits the observation that architecture dominates: rate work moves a bill by tens of percent, while routing S3 traffic through a free gateway endpoint instead of NAT, making chatty services zone-aware, putting a CDN in front of large static assets, sizing non-production databases for correctness, turning off production debug logging, and scheduling idle environments down can move it by multiples — and none of those has to be re-bought in three years.

What is true here

  1. Six pricing dimensions; data transfer and provisioned-but-idle capacity surprise most often.
  2. Tag and attribute first, clean up and rightsize second, commit last.
  3. Budgets alert on a delay and do not cap spend — budget actions are the enforcement.
  4. Architecture optimization has a far higher ceiling than rate optimization, and is permanent.
  5. Rank opportunities by waste rather than by line size.

What you will be able to do

  • Read a service by its pricing dimensions and predict what will dominate its bill
  • Set up cost allocation tagging and use Cost Explorer and CUR for the right questions
  • Sequence cleanup, rightsizing, and commitments so a Savings Plan does not lock in waste
  • Configure a budget with forecast alerts and an action, and explain why it is not a cap
  • Find and fix the six architecture-level cost sinks: NAT, cross-AZ, egress, database sizing, logging, idle compute
From pricing dimensions to the change that actually moves the bill
madevisible bywhichpoints at

Pricing dimensions

Tags, tools, commitments

Architecture dominates cost

  • Pricing dimensions
    • leads to Tags, tools, commitments (made visible by)
  • Tags, tools, commitments
    • leads to Architecture dominates cost (which points at)
  • Architecture dominates cost

Cost Optimization

The dimensions AWS bills on, the tools and commitments that make spend visible and cheaper, and the architecture choices that dominate the total.

AWS Pricing Dimensions

coreintermediate

A service is not "expensive" or "cheap" — it bills on specific dimensions, and those dimensions are what an architecture is really choosing between. Compute is time × size. Storage is bytes × time. Requests are per operation. Provisioned capacity is paid whether used or not. Data transfer is per GB and depends on where the bytes go. Managed services add their own per-unit charge on top.

Think of it as

Reading a service by its pricing dimensions is the same skill as reading it by its quotas. Both tell you what the service is optimized for, and both explain surprises on the bill long before the invoice arrives.

What we're doing: Explain a data transfer bill nobody could attribute to a service.

cross-az-transfer.txttext
A chatty microservice pair: service A calls service B 4,000 times per
second. Both run across three AZs with no zone affinity, so roughly
two thirds of the calls cross an Availability Zone boundary.

Each crossing is billed, in both directions.

The line on the bill is "EC2 data transfer", attributed to no service
in particular, and it is larger than the compute cost of either service.
1
Nothing here is misconfigured. Spreading across AZs is correct for availability; the cost is the trade nobody priced.
6
Data transfer is the dimension least visible in service-level cost views, which is why it is usually discovered late and by accident.

Why this works: Architecture decisions choose pricing dimensions, and data transfer is the one that is easiest to incur accidentally and hardest to attribute afterwards. Knowing which dimension a design leans on is what makes the bill predictable.

Comparing services by instance price alone

Wrong

text
# "Self-managed PostgreSQL on EC2 is cheaper than RDS."

Better

text
# Compare the total: instance + storage + backup + the engineering time
# for patching, failover, and restore testing

What you see: The instance line is genuinely lower and the total cost of ownership is higher, once someone is on call for backups, upgrades, and failover.

Why: A managed-service premium is the price of operational work being transferred. Comparing only the instance line prices the hardware and ignores the labour, which is the larger and less elastic of the two for most teams.

The dimensions, roughly in order of how often they surprise people

Data transfer

Cross-AZ, cross-Region, and NAT processing — invisible until the invoice

Provisioned capacity

Paid at 3am; idle costs the same as busy

Requests

Scales with traffic; a retry storm is a bill

Storage

Bytes × time; nothing ages out on its own

Compute

Time × size; the dimension everyone already watches

  1. Data transfer — Cross-AZ, cross-Region, and NAT processing — invisible until the invoice
  2. Provisioned capacity — Paid at 3am; idle costs the same as busy
  3. Requests — Scales with traffic; a retry storm is a bill
  4. Storage — Bytes × time; nothing ages out on its own
  5. Compute — Time × size; the dimension everyone already watches

Which dimension dominates, by service

Which dimension dominates, by service
ServicePrimary dimensionThe usual surprise
EC2 / FargateCompute time × sizeIdle non-production capacity overnight and at weekends
S3Storage + requestsMillions of small-object requests, and old data never tiered
DynamoDB (on-demand)RequestsA scan where a query would have done
DynamoDB (provisioned)Provisioned capacityProvisioned for peak, used at average
RDSProvisioned instance-hours + storageA production-sized instance in every environment
NAT gatewayHourly + per GB processedS3 traffic routed through it instead of a gateway endpoint
CloudWatch LogsIngestion + storageDebug logging left on in production

Together

text
# Same workload, two shapes, very different bills
Lambda:  2M invocations x 300 ms x 512 MB      -> pay only while running
Fargate: 2 tasks x 0.5 vCPU x 24 x 30 hours    -> pay all month
# Bursty traffic favours the first; steady traffic usually favours the second

Remember: Compute (time × size), storage (bytes × time), requests (per operation), provisioned capacity (paid idle), data transfer (per GB, direction matters), managed-service premium (buying operational work). An architecture chooses dimensions; the bill is the consequence.

See also: cost tools and commitments · architecture dominates cost · pricing dimensions

Cost Tools, Tags, and Commitments

coreintermediate

Cost work has two halves. Visibility: Cost Explorer for exploring, Cost and Usage Reports for the detailed record, and cost allocation tags for attributing spend to a team or a product. Reduction: rightsizing and cleanup remove waste, lifecycle policies move cold data down, and Savings Plans, Reserved Instances, and Spot exchange flexibility for a lower rate.

Think of it as

Attribute first, reduce second, commit last. Committing to a rate before removing waste locks in the waste for one to three years, which is the most common expensive mistake in this section.

text
# Tag policy first — attribution is impossible retroactively
CostCenter   = platform | payments | data
Environment  = prod | staging | dev
Owner        = <team>
# Then activate them as cost allocation tags in Billing

What we're doing: Understand why AWS Budgets is an alerting tool, not a spending cap.

budgets.txttext
Budget: monthly cost budget of $40,000, alert at 80% actual and at
forecast-to-exceed.

AWS Budgets information is updated up to three times a day, typically
8-12 hours after the previous update.

There is also a delay between incurring a charge and being billed for
it, so spend can pass the threshold before any notification arrives.

Conclusion: a budget tells you a trend has changed. It does not stop
anything — that is what a budget action (e.g. applying a restrictive
IAM policy) is for.
1
Forecast alerts are the useful half: they fire before the month's spend has already happened.
5
AWS documents this refresh cadence directly. A budget is not a real-time guard.
8
AWS also documents this second delay explicitly, which is why a runaway can exceed a threshold substantially before anyone hears about it.

Why this works: Treating a budget as a cap leads to real overspend, because two independent delays sit between the charge and the notification. Budget actions — applying a policy that denies further provisioning — are the enforcement mechanism; the alert is the signal.

Buying a commitment before rightsizing

Wrong

text
# Spend is high -> buy a 3-year Savings Plan covering current usage

Better

text
# Delete idle resources and rightsize first, then commit to the smaller,
# steady baseline that remains

What you see: The commitment covers capacity that a rightsizing pass would have removed, so the discount is applied to waste for three years and the eventual rightsizing produces no saving at all.

Why: A commitment locks in a spend level, not a resource. Committing before cleanup means the cleanup cannot reduce the bill, since the committed amount is owed regardless — the discount is real and it is being applied to capacity nobody needed.

The tools, and what each one is for

The tools, and what each one is for
ToolAnswersNot for
Cost ExplorerWhere is the money going, and how has that changed?Line-item reconciliation
Cost and Usage ReportsExactly which resource incurred which chargeQuick exploration
Cost allocation tagsWhich team or product owns this spend?Anything created before the tag existed
AWS BudgetsAlerting and acting when spend or usage crosses a thresholdHard spending caps
Rightsizing recommendationsWhich instances are over-provisionedWorkloads with spiky, bursty profiles
Savings Plans / RIsA lower rate for committed usageCapacity you might not still need in a year
SpotA much lower rate for interruptible workAnything that cannot tolerate a two-minute notice

Together

text
# The order that saves the most, and locks in the least
1. Tag everything, activate cost allocation tags
2. Delete idle resources; rightsize the over-provisioned ones
3. Lifecycle old storage down to colder classes
4. Only then commit: Savings Plans / RIs on the steady baseline
5. Spot for the interruptible remainder

Remember: Tag and attribute first, clean up and rightsize second, commit last — a Savings Plan bought before rightsizing locks in the waste. Budgets alert (updated up to three times a day, with billing delay on top); budget actions are what actually enforce. Non-production out of hours is the easiest large saving.

See also: aws pricing dimensions · architecture dominates cost · purchasing options

Architecture Choices Dominate Cost

coreadvanced

Rate optimization — commitments, rightsizing — moves the bill by tens of percent. Architecture moves it by multiples. The same workload built with a gateway endpoint instead of NAT, or with zone-aware routing instead of random cross-AZ calls, can cost a fraction of the alternative while doing exactly the same work.

Think of it as

Rate work is negotiating a better price for what you buy. Architecture work is buying less. The second has a far higher ceiling, and it is invisible on a cost dashboard organised by service, because the expensive decision usually shows up as a data transfer line nobody owns.

What we're doing: Compare what rate work and architecture work each achieve on the same bill.

two-levers.txttext
Starting point: $92,000 per month.
  compute $38k · database $21k · data transfer $19k · logs $9k · other $5k

Rate work: 3-year Savings Plans on the steady compute baseline, plus
rightsizing. Compute $38k -> $24k. Saved $14k (15%).

Architecture work:
  gateway endpoints for S3/DynamoDB      data transfer $19k -> $7k
  non-production scheduled down          compute        $24k -> $17k
  production DEBUG logging off           logs            $9k -> $2k
  staging database rightsized to purpose database       $21k -> $14k
Saved a further $33k, without any commitment.
4
Rate work is real and worth doing — it is also bounded by the discount percentage available.
9
None of these four changes alters what the system does. Three of them are configuration, and all four survive a renegotiation.

Why this works: Rate optimization is capped by the discount on offer; architecture optimization is capped by how much unnecessary work the design does. The second is usually larger, and unlike a commitment it does not have to be re-bought every three years.

Optimizing the largest line item rather than the most wasteful one

Wrong

text
# Compute is the biggest line, so spend the quarter on compute

Better

text
# Rank by waste, not by size. A $19k transfer line that should be $2k
# is a bigger opportunity than a $38k compute line that is 85% justified.

What you see: Months of effort produce a modest compute saving while a data transfer line that could be almost eliminated continues untouched.

Why: Size and reducibility are different properties. The largest line is often the one doing the most useful work, while a mid-sized line can be almost entirely avoidable — so the ranking that matters is spend that should not exist, not spend that is large.

Two levers, very different ceilings

Rate optimization

  • +Savings Plans, Reserved Instances, Spot
  • +Rightsizing and scheduling
  • +Typically tens of percent
  • +Fast to apply, easy to measure

Architecture optimization

  • Gateway endpoints instead of NAT
  • Zone-aware routing instead of random cross-AZ
  • CDN instead of origin egress
  • Can be multiples, and permanent
  • Rate optimization
    • Savings Plans, Reserved Instances, Spot
    • Rightsizing and scheduling
    • Typically tens of percent
    • Fast to apply, easy to measure
  • Architecture optimization
    • Gateway endpoints instead of NAT
    • Zone-aware routing instead of random cross-AZ
    • CDN instead of origin egress
    • Can be multiples, and permanent

The six areas worth investigating first

The six areas worth investigating first
AreaWhat to look forThe fix
NAT gatewaysS3 or DynamoDB traffic in the processing chargeGateway endpoints; centralize the remaining NAT
Cross-AZ trafficA large "data transfer" line with no owning serviceZone-aware routing, or co-locate chatty pairs
Internet egressLarge static assets served from the originCloudFront in front, with sensible cache headers
DatabasesProduction-sized instances in every environmentSize non-production for correctness, not for load
LoggingIngestion GB far above what anyone readsTurn off production DEBUG; route bulk logs to S3
Idle computeNon-production running at 3am on a SundaySchedule it down outside working hours

Together

text
# One route table entry, one large line item removed
Before: private subnets 0.0.0.0/0 -> NAT gateway (S3 traffic included)
After:  + gateway endpoint for S3 and DynamoDB
# Gateway endpoints have no hourly or per-GB charge

Remember: Rate work moves the bill by tens of percent; architecture moves it by multiples. Check NAT and gateway endpoints, cross-AZ traffic, internet egress, database sizing across environments, production log levels, and idle non-production — and rank by waste, not by line size.

See also: aws pricing dimensions · cost tools and commitments · nat gateway cost tradeoffs

Advertisement