Most attention on data transfer costs goes to internet egress, because it shows up as one clear line item and scales with the most visible kind of traffic. Data transferred between Availability Zones in the same region gets a lot less attention, partly because it's billed per-GB in both directions and partly because nothing about the architecture diagram makes it obvious that a request is crossing an AZ boundary at all.
Redundancy and cost are the same trade-off
Spreading resources across Availability Zones is the correct pattern for resilience — it's exactly what protects a workload from a single AZ failure. The cost of that resilience is every byte that crosses between zones to make it work: an Auto Scaling Group balanced across AZs where instances talk to each other, an RDS Multi-AZ standby receiving replication traffic, a load balancer routing requests to targets in a different zone than the one the request arrived in.
None of that is a mistake. It's the tax for multi-AZ resilience, and for most production workloads it's a tax worth paying. The problem is when it's paid without anyone having decided to.
Where it compounds without anyone noticing
Microservice architectures make this easy to miss because a single user-facing request can fan out into many internal calls, and if the services involved aren't AZ-aware, each of those internal hops has a real chance of crossing a zone boundary. A request path that touches five or six services internally, none of them zone-aware, can rack up several cross-AZ hops per request — multiplying a small per-GB charge by a lot of requests.
One request, several internal hops — each accent arrow crosses a zone boundary and is billed per GB in both directions.
Cross-zone load balancing (on by default for Application Load Balancers) spreads traffic evenly across target AZs regardless of where it originated, which is good for even load distribution and adds cross-AZ transfer as a direct consequence. It's a reasonable default, but worth knowing it's there rather than assuming traffic stays local.
What's actually worth changing
- For tightly-coupled services that call each other constantly, prefer routing and placement that keeps them in the same AZ where the redundancy trade-off allows it — not every service needs independent multi-AZ spread if it's always deployed alongside the services it talks to most.
- Where a service mesh or load balancer supports zone-aware routing (preferring a same-zone target when one is healthy and available), enabling it keeps normal traffic local while still falling back across zones during a failure — the resilience is preserved for the case that actually needs it.
- Treat Multi-AZ database replication and cross-zone load balancing as known, accepted costs of the resilience they provide, not something to eliminate — the goal is making sure the rest of the architecture isn't adding cross-AZ traffic on top of that for no benefit.
Where to look
VPC Flow Logs, filtered by source and destination AZ, will show which resources are generating cross-zone traffic and how much. For anything unexpected, the AZ each flow's source and destination instance actually lives in is usually enough to spot a service that fans out across zones more than its architecture requires.