“`html
Why VPC Peering Connections Get Rejected
AWS VPC peering connection refused errors have burned me more hours than I care to admit — especially when deploying across multiple AWS accounts for infrastructure that needs genuine isolation. The frustrating part? It’s not that AWS makes peering hard. It’s that failures feel completely silent until you actually try to ping something across account boundaries and get nothing back.
Most rejections fall into three buckets. First, the peering connection itself never enters ACTIVE status — it stays PENDING ACCEPTANCE or fails to activate entirely. Second, the connection is active but DNS names don’t resolve across VPCs, so applications timeout trying to reach private endpoints. Third, traffic reaches the destination but security rules block it at the last moment. From an application perspective, all three look identical: connection refused or timeout.
What makes this worse in military and government deployments is the multi-account architecture almost always in place. You’re not just connecting two VPCs in one account — you’re bridging development, staging, and production across separate AWS accounts with different teams managing permissions. The peering setup might be someone else’s responsibility, DNS is handled by a third team, and security group rules live with yet another group. Diagnosing which layer failed becomes a coordination nightmare.
Step 1 — Check Peering Connection Status and Route Tables
Probably should have opened with this section, honestly. Before touching anything else, verify the peering connection actually exists and is active.
Open the AWS Console and navigate to VPC → Peering Connections. Look at the Status column — you need to see ACTIVE. Anything else means the connection itself isn’t ready. If you see PENDING ACCEPTANCE, someone from the accepting account needs to approve it. If you see FAILED or REJECTED, check the Status Message column for why AWS rejected it. Usually it’s overlapping CIDR blocks or invalid configurations.
I made this mistake once: I had the peering connection created but never accepted on the receiving side. Spent an hour checking route tables before realizing the acceptance email got flagged as spam. Don’t make my mistake.
Once it’s ACTIVE, here’s where most people miss the next step entirely. The peering connection itself doesn’t route traffic — you need explicit route table entries in both VPCs. Using the AWS Console: go to VPC → Route Tables, select the route table you want to modify, click Edit Routes, add a new route with the destination CIDR of the remote VPC and the peering connection ID as the target.
The CLI approach is faster for troubleshooting:
- List peering connections to get the ID:
aws ec2 describe-vpc-peering-connections --filters Name=requester-vpc-info.cidr-block,Values=10.0.0.0/16 --region us-east-1 - Check if routes exist in your route table:
aws ec2 describe-route-tables --route-table-ids rtb-xxxxx --region us-east-1 - Add the route if missing:
aws ec2 create-route --route-table-id rtb-xxxxx --destination-cidr-block 10.1.0.0/16 --vpc-peering-connection-id pcx-xxxxx --region us-east-1
Then repeat this for the route table on the other side of the peering connection. This is where most peering failures actually hide — the connection is fine but routes don’t exist, so packets get dropped silently at the routing layer.
Step 2 — Verify Security Groups and Network ACLs
Security groups are stateful — you define inbound rules to allow traffic in, and responses automatically flow back out. Network ACLs are stateless and require explicit rules for both directions. Both can silently block peering traffic.
For the receiving instance, check its security group inbound rules. You need an inbound rule that allows traffic from the source IP or security group. If your source is in VPC A with CIDR 10.0.0.0/16, add an inbound rule like this:
- Type: All Traffic (or TCP for specific ports)
- Protocol: TCP (or All)
- Port Range: 443, 3306, whatever your application needs
- Source: 10.0.0.0/16 — the remote VPC CIDR — or sg-xxxxx (remote security group)
Using the CLI: aws ec2 authorize-security-group-ingress --group-id sg-xxxxx --protocol tcp --port 443 --cidr 10.0.0.0/16 --region us-east-1
For outbound traffic from the source instance, most security groups have a default allow-all rule. But if you’ve customized outbound rules, verify they include the destination.
Network ACLs are the sneaky culprit here. They’re stateless, meaning you need rules in both directions. Check the NACL associated with each subnet involved in peering. Go to VPC → Network ACLs, select the NACL, and verify inbound and outbound rules. You need something like:
- Inbound: Allow traffic from the remote CIDR
- Outbound: Allow traffic to the remote CIDR
Rule numbers matter — lower numbers take precedence. Add rules with rule numbers like 100, 110, 120 to leave space for future additions. The default ALLOW ALL rules are usually rule 32767 and act as a catch-all.
Example NACL rules for peering:
- Inbound Rule 100: Protocol TCP, Port Range 0–65535, Source 10.1.0.0/16, Allow
- Outbound Rule 100: Protocol TCP, Port Range 0–65535, Destination 10.1.0.0/16, Allow
If you’re not sure which NACL applies to a subnet, check the subnet details in the VPC console and look for the NACL association.
Step 3 — Validate DNS Resolution Across VPCs
This is where peering actually breaks for most applications. The network connectivity works fine, but DNS doesn’t resolve private hostnames across account boundaries.
By default, VPCs don’t share Route 53 private hosted zones (PHZ) across accounts. If your application tries to reach a service in the peered VPC using a private DNS name like database.example.internal, the query fails — the receiving VPC doesn’t know about that zone.
First, verify DNS settings are enabled on both VPCs. In the VPC console, select your VPC, go to Actions → Edit DNS Resolution and DNS Hostnames. Both should be enabled (set to Yes). This seems obvious but I’ve inherited VPCs where someone disabled DNS resolution to “save costs” or troubleshoot something and forgot to re-enable it.
Next, test DNS resolution from an EC2 instance in VPC A trying to resolve a hostname in VPC B. SSH into the instance and run:
nslookup private-hostname.example.internal
or
dig private-hostname.example.internal +short
If this fails, the issue is DNS — not network connectivity. For cross-account peering, you’ve got three options:
- Share the private hosted zone from account B with account A using Route 53 zone sharing
- Set up conditional forwarding from account A’s Route 53 resolver to account B’s resolver
- Use VPC Endpoints if you’re connecting to AWS services like RDS or S3
The cleanest approach for multi-account setups is Route 53 Resolver rules. Create a rule in account A that forwards queries for example.internal to the resolver IP in VPC B. This requires enabling the VPC Resolver endpoints in both VPCs, but it’s cleaner than zone sharing — especially for government and military networks where you want explicit query forwarding.
Step 4 — Test Connectivity and Troubleshoot Remaining Issues
Once routes, security groups, NACLs, and DNS are configured, test actual connectivity using VPC Flow Logs. This is the fastest way to see exactly where traffic is failing.
Enable VPC Flow Logs for the subnets involved. Go to VPC → Your VPCs, select your VPC, choose Flow Logs tab, and create a new flow log targeting CloudWatch Logs. Wait a minute for traffic to generate logs.
Query the logs in CloudWatch Insights:
fields @timestamp, srcaddr, dstaddr, srcport, dstport, action, protocol | filter dstaddr = "10.1.10.5" | stats count() by action
The action field shows ACCEPT or REJECT. If you see REJECT entries, that tells you exactly where traffic is being dropped. REJECT at the NACL level means a network ACL rule is blocking traffic. REJECT at the security group level means a security group rule is blocking traffic. If you see ACCEPT but connectivity still fails, the issue is likely DNS or the destination application isn’t listening on that port.
From the source EC2 instance, test with netcat or curl to verify the destination is actually listening:
nc -zv 10.1.10.5 443
This attempts a TCP connection to port 443 on the destination. If it succeeds, you get a success message. If it times out or gets refused, either the application isn’t running or there’s still a network block somewhere.
For peering failures in multi-account setups, double-check that the peering connection was accepted by both accounts. Sometimes the receiving account approves it but with different settings or tags, and the connection enters a degraded state.
Run this to validate peering is bidirectional:
aws ec2 describe-vpc-peering-connections --vpc-peering-connection-ids pcx-xxxxx --region us-east-1
Look for Status: active and make sure VpcPeeringConnectionOptions shows AcceptVpcPeeringConnection is true on both sides. If it’s false on one side, the accepting account used different options during acceptance — which can cause asymmetric connectivity.
Once connectivity is confirmed with netcat, test your actual application traffic. Most peering failures resolve by systematically checking these four areas: connection status and routes, security rules, DNS resolution, and VPC Flow Logs validation.
“`
Stay in the loop
Get the latest team aws updates delivered to your inbox.