AWS VPC Networking: Transit Gateway vs PrivateLink vs VPC Peering and Hub-and-Spoke Design (2026)

AWS VPC Networking: Transit Gateway vs PrivateLink vs VPC Peering and Hub-and-Spoke Design (2026)

Transit Gateway vs PrivateLink vs VPC Peering: AWS Hub-and-Spoke Design (2026)

Most AWS networks do not fail because someone picked the wrong service on day one. They fail because the first three VPCs were connected with peering, the next ten were bolted on with whatever was fastest, and nobody wrote down why. Two years later a security team wants central inspection, a finance team wants to know why cross-account traffic costs what it does, and an acquisition brings in a partner whose address space overlaps yours. A transit gateway is usually the answer to the first two problems, PrivateLink is usually the answer to the third, and VPC peering is still the cheapest answer when the topology is small.

This post is written as an architecture decision record. It sets out the forces, compares the three connectivity primitives on routing semantics, scale limits, security model and cost, and then walks through a hub-and-spoke reference design with inspection and DNS. All limits and prices below were checked against AWS documentation on the day of writing; where a figure is illustrative or region-dependent, it is labelled.

What this covers: how each primitive actually moves packets, a decision framework, a hub-and-spoke reference architecture, route table and Terraform patterns, DNS and CIDR planning, a cost model, and the failure modes that cause outages.

Context and Background

A Virtual Private Cloud (VPC) is a regional, isolated layer-3 network with its own CIDR range. Inside a VPC everything routes by default through the implicit local route. The moment you need traffic to cross a VPC boundary, you pick one of a small set of mechanisms, and each one encodes a different assumption about trust, scale and ownership.

The three mechanisms this post compares sit at different layers of intent. VPC peering joins two VPC route domains directly. Transit Gateway (TGW) is a regional virtual router to which many VPCs, VPNs and Direct Connect gateways attach. AWS PrivateLink does not join networks at all; it exposes one service from a provider VPC into a consumer VPC through an elastic network interface (ENI) with a private IP. Treating these as interchangeable “ways to connect VPCs” is the root of most bad designs.

The landscape has also grown. AWS Cloud WAN, a managed global networking service driven by a declarative core network policy, now sits above Transit Gateway for multi-Region estates; the AWS documentation notes that a core network edge is, in effect, a managed Transit Gateway in each Region. Gateway Load Balancer endpoints, VPC Lattice and PrivateLink resource endpoints widen the choices further. For background on the surrounding identity and DNS layers that every network design leans on, see our posts on workload identity with SPIFFE and SPIRE and on Kubernetes DNS and CoreDNS latency.

The authoritative references are the Amazon VPC Transit Gateway guide, the VPC peering guide and the AWS PrivateLink concepts page. Everything stated here as a limit comes from those or from the linked quota pages.

The decision being made

The decision this record addresses is narrow: how should an organization with many AWS accounts and VPCs in one or more Regions provide connectivity between workloads, to shared services, and to on-premises networks, while keeping central control over routing, inspection and cost?

The forces pulling on that decision are familiar. Security wants a chokepoint where east-west traffic can be inspected. Platform teams want self-service onboarding that does not require editing N other VPCs’ route tables. Finance wants predictable per-GB costs. Application teams want low latency and do not want to learn about route propagation. Partners and SaaS vendors want to reach a single endpoint without being given a network path into everything.

No single primitive satisfies all of these, which is why the realistic answer is a combination. The rest of this post is about choosing the combination and being explicit about what each piece is for.

The Core Decision: Three Primitives, Three Different Contracts

Short answer: use VPC peering for a handful of high-volume point-to-point links, use a transit gateway as the routed hub when you have many VPCs or need central inspection and on-premises reach, and use PrivateLink when you want to expose one service, not a network, especially across overlapping CIDRs or organizational boundaries.

Decision flow for choosing transit gateway, PrivateLink or VPC peering for AWS VPC connectivity

Figure 1: Decision flow for AWS VPC connectivity. The first question is whether the consumer needs a network or a single service.

The figure encodes the order in which to ask questions. First ask whether the consumer needs network reach or only one service. If only a service, PrivateLink (or a gateway endpoint for S3 and DynamoDB) is the answer regardless of scale. If network reach is needed, count VPCs and Regions. The decision then falls out of quota and operational-cost arithmetic rather than taste.

VPC peering: simple, cheap, non-transitive

A peering connection is a one-to-one relationship between two VPCs, same account or cross-account, same Region or cross-Region. Traffic flows over the AWS backbone with no gateway device, no bandwidth bottleneck and no per-attachment charge. Both sides add routes to the peer’s CIDR, and in the same Region you can reference the peer’s security groups in rules.

The AWS documentation is explicit about the limits. Peering is not transitive: if A peers with B and C, B cannot reach C through A. You cannot peer VPCs with matching or overlapping IPv4 or IPv6 CIDR blocks. Edge-to-edge routing is unsupported, meaning a peer cannot use your internet gateway, NAT device, VPN connection, Direct Connect connection or S3 gateway endpoint. Cross-Region peering has an MTU of 8,500 bytes versus 9,001 for same-Region peering, and security group references work only within a Region.

The scale limit is the quiet killer. The default quota is 50 active peering connections per VPC, adjustable to a maximum of 125. A full mesh of n VPCs needs n(n-1)/2 connections and n-1 route entries in every VPC. Twenty VPCs need 190 peerings; sixty need 1,770, and each VPC would exceed the default quota of 50. Long before the quota binds, the operational burden of 59 route-table entries per VPC and 1,770 objects to track binds first.

Transit Gateway: a regional router with route tables

A transit gateway is a managed regional virtual router. You create attachments (VPC, VPN, Direct Connect gateway, Connect, peering) and each attachment is associated with exactly one TGW route table, while its routes can be propagated into one or more route tables. Association decides which table is consulted for traffic arriving from an attachment; propagation decides which attachments’ CIDRs appear in a table.

This split is the entire mental model. A route table is a routing domain. Putting production attachments in one table and development attachments in another gives you isolation without a single security group. Putting a shared-services attachment’s routes in both gives both domains access to it. Inspection designs use static routes to force traffic through a firewall VPC.

Documented quotas on the AWS quota page give the scale envelope: 5,000 attachments per transit gateway, 20 route tables per transit gateway, 10,000 total routes across all route tables on one TGW, five transit gateways per account per Region, and up to 100 Gbps ingress and 100 Gbps egress per VPC attachment per Availability Zone. Maximum MTU is 8,500 bytes for VPC, Direct Connect, Connect and peering attachments, while VPN traffic is limited to 1,500 bytes. These are adjustable or fixed as noted on the quota page, so confirm before designing close to a limit.

PrivateLink inverts the model. The provider fronts a service with a load balancer and publishes an endpoint service; the consumer creates an interface endpoint that places an ENI with a private IP address in each chosen subnet. DNS resolves the service name to those IPs. By default an endpoint service is not available to anyone: the provider must add permissions for specific AWS principals, and for interface endpoints the consumer initiates the connection while the provider accepts or rejects it.

Because the consumer sees only an IP in its own address space, overlapping CIDRs are irrelevant. The provider cannot initiate connections to the consumer. Both properties make PrivateLink the right primitive for SaaS, for partner access, and for platform teams exposing an internal API to hundreds of consuming accounts without handing out network reach.

Gateway endpoints for S3 and DynamoDB are a different, older mechanism: they work through route table entries and, as the AWS documentation states, do not use PrivateLink. They carry no hourly or data processing charge, which makes them the first thing to enable in any VPC that talks to S3 heavily.

A side-by-side contract comparison

Dimension VPC peering Transit Gateway PrivateLink
What it connects Two VPC route domains Many attachments through a router One service to many consumers
Transitive routing No Yes, by route table design Not applicable
Overlapping CIDRs Not allowed Not allowed in the same route table Allowed
Central inspection Not possible Yes, via inspection VPC Per endpoint only
On-premises reach Not via the peer Yes, VPN and Direct Connect attach Via consumer VPC only
Direction of initiation Both ways Both ways Consumer to provider
Scale per unit 50 default, 125 max peerings per VPC 5,000 attachments per TGW Per service and endpoint quotas
Cost shape Data transfer only Per attachment hour plus per GB Per endpoint hour plus per GB

The “not allowed in the same route table” entry for Transit Gateway deserves care: a TGW will not stop you attaching two VPCs with overlapping CIDRs, but it cannot route to both from the same table, and the routing outcome is ambiguous. Treat overlapping CIDRs as a design error to be solved at the address-plan level or fenced with PrivateLink.

Hub-and-Spoke Reference Architecture with a Transit Gateway

A hub and spoke network puts a transit gateway in the middle and treats every workload VPC as a spoke. Spokes never connect to each other directly; every inter-VPC and VPC-to-internet flow passes through the hub, where routing policy and inspection are enforced. The pattern trades a small amount of latency and a per-GB processing charge for a single place to reason about reachability.

Hub and spoke network on AWS with a transit gateway, inspection VPC, egress VPC and on-premises attachment

Figure 2: Reference hub-and-spoke design. Spoke attachments associate with a table whose default route points at the inspection VPC; the inspection attachment associates with a table holding propagated spoke routes.

Figure 2 shows the shape. Production, staging and development spokes attach to the transit gateway. An inspection VPC holds the firewall fleet, an egress VPC holds NAT gateways and the internet gateway, and a VPN or Direct Connect attachment brings in on-premises networks. Two route tables do the work, and the next sections explain why two is the right number to start with.

Route tables as isolation domains

The first decision is to turn off default association and default propagation on the transit gateway. With the defaults on, every new attachment lands in one default table and can reach everything else, which is exactly the flat network you were trying to avoid. With them off, an attachment has no connectivity until a platform pipeline explicitly associates it with a table.

A minimal two-table design looks like this. The spoke table has associations from every workload VPC attachment and one static route, 0.0.0.0/0, whose target is the inspection VPC attachment. The inspection table has an association from the inspection attachment only, and spoke attachments propagate their CIDRs into it so that return traffic can find its way home. This is the same layout AWS shows in its inspection-VPC scenario documentation.

The consequence is that a packet from spoke A to spoke B enters the TGW, is looked up in the spoke table, matches the default route, and is delivered to the inspection VPC. After the firewall passes it, the packet re-enters the TGW on the inspection attachment, is looked up in the inspection table, and is forwarded to spoke B. Spokes can therefore be isolated from each other simply by firewall policy, with no per-spoke route surgery.

For stricter segmentation add tables rather than firewall rules. A “prod” table and a “nonprod” table, each with propagation only from permitted peers, give you a blast-radius boundary that holds even if the firewall is misconfigured. A shared-services spoke (DNS, artifact registry, directory) propagates into both. The 20-route-table default quota is generous for this: most estates need fewer than ten.

The spoke VPC side

Each spoke VPC needs a route table entry that sends non-local traffic to the transit gateway. For a spoke that should use the hub for everything, a single 0.0.0.0/0 route to the TGW suffices. For spokes that keep their own internet path, specific routes such as the RFC 1918 ranges pointing at the TGW are better, which keeps a fallback to a local NAT gateway if the hub is unavailable.

The attachment itself needs one subnet per Availability Zone in which you want TGW connectivity. AWS recommends dedicating a small subnet per AZ for the attachment, commonly a /28, so that network ACLs and route tables for the attachment interfaces are independent from workload subnets. A /28 holds 16 addresses, of which AWS reserves five per subnet, leaving 11, which is ample for the single ENI the attachment needs in that AZ.

Be deliberate about AZ coverage. Without appliance mode, a transit gateway keeps traffic in the Availability Zone where it originated until it reaches the destination, crossing AZs only during an AZ failure or if the destination has no subnet attached in that AZ. A spoke attached in only one AZ will therefore be reachable only in that AZ for same-AZ-local paths, a common source of mystery cross-AZ charges and latency.

Terraform for the hub

The following sketch creates a transit gateway with default association and propagation disabled, two route tables, and the inspection default route. It is intentionally minimal and omits resource sharing, tagging and lifecycle rules.

resource "aws_ec2_transit_gateway" "hub" {
  description                     = "org-hub"
  amazon_side_asn                 = 64512
  default_route_table_association = "disable"
  default_route_table_propagation = "disable"
  auto_accept_shared_attachments  = "disable"
  dns_support                     = "enable"
  vpn_ecmp_support                = "enable"
}

resource "aws_ec2_transit_gateway_route_table" "spokes" {
  transit_gateway_id = aws_ec2_transit_gateway.hub.id
  tags = { Name = "spokes" }
}

resource "aws_ec2_transit_gateway_route_table" "inspection" {
  transit_gateway_id = aws_ec2_transit_gateway.hub.id
  tags = { Name = "inspection" }
}

resource "aws_ec2_transit_gateway_vpc_attachment" "inspection" {
  transit_gateway_id     = aws_ec2_transit_gateway.hub.id
  vpc_id                 = var.inspection_vpc_id
  subnet_ids             = var.inspection_tgw_subnet_ids
  appliance_mode_support = "enable"
  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false
}

resource "aws_ec2_transit_gateway_route_table_association" "inspection" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.inspection.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.inspection.id
}

resource "aws_ec2_transit_gateway_route" "spokes_default" {
  destination_cidr_block         = "0.0.0.0/0"
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.inspection.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.spokes.id
}

# Per spoke: associate with spokes table, propagate into inspection table
resource "aws_ec2_transit_gateway_route_table_association" "spoke" {
  for_each                       = aws_ec2_transit_gateway_vpc_attachment.spoke
  transit_gateway_attachment_id  = each.value.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.spokes.id
}

resource "aws_ec2_transit_gateway_route_table_propagation" "spoke_to_inspection" {
  for_each                       = aws_ec2_transit_gateway_vpc_attachment.spoke
  transit_gateway_attachment_id  = each.value.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.inspection.id
}

Note the explicit appliance_mode_support = "enable" on the inspection attachment, which the next section explains. The aws_ec2_transit_gateway_vpc_attachment.spoke map is assumed to be defined elsewhere, typically one entry per account-provided VPC. Share the transit gateway to member accounts with AWS Resource Access Manager and keep auto_accept_shared_attachments disabled so that the platform team approves each attachment.

Why the hub is an organizational pattern as much as a technical one

The hub gives you a place to hang ownership. A central network account owns the transit gateway, route tables and inspection VPC; application accounts own their spoke VPCs and request an attachment. That separation maps cleanly onto how most platform teams are structured, and it gives auditors a single object to examine when asking “who can reach the database tier.”

It also concentrates risk. The hub is a shared dependency for every workload that uses it, so changes to its route tables need the same discipline as changes to a core database schema: review, staged rollout and an automated reachability check. We return to that in the failure-modes section.

Symmetric routing and appliance mode

Central inspection exposes the most counter-intuitive behavior of a transit gateway. Stateful firewalls need to see both directions of a flow. A TGW, by default, keeps traffic in the originating AZ until it reaches the destination. If spoke A in AZ1 sends to spoke B, the forward packet may traverse the inspection VPC’s AZ1 appliance, while spoke B’s reply originates in AZ2 and is delivered to the appliance in AZ2, which has no state for the flow and drops it. The AWS documentation uses essentially this example.

Appliance mode fixes it. When enabled on the inspection VPC attachment, the transit gateway chooses one appliance network interface using a flow hash and uses it for the life of the flow, so return traffic uses the same interface and the same AZ. AWS Network Firewall attachments have appliance mode enabled automatically; for third-party appliances you must enable it yourself, either at attachment creation or later with modify-transit-gateway-vpc-attachment. The documentation warns that enabling it on an existing attachment can change routing, because traffic may then use any AZ rather than staying in the originating one, so schedule that change as you would any routing change.

Sequence diagram of a transit gateway forwarding a flow through an inspection VPC with appliance mode keeping the reply on the same AZ

Figure 3: Sequence of a spoke-to-spoke flow through the inspection VPC. Appliance mode pins the reply to the same appliance AZ so stateful matching succeeds.

Several documented limitations matter in practice. Attach the appliance VPC to only one transit gateway, because multiple gateways do not share flow state and stickiness is not guaranteed. Source and destination must use the same attachment pattern, so a design that lets traffic enter the inspection VPC from another path such as an internet gateway and then forwards it to the TGW can drop packets. Stickiness applies only to traffic toward the inspection VPC.

Inside the inspection VPC, the documented pattern uses two subnets per AZ. The transit gateway subnet’s route table sends 0.0.0.0/0 to the appliance ENI, and the appliance subnet’s route table sends 0.0.0.0/0 back to the transit gateway. The firewall is, in effect, a bump in the wire between two route tables, which is why this pattern scales by adding appliances behind a Gateway Load Balancer rather than by adding routes.

Figure 4 below shows what the consumer actually experiences. The client resolves a name, DNS returns the private IPs of the endpoint ENIs in the consumer’s own subnets, and the connection terminates on the ENI. PrivateLink carries it across to the provider’s load balancer. Nothing in the consumer VPC needs a route to the provider’s address space, which is why overlapping CIDRs work.

PrivateLink architecture showing a consumer interface endpoint ENI connecting through PrivateLink to a provider network load balancer

Figure 4: PrivateLink data path. The consumer talks to a private IP in its own VPC; the provider sees connections arriving from the load balancer, not from the consumer’s network.

An endpoint service requires a load balancer on the provider side. The AWS PrivateLink concepts page describes the load balancer as the component that receives consumer requests and routes them to the service. In practice that means a Network Load Balancer for TCP and TLS workloads, or a Gateway Load Balancer when the service is a fleet of virtual appliances reached by Gateway Load Balancer endpoints, which carry traffic over private IPs and are steered by route table entries.

Two further PrivateLink constructs have appeared and are worth knowing. Resource endpoints let a provider share a single resource, such as an IP address, domain-name target or RDS database, through a resource configuration shared via AWS RAM, with no load balancer required. Service network endpoints let one endpoint reach multiple resources and services associated with a VPC Lattice service network. For a platform team that wants to publish a database to many consumer accounts without running an NLB, resource endpoints are worth evaluating.

Access control is layered. The provider controls who can connect by adding allowed principals to the endpoint service and optionally requiring manual acceptance. The consumer controls who inside its VPC can use the endpoint with security groups on the ENIs and an endpoint policy, an IAM resource policy whose default allows all actions by all principals. Tightening that endpoint policy for services such as S3 or STS is one of the cheapest data-exfiltration controls available.

DNS is the part everyone forgets

Private DNS for interface endpoints works by associating a private hosted zone with the consumer VPC so that the service’s regular public hostname resolves to the endpoint ENIs. This split-horizon behavior is why applications do not need code changes. It also means DNS settings on the VPC (DNS support and DNS hostnames) must be enabled for it to work.

DNS gets harder at hub scale. Peering across Regions requires DNS resolution support to be enabled to resolve the peer’s private hostnames. With a transit gateway, DNS does not travel with the routes: a spoke cannot resolve names from a private hosted zone associated only with another spoke. The standard answer is Route 53 Resolver endpoints in a shared-services VPC, with outbound rules forwarding corporate zones to on-premises resolvers and inbound endpoints receiving queries from them, plus private hosted zones shared and associated to spokes. Our Kubernetes DNS and CoreDNS latency post covers what happens when the in-cluster resolver chain on top of this adds latency.

A practical rule: decide where each private zone lives and who is allowed to associate with it before you attach the first spoke. Retrofitting DNS ownership across thirty accounts is slower than the networking itself.

CIDR planning math

Every option except PrivateLink requires non-overlapping address space, so the address plan is the foundation. A VPC primary CIDR can range from /16 (65,536 addresses) to /28 (16 addresses) for IPv4. Each subnet loses five addresses to AWS, so a /24 subnet yields 251 usable addresses.

A workable corporate plan reserves a /8 or a set of /12s from RFC 1918 space (10.0.0.0/8 provides 16,777,216 addresses) and allocates fixed-size blocks per environment and Region. For example, carving /16 per VPC from a single /8 gives 256 VPCs; carving /20 per VPC (4,096 addresses) gives 4,096 VPCs, which is a better fit for hundreds of small accounts. A central IP address management tool, whether Amazon VPC IP Address Manager or a spreadsheet with governance, should be the only way a new VPC gets its CIDR.

Reserve space for the things that bite later: secondary CIDRs for container networking where pods consume VPC IPs, ranges for on-premises, and ranges for acquired companies. If you inherit overlapping space you cannot fix quickly, fence it behind PrivateLink or a NAT-based translation tier rather than letting it into the shared route tables.

Multi-Region and Cloud WAN

Transit gateways are regional. To connect two Regions you create a peering attachment between two TGWs and add static routes in each table pointing at the peer; the documentation lists a quota of 50 peering attachments per TGW and only one peering between any two TGWs, or between a TGW and a Cloud WAN core network edge. Peered TGWs do not propagate routes dynamically across the peering, so every cross-Region prefix is a static route that someone must maintain.

AWS Cloud WAN removes that burden. A core network policy, a declarative JSON document, defines segments, Regional routing and how attachments map to segments, and attachment policies can map attachments to segments by tag. Segments behave like a globally consistent VRF, and a network function group can steer segment traffic through firewall attachments within and across Regions without static routes. A core network edge can be peered to an existing Transit Gateway in the same Region, so migration can be incremental. As a rule of thumb, two or three Regions with stable topology are fine with TGW peering; beyond that, the static-route toil argues for Cloud WAN. That threshold is a judgment, not an AWS-published number.

Cost Model: Where the Money Actually Goes

Networking cost is dominated by data volume and by how many times a byte is billed on its way. The numbers below come from the AWS pricing pages on the day of writing. The Transit Gateway pricing page states its example rates for US East (Ohio): $0.10 per hour for the transit gateway including $0.05 per VPC attachment, and $0.02 per GB of data sent from a VPC, Direct Connect, VPN or Network Firewall to the transit gateway. Rates differ by Region, so treat every figure here as a worked example and confirm with the AWS Pricing Calculator.

Other documented items: data arriving from a peering attachment is not charged for processing, and the TGW owner pays hourly for the peering attachment. Connect attachments add no data processing beyond the underlying attachment. Standard AWS data transfer charges apply on top. For PrivateLink, interface endpoints are billed per hour per Availability Zone, with data processing tiered per Region at $0.01 per GB for the first petabyte, $0.006 for the next 4 PB and $0.004 beyond 5 PB; the endpoint ENI example on the pricing page uses $0.01 per hour. Cross-Region interface endpoints add inter-Region data transfer. Same-Region peering that crosses Availability Zones is billed at $0.01 per GB in each direction, per the VPC pricing page.

An illustrative comparison

Take 20 spoke VPCs and 50 TB (51,200 GB) of inter-VPC traffic a month, using the Ohio rates above. These numbers are illustrative, not a quote, and they ignore cross-AZ transfer, firewall licensing and NAT.

Component Calculation Monthly (illustrative)
TGW attachment hours 20 spokes x $0.05 x 730 h $730
TGW data processing 51,200 GB x $0.02 $1,024
Inspection and egress attachments 2 x $0.05 x 730 h $73
TGW total, ignoring inspection traffic doubling about $1,827
Peering mesh (190 links) data 51,200 GB, cross-AZ share only at $0.01 per GB each way Depends on AZ placement, no hourly fee

Two lessons follow. First, peering has no hourly or processing fee, so for a high-volume pair of VPCs it is materially cheaper than routing through a TGW, which bills $0.02 per GB each time bytes enter it. Second, inspection can roughly double the processing bill: a spoke-to-spoke flow enters the TGW from the source spoke and again from the inspection VPC, so budget for two billed hops, not one.

The break-even logic is therefore this. Keep a transit gateway for the many-to-many control plane, and use a direct peering for one or two elephant flows between known pairs, for example a data lake VPC and a training VPC moving tens of terabytes. This hybrid is legitimate; the cost is that those flows bypass central inspection, which should be a conscious, documented exception. The same cost-visibility discipline applies inside Kubernetes clusters; see our guide to cost allocation, rightsizing, showback and chargeback with OpenCost for allocating the network line item to teams.

Allocation and showback for shared networking

A shared TGW creates a chargeback puzzle. The central account pays the attachment and processing bill, but consumption belongs to the application accounts. The workable approach is to enable flow logs on TGW attachments, aggregate bytes by attachment, and allocate the processing fee proportionally, with attachment-hours split evenly or by tag. Without this, networking becomes an unowned cost center that is easy to cut in the wrong place.

Trade-offs, Gotchas, and What Goes Wrong

Blast radius. A transit gateway is a shared fate component. A mistaken static route in the spokes table, or a propagation enabled into the wrong table, changes reachability for every attached VPC at once. Peering has the opposite property: damage is bounded to two VPCs. Mitigate with infrastructure-as-code review, a small number of well-named tables, and an automated reachability test (for example with AWS Network Access Analyzer or Reachability Analyzer) that runs in the pipeline before apply.

Asymmetric routing. Described above, and the most common inspection failure. Symptoms are connections that open then stall, or only fail between certain AZs. Check that appliance mode is on, that the inspection VPC is attached to a single transit gateway, and that no alternative path such as an internet gateway lets traffic enter the inspection VPC from outside the TGW.

Overlapping CIDRs. Two VPCs with matching ranges cannot be peered, and in a TGW table the ambiguity makes routing unreliable. The wrong fixes are NAT hairpins sprinkled ad hoc; the right fix is an address plan enforced by IPAM, and PrivateLink for the legacy exceptions.

Static routes and propagation drift. Cross-Region TGW peering needs static routes on both sides. A new VPC in region A that is not added to region B’s table is silently unreachable. Cloud WAN’s policy model reduces this class of bug at the cost of adopting a new control plane.

MTU surprises. The TGW supports up to 8,500 bytes between VPCs, Direct Connect, Connect and peering attachments, but VPN traffic is capped at 1,500. Jumbo frames that work VPC-to-VPC will black-hole or fragment toward on-premises over VPN unless path MTU discovery works end to end. Cross-Region peering is 8,500 versus 9,001 same-Region, which can bite tuned database replication.

PrivateLink is unidirectional and per-port. The provider cannot call back into the consumer. Protocols that require server-initiated connections, or peer-to-peer meshes such as some clustering and replication systems, do not fit. Also remember each interface endpoint costs per AZ per hour, so one endpoint per service per VPC across six services and three AZs adds up; centralizing endpoints in a shared VPC reduces cost but reintroduces the TGW data processing charge for traffic to them.

Security groups across a TGW. You cannot reference a security group from another VPC across a transit gateway; you reference CIDRs. Same-Region peering does allow referencing a peer’s security group. Teams migrating from peering to TGW often discover hundreds of rules to rewrite.

Quotas that are not adjustable. The quota page lists items such as one peering between two TGWs and one static route per prefix to a single attachment as fixed. Design with them as hard constraints, not as increase requests.

The hub is not free of limits. 100 Gbps per VPC attachment per AZ is large, but a single flow is subject to per-flow limits and the firewall fleet behind the hub is usually the true bottleneck. The vendor-published throughput of your firewall, not the TGW, usually sets the ceiling; verify with a load test rather than assuming.

Practical Recommendations

Start from the contract each primitive offers. If a consumer needs a service, give it a PrivateLink endpoint, not a route. If it needs a network, give it a spoke on a transit gateway. If two known VPCs move enormous volume, give them a peering as a documented exception. If you operate in more than two or three Regions, evaluate Cloud WAN before you accumulate static routes.

Make the platform the product. A central network account owns the TGW and inspection VPC, shares the TGW through AWS RAM, and offers a vending module that creates a spoke VPC with an IPAM-allocated CIDR, a TGW attachment, route table associations and baseline endpoints. Application teams should never hand-edit TGW routes.

Prioritize by return. Gateway endpoints for S3 and DynamoDB first, because they are free of hourly and processing charges. A written address plan second, because fixing CIDRs later is the most expensive change in the whole stack. Central inspection third, with appliance mode on from day one. Multi-account chargeback fourth, so the bill has owners. For stateful data tiers that span Regions, pair this design with the replication patterns in our multi-region active-active database architecture guide, since inter-Region latency and transfer cost dominate there.

A short checklist before go-live:

  • Default TGW route table association and propagation are disabled.
  • Every attachment has an explicit association and a documented propagation set.
  • Appliance mode is enabled on the inspection attachment, with one appliance VPC per transit gateway.
  • Each spoke attaches with one dedicated small subnet per AZ in use.
  • Address plan is enforced through IPAM, with space reserved for on-premises and acquisitions.
  • DNS ownership for private zones and Resolver endpoints is decided and tested.
  • Gateway endpoints are enabled; interface endpoint policies are restricted.
  • Flow logs feed a showback model; reachability tests run in CI.

Frequently Asked Questions

Is a transit gateway better than VPC peering?

Neither is better in general. Peering is cheaper (no hourly or processing fee) and simple for a few VPCs, but it is non-transitive and has a quota of 50 active connections per VPC by default, up to 125. A transit gateway scales to 5,000 attachments, supports transitive routing, inspection and on-premises connectivity, but charges per attachment hour and per GB processed. Use peering for a few heavy pairs and a transit gateway for the many-to-many fabric.

Use PrivateLink when the consumer needs one service rather than network reach, when CIDRs overlap, or when the consumer belongs to another organization. The consumer sees a private IP in its own VPC and the provider cannot initiate connections back. Use a transit gateway when workloads need broad, bidirectional, routed connectivity. Many estates use both: a TGW for internal routing and PrivateLink to publish shared platform APIs.

Can VPC peering connections be transitive through a hub VPC?

No. The AWS documentation states that if VPC A peers with B and C, traffic cannot route from B to C through A. Edge-to-edge routing through a peer’s internet gateway, NAT device, VPN, Direct Connect or S3 gateway endpoint is also unsupported. If you need transitive behavior, use a transit gateway, or accept a full mesh with its n(n-1)/2 connection count and the operational overhead that comes with it.

How much does a transit gateway cost?

AWS publishes example rates for US East (Ohio) of $0.05 per VPC attachment per hour and $0.02 per GB of data sent from a VPC, Direct Connect, VPN or Network Firewall to the transit gateway. Peering attachments incur hourly fees but no processing for arriving data. Rates vary by Region and standard data transfer charges apply on top, so model your traffic with the AWS Pricing Calculator. Inspection designs can bill the same bytes twice.

What is appliance mode on a transit gateway?

Appliance mode is an attachment option that makes the transit gateway pick one appliance network interface per flow using a flow hash and keep using it, so forward and return traffic traverse the same Availability Zone. Without it, return packets can reach a firewall that never saw the original request and be dropped. Network Firewall attachments enable it automatically; third-party appliance VPC attachments need it set explicitly.

Do I need AWS Cloud WAN if I already use Transit Gateway?

Not necessarily. Transit Gateway with peering is adequate for one to three Regions with a stable topology. Cloud WAN adds a global policy-driven core network with segments, attachment policies by tag and network function groups for steering traffic through firewalls without static routes. AWS documents that a core network edge can be peered with a Transit Gateway in the same Region, so you can adopt it incrementally rather than all at once.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *