AWS network costs
Data transfer is the line item most Kubernetes teams underestimate, because nothing in a Kubernetes dashboard shows it. The scheduler treats bandwidth as free; AWS does not.
The rates
These are the list prices KubeSpend uses. They come from a static table of us-east-1 rates, applied to every region — see the accuracy caveat below.
| Charge | Rate | When it applies |
|---|---|---|
| Cross-AZ (intra-region) | $0.01 / GB per direction | traffic between AZs in the same region |
| NAT gateway processing | $0.045 / GB | anything traversing a NAT gateway, on top of transfer |
| Inter-region | $0.02 / GB | between AWS regions |
| Internet egress | tiered: $0.09 → $0.085 → $0.07 → $0.05 / GB | out to the internet |
Internet egress tiers by monthly volume: first 10 TB at $0.09, next 40 TB at $0.085, next 100 TB at $0.07, beyond 150 TB at $0.05.
Load balancer rates, for reference: ALB $0.0225/hr + $0.008/LCU, NLB $0.0225/hr + $0.006/NLCU, CLB $0.025/hr + $0.008/GB processed.
Why cross-AZ is the expensive one
$0.01/GB sounds trivial. Two things make it add up.
It is charged per direction. A request crossing an AZ boundary and its response coming back are two separate charges. Effectively $0.02/GB for a round trip.
Kubernetes crosses AZ boundaries constantly and invisibly. A default ClusterIP Service load-balances across all healthy endpoints with no topology awareness. Spread a Deployment across three AZs for availability and roughly two thirds of your service-to-service traffic leaves its AZ — by design, and nothing tells you.
A chatty microservice mesh moving 10 TB a month between services, two thirds of it cross-AZ, is about $130/month in charges that appear on no Kubernetes dashboard. Databases, caches and log shippers are usually the worst offenders because they carry high volume and are often deliberately placed in one AZ.
Why NAT is the other one
Pods in private subnets reach the internet through a NAT gateway, and $0.045/GB is on top of the egress charge. Pulling container images, calling external APIs, shipping telemetry to a SaaS vendor — all of it pays both.
Traffic to S3 or DynamoDB in the same region does not need to traverse NAT if a VPC gateway endpoint exists. Without one, every S3 read pays NAT processing. Discovering that a cluster lacks a gateway endpoint is one of the highest-value findings network visibility produces.
Why this is hard to see
| Source | Why it falls short |
|---|---|
| Cost and Usage Report | tells you the region spent $X on transfer; not which pod, service or namespace |
| VPC Flow Logs | has the data, but is per-ENI with no pod identity, and at high volume the logs themselves cost real money |
| CloudWatch metrics | per-instance aggregates, no workload attribution |
| Application metrics | only what each app chose to instrument, in inconsistent units |
The gap is always the same: AWS knows the bytes but not the workload; Kubernetes knows the workload but not the bytes. Joining them is what eBPF is for.
What you can do about it
Topology-aware routing. Set spec.trafficDistribution: PreferClose on a Service (or service.kubernetes.io/topology-mode: Auto on older versions) and kube-proxy prefers endpoints in the local zone. Often the single highest-leverage change available.
Co-locate chatty pairs. Pod affinity on topology.kubernetes.io/zone for services that talk constantly. Trades availability for cost — a deliberate decision, not a default.
VPC endpoints. Gateway endpoints for S3 and DynamoDB are free and remove NAT processing entirely for that traffic. Interface endpoints for other services cost per hour but usually beat NAT at volume.
Single-AZ for tolerant workloads. Batch jobs, CI runners and dev environments rarely need multi-AZ spread.
Compression and batching. Halving the bytes halves every per-GB charge at once.
What KubeSpend does today
Priced today. Flows are classified by the zone at each end, and two of the buckets above are priced per day and folded into the cluster's cost total:
| Bucket | How it is priced |
|---|---|
| Cross-AZ (intra-region) | flat per-GB rate, already counted per direction by the flow query |
| Internet egress | tiered rates, egress direction only |
| Same-AZ | free, contributes nothing |
A network figure is a floor, not the transfer bill
Three things are missing from it, and each one only ever makes the number too low.
Unclassified bytes are not priced. Where one end of a flow has no resolvable zone — internet peers, the EKS control plane, the API ClusterIP — the bytes are neither charged nor called free, because assuming same-AZ would invent a $0. Coverage reports networkPricedBytes, networkFreeBytes and networkUnclassifiedBytes; check the unclassified share before quoting a total.
NAT gateway and inter-region are not modelled. The rates exist in the table above, but nothing produces those buckets, so that spend is absent entirely.
Network dollars are cluster-level and daily. There is no per-workload network cost — per workload you get bytes split by zone.
networkUsd is null, not 0, when there are no flow records or when no region could be resolved for the cluster's nodes — so "no eBPF agent" never looks like "no network spend".
The rate table is also reachable through a standalone transfer-cost calculator where you supply a GB figure yourself, which is how you model the NAT and inter-region buckets the flow pipeline does not produce.
Accuracy caveat
Rates are us-east-1 list prices applied to all supported regions. Cross-AZ ($0.01/GB per direction) and NAT ($0.045/GB) are uniform across commercial regions, so those are accurate. Internet egress genuinely varies by region and is the least accurate entry in the table. Responses report the source as a static table so the console can label them approximate rather than implying a live fetch.