Introduction
As your AWS footprint grows, you quickly end up with multiple VPCs: dev, staging, prod, shared services, and more. Sooner or later they need to talk to each other privately.
AWS gives you two main ways to do this: VPC Peering and Transit Gateway (TGW). They solve a similar problem but in very different ways. Let's break them down.
Quick Comparison
| VPC Peering | Transit Gateway | |
|---|---|---|
| Definition | Direct 1-to-1 private connection between two VPCs | Central hub connecting many VPCs, VPNs and on-prem networks |
| Topology | Point-to-point (mesh) | Hub-and-spoke |
| Transitive routing | No | Yes |
| Cost | No hourly fee, only data transfer | Per-attachment hourly fee + per-GB data processing |
| Best for | Small, simple setups | Large, multi-account, hybrid setups |
What is VPC Peering?
VPC Peering is a networking connection between two VPCs that lets you route traffic between them using private IPv4/IPv6 addresses. Traffic stays on the AWS backbone and never touches the public internet.
How it works
- VPC A sends a peering request to VPC B (same or different account/region).
- VPC B accepts the request.
- You add routes in both VPCs' route tables pointing to the peer's CIDR.
- Security groups and NACLs must allow the traffic.
The big limitation: no transitive routing
If A is peered with B, and B is peered with C, A cannot reach C through B. You would need a separate A-C peering.
For n VPCs, a full mesh needs n(n-1)/2 connections:
| VPCs | Peering connections |
|---|---|
| 3 | 3 |
| 5 | 10 |
| 10 | 45 |
| 20 | 190 |
This gets painful very quickly.
Terraform example
resource "aws_vpc_peering_connection" "a_to_b" {
vpc_id = aws_vpc.a.id
peer_vpc_id = aws_vpc.b.id
auto_accept = true # same account & region
tags = {
Name = "vpc-a-to-vpc-b"
}
}
# Route from A to B
resource "aws_route" "a_to_b" {
route_table_id = aws_route_table.a.id
destination_cidr_block = aws_vpc.b.cidr_block
vpc_peering_connection_id = aws_vpc_peering_connection.a_to_b.id
}
# Route from B to A
resource "aws_route" "b_to_a" {
route_table_id = aws_route_table.b.id
destination_cidr_block = aws_vpc.a.cidr_block
vpc_peering_connection_id = aws_vpc_peering_connection.a_to_b.id
}
What is Transit Gateway?
Transit Gateway is a managed, regional network hub. Instead of connecting VPCs to each other, you connect each one to the TGW. The TGW then routes traffic between all attachments.
How it works
- Create a Transit Gateway.
- Create an attachment for each VPC (and VPN / Direct Connect if needed).
- TGW route tables decide which attachment can reach which.
- Add a route in each VPC's route table pointing the other CIDRs to the TGW.
Because routing is centralized, you get transitive routing and can segment traffic (for example, prod can't talk to dev) using multiple TGW route tables.
Terraform example
resource "aws_ec2_transit_gateway" "main" {
description = "Central hub"
default_route_table_association = "enable"
default_route_table_propagation = "enable"
tags = {
Name = "main-tgw"
}
}
resource "aws_ec2_transit_gateway_vpc_attachment" "vpc_a" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
vpc_id = aws_vpc.a.id
subnet_ids = aws_subnet.a_private[*].id
}
resource "aws_ec2_transit_gateway_vpc_attachment" "vpc_b" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
vpc_id = aws_vpc.b.id
subnet_ids = aws_subnet.b_private[*].id
}
# VPC A -> everything in 10.0.0.0/8 via TGW
resource "aws_route" "a_via_tgw" {
route_table_id = aws_route_table.a.id
destination_cidr_block = "10.0.0.0/8"
transit_gateway_id = aws_ec2_transit_gateway.main.id
}
Cost Comparison
| VPC Peering | Transit Gateway | |
|---|---|---|
| Hourly charge | None | Per attachment, per hour |
| Data charge | Data transfer only (same-AZ traffic is free; cross-AZ and cross-region are charged) | Per GB processed by the TGW |
| Scales with | Data volume | Number of attachments + data volume |
Note: Exact prices vary by region and change over time. Always check the official AWS pricing page before making a decision.
Rule of thumb: Peering is cheaper. TGW costs more, but it saves a lot of operational effort as you scale.
Which Environment Fits Which?
Choose VPC Peering when:
- You have only a few VPCs (roughly 2 to 5).
- You need the lowest cost and lowest latency.
- The architecture is simple and does not need transitive routing.
Choose Transit Gateway when:
- You have many VPCs or multiple AWS accounts.
- You need hybrid connectivity (Site-to-Site VPN or Direct Connect).
- You want centralized routing, segmentation and easier operations.
- You plan to add inspection layers (for example a central firewall VPC).
Gotchas to Remember
- Overlapping CIDRs don't work in either option. Plan your IP ranges early.
- VPC Peering does not support edge-to-edge routing (you can't use the peer's VPN or internet gateway).
- TGW is a regional resource. Use TGW peering for cross-region connectivity.
- Remember to update route tables and security groups on both sides. This is the most common cause of "it's not connecting".
Conclusion
| Scenario | Pick |
|---|---|
| Small setup, tight budget | VPC Peering |
| Growing multi-VPC / multi-account | Transit Gateway |
| Hybrid cloud with on-prem | Transit Gateway |
Start simple with peering, and move to Transit Gateway when managing the mesh starts hurting. Which one are you using in your project? Let me know in the comments! 👇
Top comments (0)