AWS VPC Peering Step by Step - Connect Two VPCs (Same or Different Account and Region), Accept the Request, Add Routes, Security Groups, DNS Resolution, Test with EC2, and the Limits (AWS Part-12)


In Part-5 we built one VPC. Real accounts have several - a dev VPC and a prod VPC, a VPC per team, a shared-services VPC with the monitoring and the directory - and sooner or later an instance in one needs to talk to a database in another privately, without going out to the internet and back in. The simplest way to do that is VPC peering.

This is Part-12. We peer two VPCs, do the two things everybody forgets (routes on both sides, security groups on both sides), test with EC2, and then look at cross-account and cross-region peering, DNS, the limits that make peering stop scaling, and the point where Transit Gateway, Part-13 takes over. I also have the Google Cloud version of this - GCP VPC peering - if you work on both clouds; the concepts map almost one to one.

Table of Content

  1. What VPC peering is and how it works
  2. Prerequisites - two VPCs with non-overlapping CIDRs
  3. Step 1 - Create the peering connection (requester)
  4. Step 2 - Accept the peering connection (accepter)
  5. Step 3 - Add routes in both VPCs
  6. Step 4 - Open the security groups
  7. Step 5 - Enable DNS resolution over the peering
  8. Step 6 - Test it with EC2
  9. Cross-account and cross-region peering
  10. The rules - not transitive, no edge-to-edge routing, quotas
  11. What it costs
  12. The AWS CLI equivalents
  13. Common VPC peering errors and how to fix them
  14. Peering vs Transit Gateway - when to switch
  15. Conclusion



1. What VPC peering is and how it works

A VPC peering connection is a networking connection between two VPCs that lets them route traffic to each other using private IPv4 or IPv6 addresses, as if they were in the same network. From the how it works page -

  1. The owner of the requester VPC sends a request to the owner of the accepter VPC.
  2. The accepter accepts it - the connection becomes Active.
  3. Each side adds a route in its route tables pointing the other VPC's CIDR at the peering connection.
  4. Security groups are updated so that traffic from the peer is allowed.
  5. Optionally, DNS resolution is enabled so that public hostnames resolve to private IPs across the peering.

The traffic never leaves the AWS network, there is no gateway, VPN device or physical hardware in the path - the docs note no single point of failure for communication or a bandwidth bottleneck - and between regions it is encrypted.

VPC peering - two VPCs with non-overlapping CIDRs, routes on both sides, and why it is not transitive


2. Prerequisites - two VPCs with non-overlapping CIDRs

The one hard requirement - the two VPCs must not have overlapping CIDR blocks. 10.0.0.0/16 and 10.0.0.0/16 cannot be peered, ever; 10.0.0.0/16 and 10.0.128.0/17 cannot either. If both VPCs have multiple CIDRs, none may overlap. This is why planning CIDRs in Part-5 mattered - and why every account that used the default VPC's 172.31.0.0/16 for real workloads eventually hits this wall.

For this tutorial -

VPCCIDRRole
jhooq-vpc (VPC A)10.0.0.0/16requester - the VPC from Part-5, with an EC2 instance in jhooq-private-1a
jhooq-data-vpc (VPC B)10.1.0.0/16accepter - create it with VPC and more, 2 AZs, 0 public subnets, 2 private subnets, no NAT, and launch a t3.micro in a private subnet

Both in eu-central-1, same account, to start. (Cross-account and cross-region in section 9.)



3. Step 1 - Create the peering connection (requester)

VPC → Peering connections → Create peering connection (docs) -

  1. Name - jhooq-vpc-to-data-vpc.
  2. VPC ID (Requester) - jhooq-vpc.
  3. Select another VPC to peer with → Account - My account (or Another account and the 12-digit account ID).
  4. Region - This Region (or Another Region and pick it).
  5. VPC ID (Accepter) - jhooq-data-vpc. Within the same account and region the dropdown lists your VPCs; for another account you type the VPC ID.
  6. Create peering connection.

The status is Pending acceptance. The requester can still delete the request at this point; if nobody acts, it expires after 7 days.


4. Step 2 - Accept the peering connection (accepter)

Same account - select the connection → Actions → Accept request → Accept request. Another account - the accepter's admin opens Peering connections in their console (in the accepter's region), finds the pending request with your account ID, and accepts it. Status goes Provisioning → Active.

The console then shows a helpful banner - Modify my route tables now - which takes you to the next step. The connection ID (pcx-0123456789abcdef0) is what the routes will point to.


5. Step 3 - Add routes in both VPCs

An Active peering connection carries no traffic until both route tables know about it - the step most people do on one side only.

In VPC A (jhooq-vpc) - Route tables → jhooq-private-rt → Routes → Edit routes → Add route - Destination 10.1.0.0/16, Target → Peering Connection → pcx-0123.... Save. Repeat for jhooq-public-rt if public-subnet instances also need to reach VPC B.

110.0.0.0/16   local
20.0.0.0/0     nat-...
310.1.0.0/16   pcx-0123456789abcdef0

In VPC B (jhooq-data-vpc) - its private route table(s): Destination 10.0.0.0/16, Target pcx-0123....

110.1.0.0/16   local
210.0.0.0/16   pcx-0123456789abcdef0

You can route a subset instead of the whole CIDR - 10.0.11.0/24 only - to let just one subnet of A reach B. Routing uses the most specific match, so a /24 route beats the /16; the routing configurations page has the patterns. For cross-region peering, add the routes in each region's console.



6. Step 4 - Open the security groups

Routes get the packet to the subnet; the security group on the destination instance decides whether it gets in. On the database instance in VPC B, add an inbound rule -

  • Type PostgreSQL (5432) or SSH (22) for the test, Source 10.0.0.0/16 - or narrower, 10.0.11.0/24.
  • Same region only - you can instead reference the peer VPC's security group ID as the source (sg-0abc... /111111111111 in another account). The security groups page confirms this works within one region; across regions you must use CIDRs.

For ping tests also allow All ICMP - IPv4 from the peer CIDR. The security group on the source instance allows all outbound by default, so nothing to do there. Also check network ACLs if you customised them - the default allows everything.


7. Step 5 - Enable DNS resolution over the peering

By default, if an instance in VPC A resolves the public DNS name of an instance in VPC B (ec2-3-70-1-2.eu-central-1.compute.amazonaws.com), it gets the public IP - and the traffic would try to go via the internet, not the peering. Select the connection → Actions → Edit DNS settings → tick Requester DNS resolution and Accepter DNS resolution → Save. Now those hostnames resolve to private IPs on both sides. Both VPCs need DNS hostnames and DNS resolution enabled (Part-5, Step 1). Each VPC owner enables their own side; in cross-account peering that is two people.


8. Step 6 - Test it with EC2

From the app instance in VPC A (reach it via the bastion or Session Manager as in Part-5) -

 1# the database instance's private IP in VPC B
 2ping -c 3 10.1.11.25
 3# PING 10.1.11.25 ... 64 bytes from 10.1.11.25: icmp_seq=1 ttl=64 time=0.6 ms
 4
 5# port level
 6nc -zv 10.1.11.25 5432
 7# Connection to 10.1.11.25 5432 port [tcp/postgresql] succeeded!
 8
 9# traceroute shows one hop - there is no gateway in between
10traceroute 10.1.11.25
11
12# DNS resolution over peering - the public hostname returns the private IP
13dig +short ec2-3-70-1-2.eu-central-1.compute.amazonaws.com
14# 10.1.11.25

If ping times out, go straight to section 13 - it is always a route missing on one side or a security group.



9. Cross-account and cross-region peering

Cross-account - identical steps; in Step 1 choose Another account and enter the account ID (and VPC ID). The accepter sees the request in their console. Both sides add routes and security group rules in their own account. This is how a vendor gives you private access to their service VPC, and how the member accounts of an organization share a central VPC - though for many accounts, Transit Gateway or AWS RAM-shared subnets scale better.

Cross-region (inter-region) - choose Another Region in Step 1. Traffic is encrypted on the AWS backbone, the MTU is 8,500 bytes instead of 9,001, you cannot reference peer security groups (CIDRs only), DNS resolution must be enabled explicitly even for RFC 1918 ranges, and you pay the inter-region data transfer rate. Useful for disaster recovery replication and for reaching a region-specific service privately.


10. The rules - not transitive, no edge-to-edge routing, quotas

Straight from the limitations -

  1. Not transitive. If A is peered with B and A with C, B cannot reach C through A. Each pair that needs to talk needs its own connection. Ten VPCs that all need to reach each other = 45 peering connections and 90 route table entries; that is the scaling wall.
  2. No edge-to-edge routing. B cannot use A's Internet Gateway, NAT Gateway, VPN, Direct Connect or S3 gateway endpoint. Peering connects VPC-to-VPC, nothing beyond.
  3. One connection per VPC pair, no overlapping CIDRs (any of them), no querying the peer's Amazon DNS server directly.
  4. Quotas - 50 active peering connections per VPC by default, adjustable up to 125; 25 outstanding requests per VPC (quotas).
  5. Lifecycle - initiating-request → pending-acceptance → provisioning → active, or failed, rejected, expired (7 days), deleted; failed/deleted connections stay visible for a while.
  6. Shared VPCs - only the VPC owner can create or accept peering, not participants.

11. What it costs

A peering connection itself is free - no hourly charge. You pay only for data transfer across it: traffic between the same AZ is free, traffic between AZs in the same region is charged at the inter-AZ rate ($0.01 per GB in each direction in most regions), and inter-region traffic at the inter-region data transfer rate (from $0.02 per GB). Compare that with Transit Gateway's per-attachment-hour plus per-GB processing charge - for two or three VPCs, peering is by far the cheapest option.



12. The AWS CLI equivalents

 1# 1. request (same account & region; add --peer-owner-id / --peer-region for cross-account / cross-region)
 2PCX=$(aws ec2 create-vpc-peering-connection --vpc-id vpc-0aaa --peer-vpc-id vpc-0bbb \
 3  --tag-specifications 'ResourceType=vpc-peering-connection,Tags=[{Key=Name,Value=jhooq-vpc-to-data-vpc}]' \
 4  --query VpcPeeringConnection.VpcPeeringConnectionId --output text)
 5
 6# 2. accept (run as the accepter, in the accepter's region)
 7aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id "$PCX"
 8
 9# 3. routes on both sides
10aws ec2 create-route --route-table-id rtb-privateA --destination-cidr-block 10.1.0.0/16 --vpc-peering-connection-id "$PCX"
11aws ec2 create-route --route-table-id rtb-privateB --destination-cidr-block 10.0.0.0/16 --vpc-peering-connection-id "$PCX"
12
13# 4. security group on the database instance
14aws ec2 authorize-security-group-ingress --group-id sg-0db --protocol tcp --port 5432 --cidr 10.0.0.0/16
15
16# 5. DNS resolution both ways
17aws ec2 modify-vpc-peering-connection-options --vpc-peering-connection-id "$PCX" \
18  --requester-peering-connection-options AllowDnsResolutionFromRemoteVpc=true \
19  --accepter-peering-connection-options AllowDnsResolutionFromRemoteVpc=true
20
21aws ec2 describe-vpc-peering-connections --vpc-peering-connection-ids "$PCX" --query 'VpcPeeringConnections[0].Status'

In Terraform: aws_vpc_peering_connection (and aws_vpc_peering_connection_accepter for cross-account, with a second provider alias as in Terraform and AWS multi-account setup), aws_route on both sides, aws_vpc_peering_connection_options for DNS.


13. Common VPC peering errors and how to fix them

1. The CIDR block ... overlaps with the CIDR block ... of the peer VPC / VpcPeeringConnectionAlreadyExists on create - Overlapping ranges, or a connection between these two VPCs already exists (maybe pending). Re-IP one VPC (new VPC with a different CIDR - CIDRs cannot be changed) or use a private NAT Gateway / Transit Gateway with NAT to bridge overlapping networks.

2. ping / nc times out although the connection is Active - 95%: a route is missing on one side, or the route is in the wrong route table (the subnet of the instance uses the main table, you edited the custom one). Check Subnet associations on both tables. Then the destination security group and NACL.

3. The connection stays Pending acceptance - Nobody accepted it; in cross-account peering the accepter must do it in their account and region. After 7 days it expires - create it again.

4. Failed state - Usually overlapping CIDRs discovered at provisioning or the peer VPC was deleted. The failed entry disappears after ~2 hours.

5. Instances resolve the peer's hostname to a public IP and traffic goes nowhere - DNS resolution over the peering is off (section 7), or DNS hostnames are disabled on one VPC.

6. You cannot reference a security group in another region / the SG dropdown does not show the peer's groups - Inter-region peering - use CIDRs. Same region but another account: type the ID as sg-0abc.../<account-id>.

7. VPC B cannot reach the internet / S3 through VPC A's NAT or endpoint - Edge-to-edge routing is not supported. Give B its own NAT Gateway / gateway endpoint, or centralise egress with a Transit Gateway.

8. Only some subnets can reach the peer - Each route table needs the route. Private and public subnets have different tables; add it to every table whose subnets need the peer.

9. Return traffic drops (one direction works) - An asymmetric route - the peer routes back via a more specific route to another target, or a NACL blocks the ephemeral return ports (NACLs are stateless).

10. VpcPeeringConnectionLimitExceeded - 50 active per VPC. Request an increase (max 125) or move to Transit Gateway.


14. Peering vs Transit Gateway - when to switch

VPC peeringTransit Gateway
Topologypoint-to-point, one connection per pairhub-and-spoke, one attachment per VPC
Transitivenoyes - and you control it with route tables
On-premises (VPN / Direct Connect) sharingno (no edge-to-edge)yes - attach once, every VPC can use it
Centralised egress / inspectionnoyes
Bandwidthno limit on the connectionup to 100 Gbps per VPC attachment per AZ
Costfree, data transfer onlyper attachment-hour + per GB processed
Setup for 3 VPCs3 connections, 6 routes3 attachments, 3 routes + TGW route table
Setup for 10 VPCs45 connections, 90 routes10 attachments

Rule of thumb from the field - two or three VPCs that need to talk: peer them. More than a handful, a shared on-premises connection, or any need for centralised firewalls or egress: Transit Gateway, which is exactly Part-13.


15. Conclusion

To summarise Part-12 -

  1. VPC peering privately connects two VPCs with non-overlapping CIDRs, in the same or another account or region, over the AWS backbone with no gateway in the path.
  2. The flow is request → accept → routes on both sides → security groups → DNS resolution - and the two things people forget are the route in the second VPC and the security group on the destination.
  3. It is not transitive and has no edge-to-edge routing - no sharing of the peer's Internet Gateway, NAT, VPN, Direct Connect or endpoints.
  4. The connection is free; you pay inter-AZ or inter-region data transfer only.
  5. For more than a few VPCs or a shared on-premises link, move to Transit Gateway.

The official references are the VPC Peering Guide, how VPC peering works, routing configurations and peering quotas. Next: AWS Transit Gateway, Part-13 - the hub that removes the 45-connection mesh.


AWS step by step series -

  1. Part-1 : AWS IAM user - create a user, group, policy, access keys and MFA
  2. Part-2 : AWS Organizations - multi-account setup, OUs and SCPs
  3. Part-3 : AWS assume IAM role - trust policy, switch role in console and CLI
  4. Part-4 : How to launch an EC2 instance - key pair, security group, SSH
  5. Part-5 : AWS VPC - public and private subnets, Internet Gateway, NAT Gateway, route tables
  6. Part-8 : EC2 launch template - versions, default version, source template, SSM parameter AMI
  7. Part-10 : EC2 Auto Scaling - launch template, Auto Scaling group, target tracking, ALB
  8. Part-11 : AWS WAF - web ACL, managed rules, rate limiting, geo blocking
  9. Part-12 : AWS VPC Peering - connect two VPCs, routes, security groups, DNS
  10. Part-13 : AWS Transit Gateway - hub-and-spoke for many VPCs and on-premises
  11. Part-14 : AWS NAT Gateway deep dive - public vs private, limits, cost, troubleshooting
  12. Part-15 : Amazon Route 53 - hosted zones, records, alias, routing policies, health checks
  13. Part-16 : AWS security groups - inbound and outbound rules, stateful, referencing, quotas
  14. Part-16 : AWS Certificate Manager - free TLS certificates for ALB, CloudFront and API Gateway
  15. Part-17 : AWS Lambda - function URLs, environment variables and layers
  16. Part-18 : Network Load Balancer - setup, and ALB vs NLB
  17. Part-19 : VPC endpoints - gateway and interface endpoints (PrivateLink) instead of NAT
  18. Part-20 : AWS PrivateLink - publish your own service with an endpoint service and NLB
  19. Part-20 : Amazon EBS volumes - types, attach, mount, resize, snapshots, encryption
  20. Part-21 : VPC Flow Logs - CloudWatch Logs, S3, record format, Logs Insights, Athena
  21. Part-21 : EC2 Spot Instances - pricing, interruptions, mixed instances groups
  22. Part-24 : AWS Control Tower - landing zone, controls, Account Factory, Identity Center

Networking fundamentals -

  1. What is a VPC and a subnet? AWS networking in five minutes
  2. What is CIDR? Calculate IP ranges for VPCs and subnets
  3. What is NAT? Static NAT, dynamic NAT and PAT explained

More AWS guides -

  1. What is AWS CloudFormation? Templates, stacks, change sets, drift, StackSets
  2. Learn AWS S3 - the complete course
  3. AWS API Gateway - REST API with Lambda, authorizers, Terraform
  4. AWS Advanced Networking Specialty (ANS-C01) - course companion
  5. AWS ECS and Fargate - how to deploy a Docker container
  6. AWS S3 - how to host a static website
  7. Terraform create EC2 instance on AWS
  8. Terraform AWS IAM - users, roles and policies
  9. Terraform and AWS multi-account setup
  10. Terraform - setting up an ALB and SSL

Posts in this series