You created the peering connection, the console shows it as Active, and yet — nothing. A curl from one instance to the other just hangs. SSH times out. The application can't reach its database in the peer VPC. This is one of the most frustrating AWS networking situations precisely because every individual component appears fine in isolation.
VPC connectivity requires every layer to be correct simultaneously. A single missing route or one overly restrictive NACL rule is enough to black-hole all traffic. This checklist works through the six most common failure points in the order you should check them, along with the exact CLI commands to verify each one.
1. Does the Peering Connection or TGW Attachment Actually Exist and Is It Active?
Before checking anything else, confirm the connection itself is in the right state. A VPC peering connection must be accepted by the peer account or VPC owner before it becomes active. A TGW attachment must be in the available state.
# Check peering connection state
aws ec2 describe-vpc-peering-connections \
--filters "Name=status-code,Values=active,pending-acceptance,provisioning" \
--query 'VpcPeeringConnections[*].{ID:VpcPeeringConnectionId,Status:Status.Code,Requester:RequesterVpcInfo.VpcId,Accepter:AccepterVpcInfo.VpcId}' \
--output table
# Check TGW attachment state
aws ec2 describe-transit-gateway-attachments \
--filters "Name=state,Values=available,pending,failed" \
--query 'TransitGatewayAttachments[*].{ID:TransitGatewayAttachmentId,State:State,VPC:ResourceId,TGW:TransitGatewayId}' \
--output table
If the peering status is pending-acceptance, someone in the accepter account needs to log in and accept it. If it shows failed or rejected, delete and recreate it. A TGW attachment in a failed state usually indicates a subnet configuration problem in the attachment definition.
2. Are Routes in Both Route Tables Pointing to the Peering or TGW?
This is the most common miss. AWS does not automatically add routes when you create a peering connection — you must do this manually, and you must do it on both sides. Each VPC needs a route entry pointing the other VPC's CIDR at the peering connection ID (for peering) or TGW ID (for Transit Gateway).
# List all route tables and their routes for a VPC
aws ec2 describe-route-tables \
--filters "Name=vpc-id,Values=vpc-0abc12345" \
--query 'RouteTables[*].{RTID:RouteTableId,Routes:Routes[*].{Dest:DestinationCidrBlock,Peering:VpcPeeringConnectionId,TGW:TransitGatewayId,State:State}}' \
--output json
Look for a route with the peer VPC's CIDR as the destination and the peering connection ID or TGW ID as the target, with state active. A state of blackhole means the target no longer exists — the peering or TGW was deleted but the route was left behind.
3. Security Groups — Does the SG Allow Inbound from the Other VPC's CIDR?
Even with routing working perfectly, the destination instance's security group must allow the inbound traffic. Security groups are stateful, so you only need an inbound rule — the return traffic is allowed automatically.
# Find security groups attached to your target instance
aws ec2 describe-instances \
--instance-ids i-0abc12345 \
--query 'Reservations[*].Instances[*].SecurityGroups' \
--output table
# Check inbound rules on a security group
aws ec2 describe-security-groups \
--group-ids sg-0abc12345 \
--query 'SecurityGroups[*].IpPermissions' \
--output json
The inbound rule source can be the peer VPC's full CIDR block (e.g., 10.1.0.0/16), a specific subnet CIDR, or — if both VPCs are in the same account — you can reference the peer security group ID directly. Using CIDR ranges is more common in cross-account scenarios.
4. NACLs — Stateless Rules Need Both Inbound and Outbound Entries
Unlike security groups, Network ACLs are stateless. A TCP connection from VPC-A to port 443 in VPC-B requires: an inbound NACL rule in VPC-B allowing the source CIDR on port 443, and an outbound NACL rule in VPC-B allowing response traffic back to VPC-A on the ephemeral port range (1024–65535).
# Check NACLs associated with a subnet
aws ec2 describe-network-acls \
--filters "Name=association.subnet-id,Values=subnet-0abc12345" \
--query 'NetworkAcls[*].{ID:NetworkAclId,Inbound:Entries[?Egress==`false`],Outbound:Entries[?Egress==`true`]}' \
--output json
Pay attention to rule numbers — NACLs are evaluated in ascending order and the first matching rule wins. A DENY ALL at rule 32767 (the default) is harmless if your allow rules are numbered lower, but a misconfigured deny at rule 100 will silently block traffic that your rule 200 would have allowed.
1024-65535 on the source subnet's NACL to cover ephemeral ports.5. CIDR Overlap — Overlapping CIDRs Silently Break Routing
AWS will let you create a peering connection between two VPCs with overlapping CIDR blocks, but you will not be able to add a route for the overlapping range. The connection shows as active. The route simply can't be created. There is no visible error after the fact — you just can't route to the overlap.
# Check CIDRs of both VPCs
aws ec2 describe-vpcs \
--vpc-ids vpc-0abc12345 vpc-0def67890 \
--query 'Vpcs[*].{VpcId:VpcId,CIDR:CidrBlock,SecondaryBlocks:CidrBlockAssociationSet[*].CidrBlock}' \
--output table
If the CIDRs overlap, your options are limited: assign a non-overlapping secondary CIDR to one VPC (if address space allows), use a Transit Gateway with custom route tables that route only the non-overlapping portions, or — the nuclear option — re-IP one of the VPCs. This is painful, which is exactly why CIDR planning before VPC creation matters.
6. DNS Resolution — VPC Peering Requires enableDnsResolution on Both Sides
If IP-to-IP connectivity is working but hostnames from the peer VPC don't resolve correctly, the problem is almost always the DNS resolution setting on the peering connection. By default, DNS hostnames from a peer VPC resolve to their public IP address, not the private IP — meaning traffic bypasses the peering link entirely and goes out to the internet (or fails for private-only instances).
# Check DNS resolution settings on a peering connection
aws ec2 describe-vpc-peering-connections \
--vpc-peering-connection-ids pcx-0abc12345 \
--query 'VpcPeeringConnections[*].{ID:VpcPeeringConnectionId,RequesterDNS:RequesterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc,AccepterDNS:AccepterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc}' \
--output table
# Enable DNS resolution (must be run for both sides, may require separate account profiles)
aws ec2 modify-vpc-peering-connection-options \
--vpc-peering-connection-id pcx-0abc12345 \
--requester-peering-connection-options AllowDnsResolutionFromRemoteVpc=true
# Run in accepter account/profile:
aws ec2 modify-vpc-peering-connection-options \
--vpc-peering-connection-id pcx-0abc12345 \
--accepter-peering-connection-options AllowDnsResolutionFromRemoteVpc=true
For Transit Gateway, DNS resolution works differently — TGW doesn't carry DNS, so you rely on Route 53 Resolver inbound and outbound endpoints in the appropriate VPCs, or a centralized DNS resolver in a shared services VPC.
The Most Common Pattern: One-Way Traffic
The single most common VPC connectivity failure looks like one-way traffic. ping sends but nothing comes back. TCP connections time out rather than being refused. The cause is almost always asymmetric routing: a route exists in one VPC but not the other. Traffic from VPC-A reaches VPC-B fine, but VPC-B has no route back to VPC-A's CIDR, so responses are silently dropped.
- Traffic flows A → B: VPC-A has a route for VPC-B's CIDR via the peering connection. VPC-B's security group allows inbound from VPC-A's CIDR.
- But VPC-B has no route for VPC-A's CIDR, so response packets hit the implicit default route and go nowhere useful.
- Fix: add the missing route in VPC-B's route table, pointing VPC-A's CIDR at the peering connection ID.
Run through this checklist in order and you'll find the break. In a complex environment with dozens of VPCs, peering connections, and TGW attachments across multiple accounts, doing this manually for every path is slow and error-prone. A topology tool that shows the actual routing graph — including missing routes and blocked paths — makes these gaps visible immediately rather than after 45 minutes of CLI output parsing.