The $0.02/GB Charge That Never Shows Up as Its Own Line Item
Availability Zones make your app resilient. They also meter every byte between them.
AWS splits every region into multiple Availability Zones so a single data center failure doesn't take your application down. That resilience has a price: traffic that crosses an AZ boundary — Multi-AZ RDS replication, a load balancer routing to a target in another zone, an ECS task calling a database replica scheduled elsewhere — is billed at roughly $0.01/GB in each direction, so effectively $0.02/GB round-trip, on top of whatever the service itself already costs.
It's a real, well-known AWS billing behavior. What's less well known is that it almost never shows up as its own line item. Cost Explorer's default service breakdown shows "Amazon RDS," "EC2-Other," "Amazon Elastic Load Balancing" — the cross-AZ transfer charge is folded into whichever service generated it, not broken out on its own. You can be paying it every month without a single row in your bill that says "cross-AZ data transfer."
Where it actually comes from
- Multi-AZ RDS/Aurora — every write is synchronously replicated to the standby in another AZ. That replication stream is cross-AZ traffic, continuously, for the life of the database.
- ALB/NLB cross-zone load balancing — if targets are spread across AZs (the normal, recommended setup) and cross-zone balancing is on, a request landing on the load balancer in AZ-a can be routed to a target in AZ-b.
- EKS/ECS workloads split across AZs — the standard, resilient way to run a cluster — talking to a database, cache, or each other. A chatty microservice architecture with pods scheduled across 3 AZs can generate meaningfully more cross-AZ chatter than a monolith ever would.
- VPC-peered or Transit Gateway-routed traffic that happens to cross AZs along the way.
None of this is a mistake. Multi-AZ and cross-AZ load balancing are the correct way to run resilient infrastructure — this isn't a "turn it off" finding like an idle instance. It's a cost that comes bundled with an architecture decision most teams made for the right reasons and then never measured.
Where to actually find it in AWS's own data
AWS does track this at a granular level — it's just one dimension deeper than the
default view. GetCostAndUsage grouped by USAGE_TYPE (not just SERVICE) surfaces
usage types with a -DataTransfer-Regional-Bytes suffix, region-code-prefixed
(USE1-DataTransfer-Regional-Bytes for us-east-1, EUC1-... for eu-central-1,
and so on) — AWS's own label for "this byte crossed an AZ boundary inside the
region."
aws ce get-cost-and-usage \
--time-period Start=$(date -v-1m +%Y-%m-01),End=$(date +%Y-%m-01) \
--granularity MONTHLY \
--metrics UnblendedCost \
--group-by Type=DIMENSION,Key=SERVICE Type=DIMENSION,Key=USAGE_TYPE \
--query "ResultsByTime[0].Groups[?contains(Keys[1], 'DataTransfer-Regional-Bytes')]"
(On Linux, swap date -v-1m for date -d 'last month'.)
If that query returns nothing, your account genuinely isn't generating billable cross-AZ traffic worth worrying about — which is a perfectly normal, good outcome for a smaller or single-AZ-heavy account. If it returns a number, that's real spend currently living anonymously inside your RDS/EC2/ELB bill.
What GreenOps Scan does with it
This is one of the rare findings that costs zero new IAM permissions or API
calls: ce:GetCostAndUsage grouped by SERVICE + USAGE_TYPE is a call the
scanner already makes for its billing summary, to extract Lambda-specific usage
costs. Cross-AZ transfer just needed its own filter on the same response.
- severity: MEDIUM
module: VPC-ENDPOINTS
resource: 123456789012
resourceType: AWS Account
issue: $18.40 spent last month on inter-AZ ("regional") data transfer — traffic crossing Availability Zone boundaries within the region, billed separately from the services generating it
costSavings: $18.40/mo
confidence: LOW
co2Savings: 0.00 kg/mo
recommendation: Review Multi-AZ replication, ALB/NLB cross-zone routing, and whether workloads (ECS/EKS tasks, Lambda, EC2) that talk to each other frequently can be pinned to the same AZ as their dependencies — this is a data-mix and architecture cost, not a single resource to delete
That's an illustrative shape. Confidence is reported as low on purpose: Cost
Explorer's USAGE_TYPE grouping tells you how much crossed an AZ boundary and
what it cost, but not which two resources were talking to each other — that detail
lives in VPC Flow Logs, not billing data. The scanner surfaces the number so you know
whether it's worth digging into; it doesn't pretend to know the exact wiring.
What's actually worth doing about it
Unlike most findings in this scanner, there's no single command that fixes this — because in most cases, nothing is wrong. The options, roughly in order of effort:
- Check if ALB/NLB cross-zone load balancing is costing you specifically. For Network Load Balancers, cross-zone balancing is billed per GB and is off by default; if it's on and your targets are unevenly distributed, turning it off (and relying on even AZ-level target distribution instead) removes that slice cleanly. For ALBs, cross-zone is always on and not separately billed the same way — this lever mainly applies to NLBs.
- Look at whether chatty services can be topology-aware. Kubernetes' built-in topology-aware routing (or an ECS service discovery setup that prefers same-AZ targets) keeps traffic local when a same-AZ replica is available, and only crosses zones as a fallback. This is a real engineering change, not a config flip — worth it only once the number justifies it.
- Accept it for Multi-AZ RDS/Aurora. The replication stream to the standby is the entire point of Multi-AZ — that's not overhead to eliminate, it's what you're paying for. The only lever here is the Multi-AZ-on-non-prod finding this scanner already flags separately, which asks whether you need that resilience at all in a staging environment.
- Measure before architecting. For most accounts, this number will be small enough that redesigning traffic patterns around it isn't worth the engineering time. That's the actual point of surfacing it: turning "we assume this doesn't matter" into "we checked, and it's $3/month" or "we checked, and it's $400/month and climbing" — two very different next steps.
Check it in one command
npx greenops-scan --modules vpc-endpoints
If the number that comes back is small, that's not a wasted scan — it's confirmation that a well-known AWS billing gotcha isn't quietly compounding in your account. If it's bigger than expected, it's the first concrete number you've had to decide whether cross-zone traffic patterns are worth re-architecting around.
Tags: #AWS #CloudCostOptimization #FinOps #DataTransfer #NetworkCosts #GreenOps
