EKS clusters ship with a default that sends roughly two thirds of pod-to-pod traffic across availability zone boundaries, and AWS charges 0.01 dollars per gigabyte in each direction for that crossing (0.02 dollars per gigabyte round trip). On a busy cluster moving 50 terabytes of inter-pod traffic per month, the cross-AZ portion lands somewhere between 400 and 700 dollars in pure networking fees, before you have added a single feature. The default exists because Kubernetes treats every healthy pod as an equally good destination regardless of where the request originated. Kubernetes 1.35 (released December 2025) fixes this with one new line on each Service: trafficDistribution: PreferSameZone keeps traffic inside the originating zone whenever a healthy pod is available, falling back to other zones only when nothing local is healthy.
This article walks through the math, the mechanism behind the default, the K8s 1.35 fix, how to spot the cost in CUR before you change anything, and the adjacent cross-AZ patterns worth auditing while you have the topic open.
If you have already worked through our AWS Cost Optimization Guide, this article zooms in on the EKS networking layer specifically.
Why Cross-AZ Traffic Is the Default
Kubernetes was designed for a single failure-domain world. The original Service abstraction treats endpoints as a uniform pool: any healthy pod is a valid destination, kube-proxy picks one essentially at random, and that decision predated multi-AZ deployment as a common pattern. When Kubernetes did learn about topology, the feature went through several iterations (topologyKeys, then service.kubernetes.io/topology-aware-hints, then topologyAwareHints, then internalTrafficPolicy Local, and most recently trafficDistribution). Each iteration carried its own caveats around when fallback kicked in, how heuristics evaluated which pods were eligible, and what happened during rolling deployments. The defaults stayed zone-blind because changing them risked breaking workloads in ways that would not show up in tests.
The cost problem follows directly. If your service runs three replicas evenly distributed across three availability zones, a random endpoint pick has a two-out-of-three chance of crossing a zone boundary. That is the 67 percent figure you see quoted in newsletters: it is not measured, it is the geometric inevitability of three-zone replication with no topology hint. The actual figure on your cluster varies with replica counts that are not multiples of the AZ count, with HPA-driven scaling that concentrates pods unevenly, and with cross-namespace traffic patterns. But "majority of traffic crosses zones by default" is true on every multi-AZ cluster we have audited.
The Math, With Real Numbers
AWS data transfer pricing across availability zones in the same region is 0.01 dollars per gigabyte in each direction, billed on both the inbound and outbound endpoint. That means a single byte that leaves zone A bound for zone B incurs 0.02 dollars of round-trip data transfer (charged twice, once on the sender side, once on the receiver side, with the per-direction rate). The pricing is consistent across regions and has been steady through 2026.
What that produces on real clusters:
| Monthly inter-pod traffic | Cross-AZ portion (67 percent default) | Monthly cost at 0.02 per GB round trip |
|---|---|---|
| 5 TB | 3.35 TB | 67 dollars |
| 10 TB | 6.7 TB | 134 dollars |
| 50 TB | 33.5 TB | 670 dollars |
| 100 TB | 67 TB | 1,340 dollars |
| 500 TB | 335 TB | 6,700 dollars |
DataTransfer-Regional-Bytes, separate from EC2 compute, and engineers reading service-level dashboards rarely see it broken down by zone.
Adjacent line items add to the picture. NAT Gateway data processing charges hit at 0.045 dollars per gigabyte on top of the cross-AZ transfer fee. If your egress to the public internet routes through a NAT Gateway in a different zone from the pod, you pay both the cross-AZ transfer and the NAT data processing on every byte. PrivateLink endpoints, RDS read replicas, and Aurora cross-AZ failover traffic compound similarly. Cross-AZ traffic is the gateway pattern. Once you start measuring it, you find it everywhere.
How EKS Service Traffic Actually Works
The default kube-proxy behavior is to pick an endpoint at random from the EndpointSlice for a Service. EndpointSlices contain a topology.kubernetes.io/zone label on each entry, but kube-proxy in iptables mode does not consult it. In IPVS mode, the scheduler can be configured for zone awareness but rarely is. The net result is that a pod in zone us-east-1a making a request to a Service backed by three pods (one in each zone) has a two-out-of-three chance of hitting a pod outside its own zone.
internalTrafficPolicy: Local (stable since K8s 1.26) restricts traffic to pods on the same node, not the same zone. It is too aggressive for most workloads because a node failure or a rolling restart leaves the Service unreachable until kube-proxy reconciles.
topologyAwareHints (now deprecated and renamed) gave kube-proxy a hint about which zones to prefer, but the heuristic was conservative: it only kicked in when each zone had enough endpoints to satisfy estimated traffic, and it disabled itself silently during scaling events or when pods were unevenly distributed. The result was unpredictable savings and frequent fallback to the zone-blind default.
trafficDistribution: PreferSameZone (GA in K8s 1.35) replaces both. The semantics are explicit: route to a pod in the same zone if any are healthy, otherwise fall back to the full cluster. There is no minimum-endpoints heuristic. The behavior is deterministic per request.
trafficDistribution: PreferSameNode (also GA in 1.35) is a stricter variant: prefer pods on the same node, fall back to the same zone, fall back to the full cluster. Useful for sidecar patterns or for workloads where every node runs a copy of the destination Service.
The K8s 1.35 One-Line Fix
The change is one line in the Service spec:
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
trafficDistribution: PreferSameZone
Apply with kubectl apply -f like any other manifest change. The Service reconciles within seconds, kube-proxy picks up the new EndpointSlice hints on the next sync (a few seconds at most), and you can verify with kubectl describe service my-app (look for Traffic Distribution: PreferSameZone in the output).
What happens during failover. If every pod in the originating zone becomes unhealthy or is in the middle of a rolling restart, traffic falls back to pods in other zones automatically. There is no manual intervention required and no Service downtime. The fallback path costs the same regional data transfer rate that you were paying before; you are only paying it during the brief window when local pods are unavailable.
What happens with uneven distribution. If your HPA scales up unevenly (say, five pods in us-east-1a and one each in us-east-1b and us-east-1c during a traffic spike), PreferSameZone routes within each originating zone independently. The 1b and 1c originators all hit their local pod (until it saturates and they queue or fail), at which point fallback kicks in. The behavior is "prefer local until local breaks" rather than "balance globally regardless of zone." That trade-off is right for cost; if you need active load balancing across zones, omit the directive and accept the cross-AZ bill.
Detection in CUR
If you are on CUR 2.0 with FOCUS export (which we recommend for any multi-cloud or unified billing analysis), cross-AZ data transfer shows up as a distinct usage type. The fastest path to a number:
SELECT
product_region,
line_item_usage_type,
SUM(line_item_unblended_cost) AS cost,
SUM(line_item_usage_amount) AS gb_transferred
FROM cur_2_0
WHERE line_item_usage_type LIKE '%DataTransfer-Regional-Bytes%'
AND bill_billing_period_start_date >= DATE_TRUNC('month', CURRENT_DATE)
GROUP BY 1, 2
ORDER BY cost DESC
The DataTransfer-Regional-Bytes line item captures cross-AZ transfer within a single region. The cost column gives you the bill; the gb_transferred column gives you the volume, which is what determines whether the K8s fix is worth applying. A cluster with 50 dollars per month in regional data transfer is not worth a careful Service-by-Service audit; a cluster with 5,000 dollars per month is.
For workload attribution, join against line_item_resource_id to map back to specific ENIs, and from there to specific pods if you are running CloudWatch Container Insights or have ENI-level tagging through the AWS VPC CNI plugin. Without that mapping, you have a cluster-wide number but not a service-level breakdown.
Adjacent Cross-AZ Patterns Worth Auditing While You Are Here
The same audit pattern that catches EKS pod-to-pod cross-AZ also catches several adjacent line items most teams underweight.
Application Load Balancer cross-zone load balancing. ALBs balance across zones by default, which is correct for availability but means every request has the same two-out-of-three chance of crossing a zone boundary on the load-balancer-to-target leg. The fix depends on whether your ALB and targets are in the same AZ; if so, you can disable cross-zone balancing per target group.
NAT Gateway placement. A NAT Gateway in zone us-east-1a serving pods in zones 1b and 1c charges cross-AZ data transfer on every byte that leaves the pod and arrives at the NAT before getting NAT-processed for 0.045 dollars per gigabyte egress. The fix is one NAT Gateway per AZ rather than one per region; each subnet routes through the NAT in its own AZ. NAT Gateways are not free (roughly 32 dollars per month per gateway), but the per-byte savings on cross-AZ traffic usually pay for the extra gateways within the first terabyte.
RDS read replica zones. RDS read replicas in different AZs from the application generate 0.02 dollars per gigabyte on every replication byte and every read query the application sends. Co-locating the read replica with the application primary zone (or running a replica per application zone) eliminates the read-side transfer; replication still pays cross-AZ but the replication volume is usually smaller than query volume.
A 30-Minute First-Pass Audit
If you have never inventoried cross-AZ traffic on your EKS clusters, here is a 30-minute first pass that captures the highest-leverage wins.
- Minutes 0 to 5. Run the CUR query above to get your current monthly cross-AZ data transfer total. If the number is under 100 dollars a month, file the audit for later. If it is over 1,000 dollars a month, this audit will pay for itself before lunch.
- Minutes 5 to 15. Inventory your Services. Run
kubectl get services --all-namespaces -o jsonand filter for Services with replicas spread across multiple zones (this is most of them on a multi-AZ cluster). For each Service receiving meaningful traffic, check whether the destination pods could safely route locally. Most internal microservice traffic can; few-replica Services or workloads with strict latency consistency requirements need more care. - Minutes 15 to 25. Apply
trafficDistribution: PreferSameZoneto the top five to ten Services by traffic volume. Usekubectl describe serviceafter apply to confirm the directive is in effect. Check kube-proxy logs or service mesh telemetry (if you run one) for any errors during the first few minutes of routed traffic. - Minutes 25 to 30. Verify cluster health is unchanged. Re-run the CUR query 24 hours later to confirm the cost line is dropping. On a well-distributed cluster the cost drop is visible within a single billing day.
Where to Go From Here
Cross-AZ data transfer is one of several AWS networking line items that ship invisible by default. NAT Gateway pricing, VPC peering, PrivateLink endpoints, and inter-region data transfer all follow the same pattern: necessary for an availability or architectural reason, billed in a way that does not surface in service dashboards, and avoidable with a configuration change that takes minutes to apply. Our AWS Cost Optimization Guide covers the broader networking-cost surface area.
If you are running multi-cloud, the same audit pattern applies on GKE and AKS. GKE charges 0.01 dollars per gigabyte for cross-zone traffic in the same region (similar to AWS), and trafficDistribution: PreferSameZone works identically in K8s 1.35 across all distributions. AKS pricing on cross-zone transfer is region-specific and worth checking against the Azure Cost Optimization Guide.
For a foundation on how to read multi-cloud networking costs in unified billing data, see What Is FOCUS? and Multi-Cloud Cost Management Guide.
Tired of explaining last month's mystery DataTransfer-Regional-Bytes line? Brain Agents AI ingests your FOCUS-normalized AWS, GCP, and Azure billing data and surfaces cross-AZ data transfer as part of its standard cost analysis. Cost Scout flags anomalies when regional data transfer spikes above your baseline (catching the noisy-deployment or runaway-microservice cases that are easy to miss in monthly reviews). Savings Advisor identifies workloads where the cost-to-volume ratio suggests a trafficDistribution opportunity worth surfacing in your weekly briefing. The K8s manifest change is yours to apply; we make the cost pattern visible in the bill, attribute it to the right workload, and track the recovered spend month over month across all three clouds.
