awsdata-transfernetworkingcost-optimization

AWS data transfer costs explained

The most opaque part of the AWS bill, broken down. Internet egress, cross-region, cross-AZ, NAT processing, CloudFront, VPC endpoints. The five biggest cost surprises and how to fix them.

The C3X Team··11 min read

Quick answer

AWS data transfer pricing depends on direction and destination. Inbound from internet: free. Outbound to internet: $0.09/GB (first 10 TB/month). Cross-region: $0.02/GB. Cross-AZ within region: $0.01/GB each direction. Same-AZ: free. The biggest cost surprises usually come from NAT Gateway data processing ($0.045/GB on top of egress), inter-AZ replication, and unintentional cross-region traffic.

Data transfer is the most opaque part of the AWS bill. Engineers provision resources thinking about compute and storage but rarely about which AZ or region traffic flows between. By the time Cost Explorer surfaces the data transfer line item, the architecture is already in place and changing it is expensive.

This post breaks down every AWS data transfer dimension with the actual rates, where they apply, and the design patterns that avoid the worst surprises.

The pricing matrix

Approximate rates for traffic in major US regions. Other regions have similar structure with different absolute numbers.

Inbound (to AWS)

From internet to any AWS service: free.

From on-premises via Direct Connect or VPN: free at the AWS end (Direct Connect port hours and data transfer-in are different line items; data-in is free).

From another AWS region to your VPC: billed as cross-region egress on the source region. So it's "in" from your perspective but charged on the source.

Outbound (from AWS)

To the public internet: $0.09/GB for the first 10 TB/month, $0.085/GB for the next 40 TB, $0.07/GB for the next 100 TB, $0.05/GB above. First 100 GB/month is free at the account level.

Cross-region (US to US): $0.02/GB.

Cross-region (US to Europe or Asia): $0.02-$0.09/GB depending on destination.

Cross-AZ within region: $0.01/GB each direction. So a round-trip cross-AZ flow pays $0.02/GB total.

Same-AZ: free between EC2 instances or to/from AWS services.

Service-specific data fees

Several services have their own data fees on top of (or replacing) standard data transfer:

  • NAT Gateway: $0.045/GB processed (in addition to internet egress)
  • Transit Gateway: $0.02/GB processed
  • VPC Endpoint (Interface): $0.01/GB processed
  • CloudFront to viewers: $0.085/GB tier 1 (cheaper than standard egress per byte)
  • ALB and NLB to internet: Standard internet egress applies
  • S3 to S3 cross-region replication: $0.02/GB

The five highest-impact cost surprises

1. NAT Gateway data processing

The biggest single surprise in production AWS bills. NAT charges $0.045/GB processed in addition to internet egress fees. A microservices workload pulling 5 TB/month through NAT pays $225 in NAT processing alone, before the $0.09/GB internet egress on top.

See our NAT Gateway alternatives post for the full breakdown. VPC Gateway Endpoints for S3 and DynamoDB (free) typically eliminate 50%+ of NAT costs.

2. Cross-AZ replication for databases

Multi-AZ databases (RDS Multi-AZ, Aurora, Postgres with replicas) replicate synchronously between AZs. The replication traffic is cross-AZ, billed at $0.01/GB each direction.

For a write-heavy database doing 10 GB/day of writes, that's 10 GB × 2 directions × 30 days × $0.01/GB = $6/month. Not catastrophic, but multiply by every Multi-AZ database in the account. For RDS Multi-AZ, replication traffic is included in the Multi-AZ premium and not separately billed; for self-managed databases on EC2, it is billed.

3. Cross-AZ traffic in Kubernetes

Kubernetes schedules pods across AZs by default for HA. If a pod in AZ A calls a service whose backend is in AZ B, that crosses an AZ. Each call pays $0.01/GB in each direction.

At microservice scale (millions of service-to-service calls per day), cross-AZ traffic adds up. Mitigation: topology-aware service routing (zone-balanced load balancing, topology spread constraints with preferred ZoneSafetyDegradation).

4. S3 to EC2 cross-region

Reading from an S3 bucket in us-east-1 to EC2 in us-west-2 costs $0.02/GB. For a daily data pipeline pulling 100 GB from a central S3 to compute in another region, that's $60/month per pipeline.

Mitigation: keep S3 buckets in the same region as the compute that reads them. Or use S3 Cross-Region Replication (one-time cost to replicate, then in-region reads).

5. Internet egress from EC2 with public IPs

EC2 instances responding to public traffic pay $0.09/GB. For a public-facing service serving 10 TB/month, that's $900/month in egress alone.

Mitigation: front the service with CloudFront. Even with no caching, CloudFront's egress rate is similar to EC2's, and cache hits eliminate the EC2 egress entirely.

The CloudFront calculation

CloudFront is often presented as a content delivery network, but for data transfer cost optimization it's also a hedge against S3/EC2 internet egress.

Compare two scenarios for serving 100 GB/day of static assets:

Direct from S3

  • S3 to internet: 3,000 GB × $0.09/GB = $270/month
  • S3 GET request fees: ~3M requests × $0.0004 = $1.20
  • Total: ~$271/month

Via CloudFront with 95% cache hit rate

  • CloudFront to viewers: 3,000 GB × $0.085/GB = $255
  • S3 GET requests (cache misses only): 150K × $0.0004 = $0.06
  • CloudFront request fees: 30M × $0.0075/10K = $22.50
  • Total: ~$277/month

The cost is roughly the same at this volume; the latency and availability are much better with CloudFront. At higher volumes where CloudFront's tiered pricing kicks in (above 50 TB/month global), CloudFront becomes cheaper than direct S3 egress.

For details, see the aws_cloudfront_distribution catalog page.

VPC endpoints: the free win

S3 and DynamoDB Gateway Endpoints are completely free and eliminate NAT processing fees for traffic to those services. For any VPC with private subnets making S3 or DynamoDB calls (which is most production VPCs), adding the endpoints is a strict cost win.

resource "aws_vpc_endpoint" "s3" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.us-east-1.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = aws_route_table.private[*].id
}

Ten lines of Terraform, immediate cost savings. See the aws_vpc_endpoint catalog page for the full pattern including Interface endpoints for other AWS services.

How to measure your current data transfer cost

Three ways to triage the current bill:

Cost Explorer

Set the date range to last 30 days. Filter by Usage Type containing "DataTransfer". Group by API Operation to see which AWS service is generating the most transfer.

Common patterns: high "DataTransfer-Out-Bytes" usually means internet egress. High "RegionXX-DataTransfer" means cross-region. High "DataTransfer-Regional-Bytes" means cross-AZ.

VPC Flow Logs

Enable VPC Flow Logs on private subnets. Query in CloudWatch Insights or Athena to find which IPs (and via reverse DNS, which services) consume the most bytes.

Common findings: container image pulls from external registries, npm/PyPI/RubyGems traffic, external API calls (Stripe, Twilio, third-party APIs).

C3X recommend

c3x recommend --path . on your Terraform flags NAT-heavy patterns, missing VPC endpoints, and other data transfer optimization opportunities.

FAQ

Why is AWS data transfer so confusing?

Because the rates depend on where bytes go AND who's sending them. Internet egress is $0.09/GB. Cross-region is $0.02/GB. Cross-AZ within a region is $0.01/GB. And each service has its own variations (S3, CloudFront, EC2, NAT all bill differently). On top of that, some transfers are free in one direction but billed in the other.

What's the cheapest way to serve traffic to the public internet?

CloudFront. Egress from CloudFront edge to viewers is roughly the same per-GB as from S3 to internet, but CloudFront's cache hits eliminate origin requests entirely. For repeat-read traffic, a 95% cache hit rate cuts S3 egress to 5% of what it would be without CloudFront.

Is data transfer in always free?

Inbound to AWS from the internet is free for almost every service. Inbound to AWS regions from on-premises via Direct Connect or VPN is also free. The big exception: pulling data from another AWS region to your VPC isn't free — that's cross-region egress charged on the source.

Are cross-AZ transfers really billed?

Yes, $0.01/GB each direction within a region. A 3-AZ Postgres cluster replicating between primary and standby pays cross-AZ data transfer. Container clusters spreading pods across AZs pay it. The fee is small per GB but real at scale.

How can I tell what's driving my data transfer bill?

Three views in AWS Cost Explorer. Filter by usage type containing 'DataTransfer' and group by 'API operation' to see which AWS service is the source. Group by 'Region' to see if it's cross-region. Group by 'Resource' (with tags enabled) to find specific high-traffic resources. VPC Flow Logs give per-flow detail if you need it.

Does C3X estimate data transfer?

Yes, but only with usage data. Most data transfer dimensions are usage-based, so c3x needs expected monthly volumes specified in c3x-usage.yml. Without usage data, c3x reports the per-GB rates and flags transfer as usage-dependent.

The optimization checklist

In order of impact for most production AWS workloads:

  1. Add S3 and DynamoDB VPC Gateway Endpoints. Free.
  2. Add Interface Endpoints for ECR, Secrets Manager, KMS, CloudWatch Logs. Roughly $7/month per AZ per endpoint, easily pays back.
  3. Put CloudFront in front of public-facing S3 buckets and ALBs. Cache where possible.
  4. Audit cross-region traffic. Move data closer to compute.
  5. Use topology-aware routing for service-to-service calls in Kubernetes to minimize cross-AZ.
  6. Switch to IPv6 where supported (free egress on IPv6).

For the deeper dive on NAT specifically (often the single biggest line item), see AWS NAT Gateway cost alternatives. For the broader cost estimation workflow, see how to estimate AWS costs from Terraform.

Try C3X on your own Terraform

Free and open source. No API key required. One command to install, one command to estimate.