Transit Gateway and VPC Peering — Multi-Account Networking on AWS
You start with one VPC. Then you add a staging VPC. Then a shared-services VPC. Then three more teams each get their own accounts. Suddenly you have 8 VPCs that all need to talk to each other, and you've created a mesh of 28 VPC peering connections that nobody can draw on a whiteboard anymore. This is where Transit Gateway enters the picture.
VPC Peering — Simple but Limited
VPC Peering creates a direct network route between two VPCs. It's free (no hourly charge, you only pay for data transfer), it works cross-account and cross-region, and it's simple to set up:
# Create a peering connection from VPC-A to VPC-B
PEERING_ID=$(aws ec2 create-vpc-peering-connection \
--vpc-id vpc-aaa111 \
--peer-vpc-id vpc-bbb222 \
--peer-owner-id 987654321098 \
--peer-region us-west-2 \
--query 'VpcPeeringConnection.VpcPeeringConnectionId' --output text)
# Accept the peering in the other account/region
aws ec2 accept-vpc-peering-connection \
--vpc-peering-connection-id $PEERING_ID
# Add routes in BOTH VPCs (this is manual and easy to forget)
aws ec2 create-route --route-table-id rtb-aaa111 \
--destination-cidr-block 10.1.0.0/16 \
--vpc-peering-connection-id $PEERING_ID
aws ec2 create-route --route-table-id rtb-bbb222 \
--destination-cidr-block 10.0.0.0/16 \
--vpc-peering-connection-id $PEERING_ID
The limitations hit fast:
- No transitive routing. If VPC-A peers with VPC-B, and VPC-B peers with VPC-C, then VPC-A cannot reach VPC-C through VPC-B. You need a direct peering between A and C.
- Full mesh scaling problem. For N VPCs you need N(N-1)/2 peering connections. 5 VPCs = 10 connections. 10 VPCs = 45 connections. 20 VPCs = 190 connections.
- No centralized routing. Each VPC manages its own route tables independently.
| VPC Count | Peering Connections Needed | Manageable? |
|---|---|---|
| 3 | 3 | Yes |
| 5 | 10 | Probably |
| 10 | 45 | Painful |
| 20 | 190 | No |
| 50 | 1,225 | Absolutely not |
Transit Gateway — The Hub-and-Spoke Solution
Transit Gateway (TGW) acts as a regional network hub. Every VPC attaches to the TGW, and the TGW handles routing between them. Instead of N(N-1)/2 connections, you have N attachments:
# Create a Transit Gateway
TGW_ID=$(aws ec2 create-transit-gateway \
--description "Central network hub" \
--options '{
"AmazonSideAsn": 64512,
"AutoAcceptSharedAttachments": "disable",
"DefaultRouteTableAssociation": "enable",
"DefaultRouteTablePropagation": "enable",
"DnsSupport": "enable",
"VpnEcmpSupport": "enable"
}' \
--query 'TransitGateway.TransitGatewayId' --output text)
# Attach VPC-A
aws ec2 create-transit-gateway-vpc-attachment \
--transit-gateway-id $TGW_ID \
--vpc-id vpc-aaa111 \
--subnet-ids subnet-a1 subnet-a2
# Attach VPC-B
aws ec2 create-transit-gateway-vpc-attachment \
--transit-gateway-id $TGW_ID \
--vpc-id vpc-bbb222 \
--subnet-ids subnet-b1 subnet-b2
# Attach VPC-C (in a different account via RAM — covered below)
aws ec2 create-transit-gateway-vpc-attachment \
--transit-gateway-id $TGW_ID \
--vpc-id vpc-ccc333 \
--subnet-ids subnet-c1 subnet-c2
Now VPC-A can reach VPC-B and VPC-C through the Transit Gateway, and VPC-B can reach VPC-C. No full mesh required.
TGW Route Tables — Segmented Routing
The real power of Transit Gateway is route table segmentation. You can create separate route tables to control which VPCs can talk to each other:
# Create a route table for production VPCs
PROD_RT=$(aws ec2 create-transit-gateway-route-table \
--transit-gateway-id $TGW_ID \
--query 'TransitGatewayRouteTable.TransitGatewayRouteTableId' --output text)
# Create a route table for development VPCs
DEV_RT=$(aws ec2 create-transit-gateway-route-table \
--transit-gateway-id $TGW_ID \
--query 'TransitGatewayRouteTable.TransitGatewayRouteTableId' --output text)
# Associate prod VPC with prod route table
aws ec2 associate-transit-gateway-route-table \
--transit-gateway-route-table-id $PROD_RT \
--transit-gateway-attachment-id tgw-attach-prod-vpc
# Propagate shared-services routes to BOTH route tables
# (so prod and dev can both reach shared services)
aws ec2 enable-transit-gateway-route-table-propagation \
--transit-gateway-route-table-id $PROD_RT \
--transit-gateway-attachment-id tgw-attach-shared-vpc
aws ec2 enable-transit-gateway-route-table-propagation \
--transit-gateway-route-table-id $DEV_RT \
--transit-gateway-attachment-id tgw-attach-shared-vpc
This means production VPCs can reach shared services but not development VPCs, and vice versa. Classic network segmentation without firewalls.
Multi-Account Connectivity With RAM
In an AWS Organizations setup, you share the Transit Gateway across accounts using Resource Access Manager (RAM):
# Share TGW with your entire organization
aws ram create-resource-share \
--name "shared-transit-gateway" \
--resource-arns "arn:aws:ec2:us-east-1:111111111111:transit-gateway/$TGW_ID" \
--principals "arn:aws:organizations::111111111111:organization/o-abc123" \
--allow-external-principals false
# In the other account, they can now attach their VPC
aws ec2 create-transit-gateway-vpc-attachment \
--transit-gateway-id $TGW_ID \
--vpc-id vpc-other-account \
--subnet-ids subnet-x1 subnet-x2
RAM can also share subnets, Route 53 resolver rules, License Manager configurations, and more across accounts.
VPN and Direct Connect Attachments
Transit Gateway can also terminate VPN connections and Direct Connect gateways, making it the single entry point for on-premises connectivity:
# Create a VPN attachment to TGW (instead of attaching VPN to a single VPC)
CUSTOMER_GW=$(aws ec2 create-customer-gateway \
--type ipsec.1 \
--public-ip 203.0.113.1 \
--bgp-asn 65000 \
--query 'CustomerGateway.CustomerGatewayId' --output text)
aws ec2 create-vpn-connection \
--type ipsec.1 \
--customer-gateway-id $CUSTOMER_GW \
--transit-gateway-id $TGW_ID \
--options '{"StaticRoutesOnly": false}'
Direct Connect provides a dedicated physical connection from your data center to AWS. It offers lower latency, consistent bandwidth (1 Gbps or 10 Gbps), and reduced data transfer costs compared to VPN over the internet. You associate a Direct Connect Gateway with your Transit Gateway to give on-premises access to all attached VPCs.
PrivateLink — Service Sharing Without Network Merging
PrivateLink is fundamentally different from peering or TGW. Instead of connecting entire networks, it exposes a single service (behind an NLB) as a private endpoint in another VPC:
# Service provider: Create a VPC Endpoint Service
aws ec2 create-vpc-endpoint-service-configuration \
--network-load-balancer-arns arn:aws:elasticloadbalancing:us-east-1:111111111111:loadbalancer/net/my-service-nlb/abc123 \
--acceptance-required
# Service consumer: Create an interface endpoint to consume the service
aws ec2 create-vpc-endpoint \
--vpc-id vpc-consumer \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.vpce.us-east-1.vpce-svc-abc123 \
--subnet-ids subnet-consumer-a \
--security-group-ids sg-endpoint
PrivateLink is ideal when you want to share a specific API or service without exposing your entire network. The consumer only sees an ENI in their VPC — they can't route to anything else in your network.
Network Architecture Patterns Comparison
| Pattern | Use Case | Scalability | Cost | Complexity |
|---|---|---|---|---|
| VPC Peering | 2-3 VPCs, simple connectivity | Low (full mesh) | Low (data transfer only) | Low |
| Transit Gateway | 4+ VPCs, hub-and-spoke | High (5,000 attachments) | Medium ($0.05/hr per attachment + $0.02/GB) | Medium |
| PrivateLink | Service-to-service, no network merge | High | Low-Medium ($0.01/hr + $0.01/GB) | Low |
| TGW + Direct Connect | Hybrid cloud | High | High (DX port + TGW + data) | High |
| TGW Inter-Region Peering | Multi-region | High | High (TGW + inter-region data) | High |
| CloudWAN | Global network management | Very High | High | Very High |
Cost Implications
Transit Gateway is not free, and the costs can add up:
- TGW attachment: $0.05/hr per attachment ($36/month per VPC)
- Data processing: $0.02/GB through the TGW
- VPC Peering: $0.01/GB (same region), $0.02/GB (cross-region), no hourly fee
For two VPCs exchanging 500 GB/month in the same region:
- VPC Peering cost: 500 GB x $0.01 = $5/month
- TGW cost: (2 x $36) + (500 GB x $0.02) = $82/month
So if you only have 2-3 VPCs, peering is drastically cheaper. Transit Gateway pays for itself when you have 5+ VPCs and need centralized routing, segmentation, or on-premises connectivity.
What's Next
Networking determines how your services communicate, but how do those services authenticate? In the next post, we'll compare Secrets Manager, Parameter Store, and HashiCorp Vault — three approaches to managing the credentials your applications need to function securely.
