Exam Room · Advanced Solutions Architect

VPC Peering or Transit Gateway

· 33 min read

Advanced Cloud Architecture · part of The Exam Room

The situation

A platform engineering team operates 52 VPCs across three Regions (us-east-1, eu-west-1, ap-southeast-2), spread over 18 accounts in one AWS Organization. Each VPC houses a separate workload, production, staging and dev for several product lines, plus shared services for auth, observability, secrets, and internal tooling. Product teams own their own accounts and want to keep owning them.

They need any-to-any connectivity between VPCs in the same Region and most cross-Region paths reachable. They need a single egress point per Region so all outbound traffic leaves through one inspection hop. They want east-west inter-VPC traffic to flow through a third-party firewall fleet. Adding a 53rd VPC should not mean touching route tables in 52 other VPCs, and it should not mean giving a product team the ability to route into production. They also need a cost shape that scales with traffic, not with the count of pairwise connections.

Today everything is stitched together with VPC peering. The networking engineer who has been holding the duct tape has asked whether there is a better shape. It is the same choice a 30-VPC estate faces, with twenty-two more VPCs and a third Region on top.

What actually matters

Transitivity comes first. VPC peering does not have it: AWS states plainly that if there are peerings between A and B, and between A and C, you cannot route from B to C through A. Every pair needs its own connection and its own route table entries on both sides, so edges grow as N × (N-1) / 2. The operational work grows faster than that, because adding the 53rd VPC means 52 new connections and route entries in 52 existing VPCs, each one a chance to typo a CIDR.

Where control lives matters as much as reachability, and at 18 accounts it is a permissions problem rather than a routing one. Pairwise connections put every routing decision in each spoke’s own route tables, in each spoke’s own account. A hub concentrates the decisions, which makes segmentation auditable in one place and makes the hub the blast radius for a bad change. What separates the candidates is whether the hub’s control plane can be split from its data plane across account boundaries: whether a product team can attach its own VPC without also being able to route into someone else’s.

The data path is next. Reaching each other is the easy half. Reaching each other via a specific inspection point is what security review cares about, and a hub gives an obvious place to put a firewall fleet between segments. Peering offers no such place, because packets travel peer-to-peer over the AWS backbone with nowhere to land a Gateway Load Balancer in the middle. Peering also blocks the egress design outright: AWS lists edge-to-edge routing as unsupported, so resources in VPC B cannot use a NAT device, internet gateway, VPN or gateway endpoint that lives in VPC A.

Peering charges nothing for a connection, and charges data transfer at USD$0.01/GB in each direction only when it crosses an Availability Zone, nothing at all within one. A hub charges per attachment-hour plus per-GB processed on top of that. What decides the bill at 52 VPCs is whether the fixed part grows with pairs or with VPCs, and whether the Region count has a ceiling: a Regional hub’s own cross-Region mechanism can turn into a second N × (N-1) / 2 problem one layer up.

What we’ll filter on

  1. Transitive any-to-any reachability without O(N²) configuration.
  2. A single egress point per Region all VPC traffic funnels through.
  3. A seam in the data path where a stateful security appliance can sit.
  4. Cross-account attachment without cross-account routing authority.
  5. Cross-Region connectivity that doesn’t explode into another mesh.
  6. Fixed cost scaling with VPCs, not with pairwise connections.

The landscape

VPC Peering. A point-to-point connection between exactly two VPCs, in the same Region or across Regions, in the same account or across accounts. Non-transitive, and no edge-to-edge routing through a gateway in the peer VPC. To connect N VPCs in a mesh you need N × (N-1) / 2 peering connections, and a route table entry in every VPC for every other VPC’s CIDR. For N = 52 that is 1,326 peering connections and at least 2,652 route table entries to maintain. Nothing to pay for the connection; USD$0.01/GB each way across an Availability Zone, free within one, inter-Region rates across Regions. The right primitive for two or three VPCs. Wrong for 52.

AWS Transit Gateway (TGW). A Regional virtual router. Each VPC gets one attachment, and the TGW’s route tables decide which attachments can route to which. Routing is transitive: A to TGW to C works. For N VPCs in one Region that is N attachments and one TGW. Peering attachments connect TGWs to each other, intra-Region or inter-Region. Centralised egress is a dedicated VPC with a NAT gateway attached to the TGW; centralised inspection is a GWLB-fronted appliance fleet in an inspection VPC. AWS RAM shares the TGW itself across the Organization. US East list prices are USD$0.05 per attachment-hour and USD$0.02 per GB processed.

AWS PrivateLink. Application-level connectivity to one service fronted by a Network Load Balancer or a Gateway Load Balancer; those two types only, and an endpoint service can now be reached from other Regions through cross-Region access. It exposes service X from VPC A to consumers in VPC B and C rather than providing a mesh, so it does not satisfy the general layer-3 reachability here. Worth knowing for the adjacent problem where one team publishes and others consume.

AWS Cloud WAN. A managed global network whose Regional connection points are core network edges. AWS documents a core network edge as a transit gateway that inherits many of the same properties, and every CNE in a core network full-mesh peers with every other one automatically. A declarative JSON core network policy defines Regions, segments as isolated routing domains, and how attachments map to segments, including by tag. Network function groups steer intra-Region and inter-Region traffic through firewall attachments with no static routes to maintain. RAM shares a core network, splitting a core network owner from attachment owners.

Evaluation

Side by side

Option Transitive Central egress Inspection seam Cross-account split Cross-Region Fixed cost scales with VPCs
VPC Peering ✗ ✗ ✗ ✗ ✓ (pairwise) ✗
Transit Gateway ✓ ✓ ✓ ✓ ✓ (peering mesh) ✓
PrivateLink ✗ ✗ ✗ ✓ ✓ (per service) ✓
Cloud WAN ✓ ✓ ✓ ✓ ✓ (auto full mesh) ✓

Cloud WAN ticks every box, and at three Regions it costs more to run for capability the team cannot use yet. A core network edge is USD$0.50 an hour in every Region, so three of them come to roughly USD$1,100 a month before a single VPC attaches. Cloud WAN attachments are USD$0.065 an hour against the TGW’s USD$0.05. Across the 58 attachments this topology needs, that puts the fixed monthly charge about USD$1,500 above the equivalent TGW build, for a policy engine whose payoff comes from Region count and segment count, neither of which is high here. Data processing is USD$0.02/GB either way. Transit Gateway lands.

Matching the shape to the primitive

Small cluster two or three VPCs, one Region Regional hub dozens of VPCs, a few Regions Global network hundreds of VPCs, many Regions N = 3, one account 3 pairs, 6 route entries no segmentation needs no central inspection N = 52, 3 Regions, 18 accounts 1,326 pairs if meshed prod / non-prod / shared east-west inspection required N = 500+, 8+ Regions static routes multiply per Region segments as intent SD-WAN on-prem integration Pairwise count still tractable? yes Transitivity and inspection? yes Imperative route tables scale? no Central control needed? no Global policy engine needed? no Segments as declarative policy? yes VPC Peering no charge per connection non-transitive simple, CIDR-coordinated Transit Gateway one attachment per VPC RAM-shared, owner keeps routing egress + inspection VPCs Cloud WAN policy as JSON segments and network function groups CNEs full-mesh automatically
Three shapes, three primitives. The 52-VPC, 3-Region, 18-account shape lives firmly in the middle column.

The solution

Attachments and route tables. Each VPC connects to the TGW via an attachment, which places an elastic network interface in one subnet per Availability Zone you enable. Only resources in an enabled Availability Zone can reach the TGW at all. Each attachment associates with exactly one route table, which decides where its traffic can go, and propagates its CIDRs into one or more route tables, which decides who can route to it. Those two knobs are what build a segmented topology out of one router.

A shape for 52 VPCs, well inside the default quota of 20 route tables per TGW:

  • Production route table, production VPCs associate here and propagate here, seeing production routes plus the egress attachment.
  • Non-production route table, dev and staging VPCs associate here and see each other plus the egress attachment, never production.
  • Shared services route table, where auth, secrets and observability VPCs associate, and from which they propagate into both production and non-production. Everything reaches them; they cannot route between segments themselves.
  • Egress route table, the egress VPC associates here and every workload CIDR propagates in, so return traffic has a route back.
  • Inspection route table, which the inspection attachment associates with and the workload CIDRs propagate into. What pulls an east-west pair through the inspection VPC is a static route in each segment’s own route table, aimed at the inspection attachment.

Five route tables, not 1,326 peering connections. One gotcha to design around. A new attachment lands in the TGW’s default association and propagation route table unless you turn those defaults off, and in a segmented build that default table is the one a production VPC must not be in.

Sharing the TGW across 18 accounts

The hub lives in one platform account and is shared to the Organization with AWS RAM, which needs Organizations sharing enabled first. A participant account can run CreateTransitGatewayVpcAttachment against the shared TGW for its own VPCs. Beyond describing the TGW and managing its own attachments, it can do nothing more: AWS states that when a transit gateway is shared with you, you cannot create, modify, or delete its route tables, or their propagations and associations. Segmentation therefore stays with the platform team by construction rather than by policy, which is the property the product teams’ independence rests on.

Three consequences follow. Enrolling the 53rd VPC becomes a two-sided operation. The product team creates the attachment; the platform team associates it with the right route table. That seam is the natural place for an approval. Any Site-to-Site VPN attachment must be created in the TGW owner account, so the on-premises edge cannot be delegated the way VPC attachments can. Availability Zone names also map to different physical zones in different accounts. Pick each attachment’s subnet by Availability Zone ID (use1-az1), never by name. Otherwise attachments in different accounts land in zones that do not line up.

Centralised egress

The egress VPC holds a NAT gateway per Availability Zone, an internet gateway, and a TGW attachment in a private subnet. Putting that attachment in the public subnet is the classic way to break this: traffic then routes to the internet gateway, and an instance only reaches the internet that way if it has a public IPv4 address of its own for the gateway to translate. Workload VPCs carry 0.0.0.0/0 -> TGW, and the production and non-production route tables carry a static 0.0.0.0/0 pointing at the egress attachment. Inside the VPC the path runs attachment subnet to appliance to NAT gateway to internet gateway. Return traffic reverses it. This replaces up to 52 sets of NAT gateways with one set per Region, at USD$0.045 per gateway-hour and USD$0.045 per GB processed.

Centralised inspection, and appliance mode

East-west traffic between, say, production and shared services routes through an inspection VPC where a GWLB-fronted fleet sees every packet. A stateful fleet spread over Availability Zones needs ApplianceModeSupport=enable on the inspection VPC’s attachment. With it, the TGW hashes each flow to one network interface in that VPC and uses the same one for the return direction, so both halves of a connection meet the same appliance. Without it, the TGW keeps traffic in its originating Availability Zone, and the response arrives at an appliance holding no state for the request and is dropped. Attach exactly one TGW to the appliance VPC. Separate TGWs keep separate flow tables, so a flow arriving through a second one can be hashed to a different appliance.

If the fleet were AWS Network Firewall rather than a third-party product, a network function attachment would remove the inspection VPC altogether: AWS provisions the GWLB endpoints in a service-managed VPC, enables appliance mode itself, and takes static routes only. Third-party appliances are not supported there, which is why this team still builds the inspection VPC by hand.

Cross-Region, and the ceiling

A peering attachment between two TGWs runs over the AWS backbone, inter-Region or intra-Region. Inter-Region peering rides the same infrastructure as VPC peering, so that traffic carries AES-256 encryption at the virtual network layer; intra-Region traffic is encrypted at the physical layer only, and only on links outside AWS’s own physical control. Routing is static only, so each TGW carries explicit routes for the remote Regions’ CIDRs, and there is no ECMP to aggregate two peerings for more bandwidth. Bandwidth runs to 100 Gbps each direction per Availability Zone per attachment, and around 7.5 million packets per second, with more available on request rather than as a hard wall. Billing follows a sender-pays model. Each TGW owner pays hourly for its own end of the peering. Data processing is charged on the sending VPC attachment, not on the peering attachment, and outbound inter-Region transfer is charged in the sending Region while inbound is free. Three Regions means three peerings, six billable attachment-ends.

The published quotas leave room: 50 peering attachments per TGW, adjustable, against 5,000 attachments in total and 10,000 combined routes. A ten-Region full mesh puts nine peering attachments on each TGW, so what runs out first is the hand-maintained routing, not the quota. Six Regions is 15 peerings and 30 billable ends; ten Regions is 45 and 90, plus static routes on every TGW for every other Region’s CIDRs. The mesh that made peering wrong for 52 VPCs reappears at Region scale.

When Cloud WAN takes over

The move from TGW to Cloud WAN tends to come when at least one of these holds:

  • VPC count runs into the hundreds, and route table editing becomes its own operational load.
  • Region count passes about five, where CNEs full-meshing themselves removes the peering arithmetic above.
  • Segments as declarative intent are wanted over imperative route tables. The policy document says production reaches production and shared services and never non-production, Cloud WAN materialises the routes, and new VPCs map to segments by tag.
  • Firewall insertion has to follow traffic across Regions, which network function groups do with no static routes to maintain.
  • SD-WAN integration with on-premises branches comes into scope.

Worked example

For the 52-VPC, 3-Region, 18-account scenario:

  • Three TGWs, one per Region, each in the platform account and RAM-shared to the Organization.
  • Three peering attachments, forming a triangle between the Regions, with static routes for the two remote Regions in each TGW.
  • A per-Region egress VPC with NAT gateways and the outbound appliance, attached to the Regional TGW.
  • A per-Region inspection VPC with GWLB and appliances, its attachment in appliance mode.
  • Workload attachments associated across the production, non-production and shared-services route tables.

Adding a 53rd VPC: the product team creates the VPC and one attachment; the platform team associates it with a route table and propagates it where needed. Four operations in one Region, split across two accounts, with no change to any other VPC’s route tables.

Cost estimate at US East list prices:

  • 58 VPC attachments, the 52 workload and shared-services VPCs plus a per-Region egress and inspection VPC, × USD$0.05/hr × 730 hrs ≈ USD$2,120/month
  • 6 peering attachment-ends × USD$0.05/hr × 730 hrs ≈ USD$220/month
  • Data processing on 100 TB/month × USD$0.02/GB = USD$2,000/month
  • 9 NAT gateways, one per Availability Zone in three three-zone egress VPCs, × USD$0.045/hr × 730 hrs ≈ USD$300/month

That is about USD$4,600 a month in charges that can be worked out up front. Three things sit on top of that. NAT data processing runs at USD$0.045/GB on whatever share of the 100 TB actually leaves for the internet. Inter-Region transfer applies to the cross-Region share. And the appliance fleet’s licence and instance cost is, in most builds, the largest line of the lot. Peering charges nothing for its connections, and cannot deliver centralised egress or inspection at all, because edge-to-edge routing through a peer’s NAT device is not supported.

What’s worth remembering

  1. Peering is pairwise and non-transitive. Connections grow as N × (N-1) / 2, and routing through a peer’s NAT device or internet gateway is unsupported.
  2. Two knobs segment a Transit Gateway. Association and propagation decide who reaches whom; each VPC gets one attachment and routing is transitive.
  3. RAM shares the transit gateway itself. Participants create attachments but cannot change the owner’s route tables, propagations or associations.
  4. Stateful inspection needs appliance mode. Enable it on the inspection VPC’s attachment, or return traffic reaches an appliance with no state for the flow.
  5. TGW peering is hand-routed. Static routes only, no ECMP, both owners billed for their end, and each new Region needs routes on every other TGW.
  6. Cloud WAN suits many Regions. Past about five it wins despite higher fixed charges: edges full-mesh automatically and a policy document replaces route table editing.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.