DEV Community

Engr.Hamza
Engr.Hamza

Posted on

Demystifying AWS Inter-VPC Connectivity: VPC Peering Versus Transit Gateway in Production

Cover Image

Demystifying AWS Inter-VPC Connectivity: VPC Peering Versus Transit Gateway in Production

Have you ever looked at an AWS architecture diagram that resembles a plate of spaghettified spaghetti, where every single Virtual Private Cloud is directly connected to every other VPC, and felt a cold sweat run down your neck? I have. Back when our microservices architecture scaled past ten isolated workloads, we made the classic junior mistake of setting up point-to-point VPC peer connections everywhere, assuming it was free, fast, and simple. Six months later, managing route tables across a mesh of fifty peering connections felt like trying to untangle headphones in a pocket while driving on the highway.


The Problem Everyone Ignores

When you are spinning up your first few workloads on AWS, network architecture feels like an afterthought. You create a VPC for staging, one for production, and maybe a separate one for data engineering, and connecting them via a simple VPC Peering link feels like a harmless five-minute task in the console. The problem is that peer connections do not scale linearly; they scale quadratically. If you have $N$ VPCs, a full mesh requires up to $N(N-1)/2$ connections, which means your administrative overhead explodes the moment your organization grows.

Architecture Overview

Above: High-level architecture overview of the topic covered in this article.

The real nightmare begins when your security team asks for centralized logging, inspection, and firewall routing through a single security VPC. With pure VPC peering, you quickly realize that transitive routing is not supported, meaning VPC A cannot talk to VPC C via VPC B unless you set up explicit peering between A and C. I remember sitting on an incident call at 2:00 AM because an engineer added a new application VPC, forgot to update the route tables on three other dependent VPCs, and completely broke our cross-region database replication pipeline. You end up maintaining custom shell scripts just to keep track of overlapping Classless Inter-Domain Routing blocks and route propagation states.


What Actually Works

To escape the maintenance hell of full-mesh peering, you need to transition your thinking from point-to-point connections to a centralized hub-and-spoke model powered by AWS Transit Gateway. Instead of wiring every VPC directly to each other, you attach each VPC to a managed regional router that handles routing policies, security domain isolation, and traffic aggregation natively. This architectural shift decouples your network topology from your application growth, allowing you to scale from five VPCs to five hundred without changing your fundamental routing logic.

Before we dive into the infrastructure configuration, let's look at how we can provision a clean, modular hub-and-spoke topology using Infrastructure as Code. The following Terraform configuration sets up a foundational Transit Gateway along with core attachments and route table associations designed for production workloads.

# Configure the core AWS provider
provider "aws" {
  region = "us-east-1"
}

# Create the central AWS Transit Gateway
resource "aws_ec2_transit_gateway" "main_tgw" {
  description                     = "Central production transit gateway for enterprise microservices"
  amazon_side_asn                 = 64512
  auto_accept_shared_attachments  = "disable"
  default_route_table_association = "disable"
  default_route_table_propagation = "disable"

  tags = {
    Name        = "prod-core-tgw"
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

# Create a dedicated Transit Gateway Route Table for isolated workloads
resource "aws_ec2_transit_gateway_route_table" "isolated" {
  transit_gateway_id = aws_ec2_transit_gateway.main_tgw.id

  tags = {
    Name = "tgw-rt-isolated-workloads"
  }
}

# Output the Transit Gateway identifier for downstream modules
output "transit_gateway_id" {
  value       = aws_ec2_transit_gateway.main_tgw.id
  description = "The unique identifier of the core production transit gateway"
}
Enter fullscreen mode Exit fullscreen mode

This Terraform manifest initializes a secure, production-grade Transit Gateway with auto-acceptance disabled and custom route table associations. By disabling default propagation, we ensure that no VPC can talk to another VPC by default, enforcing a strict zero-trust network posture right from the ground up.


Step-by-Step: Let's Build It Together

Now that our core hub is defined, let's walk through attaching a spoke application VPC and configuring the necessary route table entries to route traffic safely through the Transit Gateway. This step-by-step implementation ensures your application subnets know exactly where to send cross-VPC packets.

First, we need to provision the VPC attachment resource, linking our application VPC subnets directly to the Transit Gateway. Here is how you write that configuration:

# Define an application VPC attachment to the Transit Gateway
resource "aws_ec2_transit_gateway_vpc_attachment" "app_vpc_attachment" {
  transit_gateway_id = aws_ec2_transit_gateway.main_tgw.id
  vpc_id             = aws_vpc.app_vpc.id
  subnet_ids         = [aws_subnet.app_subnet_a.id, aws_subnet.app_subnet_b.id]

  dns_support                                     = "enable"
  ipv6_support                                    = "disable"
  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_propagation     = false

  tags = {
    Name = "att-app-production-vpc"
  }
}
Enter fullscreen mode Exit fullscreen mode

That resource block hooks your application subnets into the Transit Gateway fabric across multiple Availability Zones for high availability.

Next, we must update the local route tables inside the application VPC so that any traffic destined for our shared services CIDR block points toward the Transit Gateway attachment. Here is the second code snippet for that routing layer:

# Add custom routes in the application VPC route table pointing to the TGW
resource "aws_route" "app_to_shared_services" {
  route_table_id         = aws_route_table.app_private_rt.id
  destination_cidr_block = "10.100.0.0/16"
  transit_gateway_id     = aws_ec2_transit_gateway.main_tgw.id

  depends_on = [
    aws_ec2_transit_gateway_vpc_attachment.app_vpc_attachment
  ]
}

# Associate the attachment with our custom isolated route table
resource "aws_ec2_transit_gateway_route_table_association" "app_assoc" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.app_vpc_attachment.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.isolated.id
}
Enter fullscreen mode Exit fullscreen mode

What just happened is that we successfully wired our spoke VPC into the central hub, injected the specific destination routes into the private subnet route tables, and bound the attachment to an isolated routing table on the Transit Gateway itself. Traffic can now flow cleanly and deterministically without messy peer meshes.


The Mistakes That Will Burn You

Even with the best tools, cloud networking offers plenty of ways to shoot yourself in the foot. Here are the most common landmines I have seen teams step on when designing inter-VPC connectivity:

  • Mistake 1: Overlapping CIDR blocks across peered or attached VPCs. If VPC A uses 10.0.0.0/16 and VPC B uses 10.0.0.0/16, your routing tables will conflict instantly, resulting in dropped packets and impossible debugging sessions. Always use an enterprise IPAM strategy before provisioning infrastructure.
  • Mistake 2: Relying on default route table associations in production. Leaving auto-accept and default propagation enabled means any new VPC attachment can automatically communicate with everything else, completely bypassing your security posture and compliance requirements.
  • Mistake 3: Ignoring data transfer costs and cross-AZ traffic charges. Routing massive data pipelines through a Transit Gateway across multiple Availability Zones or regions incurs hidden processing and transfer fees that will shock your finance team at the end of the month.

Production Checklist

Before you push your inter-VPC connectivity changes to production, run through this final checklist to ensure stability, security, and scalability:

  • Enforce IPAM: Verify that your entire organization uses non-overlapping CIDR blocks partitioned carefully across regions and business units to prevent catastrophic routing collisions down the road.
  • Isolate Route Tables: Never use default Transit Gateway route tables for production workloads; instead, create dedicated routing domains for internal apps, shared services, and ingress/egress points.
  • Monitor Network Metrics: Set up CloudWatch alarms on Transit Gateway packet drops, bytes-in, and bytes-out metrics to catch bottlenecks and misconfigured firewalls before your users do.
  • Test Failover Paths: Validate cross-AZ resilience by simulating a subnet or availability zone failure to ensure your traffic paths gracefully re-route through redundant attachments.

Key Takeaways

  • VPC Peering works well for simple, static, point-to-point connections between a small number of isolated VPCs, but it fails to scale due to quadratic complexity and a lack of transitive routing.
  • AWS Transit Gateway provides a robust, centralized hub-and-spoke model that simplifies complex enterprise architectures, centralizes security inspections, and scales cleanly to hundreds of VPCs.
  • Zero-Trust Routing should be enforced by disabling default route propagation and utilizing custom route tables for distinct operational environments.
  • Proper IPAM planning prevents overlapping CIDR disasters and ensures your networking layer remains flexible as your organization grows.

Engr. Hamza | AI & MLOps Engineer | Building autonomous systems at the edge of possibility

Top comments (0)