Sitemap

AWS VPC Peering: Connect Two VPCs with a Private Link!

10 min readSep 11, 2025

As AWS environments grow, a single VPC is rarely enough. Applications may run in separate VPCs for security, project isolation, or organizational reasons. At this point, VPC Peering allows us to connect two VPCs over a secure, private link.

Why VPC Peering?

  • Low latency: Traffic flows through the AWS backbone, not over the internet.
  • Security: Communication happens entirely over private IPs.
  • Simplicity: You don’t need Transit Gateway or VPN — two VPCs can talk directly.

⚠️ Keep in mind:

  • No transit routing: If A→B and B→C exist, A cannot directly reach C.
  • CIDR blocks must not overlap.
  • Route tables must be updated on both sides.

What You Will Learn

In this lab:

  • Create two VPCs in the same region (us-east-1 / N. Virginia):
  • VPC-A: Public subnet with a bastion EC2.
  • VPC-B: Private subnet with no public access.
  • Build a VPC Peering Connection between them.
  • Connect from the bastion to the private EC2 using private IP and SSH.
  • Clean up resources to avoid unnecessary costs.

Scenario and Architecture

VPC-A (Public)

  • CIDR: 10.0.0.0/16
  • Public Subnet: 10.0.1.0/24
  • EC2: VPC-A_EC2
  • Has Public IP
  • Works as Bastion
  • SSH + HTTP open

VPC-B (Private)

  • CIDR: 20.0.0.0/16
  • Private Subnet: 20.0.0.0/24
  • EC2: VPC-B_EC2
  • No Public IP
  • Only internal SSH access

👉 Goal: SSH into the private EC2 in VPC-B through the bastion EC2 in VPC-A, using its private IP.

The architecture of the VPC peering connection we will establish between VPC-A (public subnet) and VPC-B (private subnet)

Step-by-step hands-on lab to connect a public bastion to a private EC2 with VPC Peering

Section 1 — VPC-A (Public) Setup

First, we will create VPC-A, which has internet access. In this VPC, we will set up a public subnet, attach an Internet Gateway for internet access, and finally launch a bastion EC2 instance. Through this bastion host, we will connect to the other VPC.

1. Create VPC

VPC →Your VPC → CreateVPC

  • Log in to the AWS Console.
  • Select the region N. Virginia (us-east-1).
  • From the menu, go to VPC → Your VPCs → Create VPC.
  • Option: VPC only
  • Name: VPC-A
  • IPv4 CIDR: 10.0.0.0/16
  • Leave the other settings as default and click Create VPC.
Press enter or click to view image in full size

From the list, select VPC-AActions → Edit VPC settings.
Check the boxes for Enable DNS resolution and Enable DNS hostnames, then click Save.

VPC-A is now ready. DNS resolution and hostnames settings are enabled.

2. Create Public Subnet

Go to Subnets → Create subnet.

Press enter or click to view image in full size
  • VPC ID: VPC-A
  • Subnet name: Public-Subnet-A
  • Availability Zone: No Preference

IPv4 subnet CIDR block : 10.0.1.0/24

Press enter or click to view image in full size

Leave the Availability Zone as default and click the Create subnet button.

3. Attach Internet Gateway

For the EC2 instance in the public subnet to access the internet, an Internet Gateway (IGW) is required.

Go to Internet Gateways → Create internet gateway

  • Name: IGW-A

After creating it, select Attach to VPC → VPC-A.

4. Create Public Route Table

We will create a route table to direct the traffic of the public subnet to the IGW.

Go to Route tables → Create route table

  • Name: PublicRT-A
  • VPC: VPC-A

After creating the route table, go to Subnet associations → select Public-Subnet-A and save.

Edit routes → Add route

Destination: 0.0.0.0/0 Target: IGW-A

5. Launch Bastion EC2

Now let’s create a test server inside the public subnet.

  • Region: us-east-1 (N. Virginia)
  • Go to Services → EC2 → Instances → Launch instances
  • Name and tags:
  • Name: VPC-A_EC2
  • Application and OS Images (AMI):
  • Quick Start → Amazon Linux 2023 (kernel-6.1)

Key Pair (login):

  • Select Create new key pairKey pair name: ec2_ssh_key
  • Type: RSA, File format: .pem → Click Create and select it.
Press enter or click to view image in full size

Save the key file to your computer.

Network settings → Edit:

  • VPC: VPC-A
  • Subnet: default (Public-Subnet-A should be pre-selected)
  • Auto-assign public IP: Enable

Firewall (SG): Create a new security group

  • Security group name: Public_EC2_SG
  • Description: Security group for public EC2

Rules:

  • SSH → Source: Anywhere (0.0.0.0/0)
  • HTTP → Source: Anywhere
Press enter or click to view image in full size

Advanced details:
User Data Script (optional, to display a simple web page):

#!/bin/bash
sudo dnf update -y
sudo dnf install httpd -y
systemctl start httpd
systemctl enable httpd
echo "<html><h1> Welcome to Tech Istanbul: VPC Peering Lab</h1></html>" > /var/www/html/index.html
Press enter or click to view image in full size

When you click the Launch instances button, your bastion server will be ready within a few minutes. Paste the Public IP into your browser to see the test page.

Press enter or click to view image in full size

Note down the IPv4 Public IP address of the EC2 instance.

Press enter or click to view image in full size

When you paste this IP into your browser, you should see the welcome page.

Press enter or click to view image in full size

Section 2: VPC-B (Private) Setup

Now we will create our second VPC, VPC-B. This VPC will contain only a private subnet, and the EC2 instance will not have a public IP. It will be accessible only from inside, through the bastion.

1. Create VPC

Go to VPC → Your VPCs → Create VPC.

  • Option: VPC only
  • Name: VPC-B
  • IPv4 CIDR: 20.0.0.0/16
  • Click Create VPC.
Press enter or click to view image in full size

✅ The second VPC is now ready.

From the list, select VPC-B → Actions → Edit VPC settings
Check Enable DNS resolution and Enable DNS hostnames, then click Save.

Press enter or click to view image in full size

2. Create Private Subnet

This subnet will not have internet access; it will be used only for internal communication.

Go to Subnets → Create Subnet.

  • VPC ID: VPC-B
  • Subnet name: Private-Subnet-B
  • Availability Zone: No Preference
  • IPv4 CIDR block: 20.0.0.0/24
  • Click Create subnet.
Press enter or click to view image in full size

3. Create Private Route Table

Go to Route tables → Create route table

  • Name: PrivateRT
  • VPC: VPC-B
Press enter or click to view image in full size

After creating the route table, go to Subnet associations → select Private-Subnet-B and save.
(Note: We do not add an IGW here because this subnet will not have internet access.)

Press enter or click to view image in full size

4. Launch Private EC2

Now we will launch an EC2 instance inside the private subnet without internet access.

  • Go to EC2 → Launch instances
  • Name: VPC-B_EC2
  • AMI: Amazon Linux 2023 (kernel-6.1)
Press enter or click to view image in full size

Network settings → Edit:

  • VPC: VPC-B
  • Subnet: varsayılan (Private_subnet_VPC-B)
  • Auto-assign public IP: Disable

Firewall (SG): Create a new security group

  • Name: Private_EC2_SG
  • Description: Security group for private EC2
  • Rule: SSHSource: Anywhere
Press enter or click to view image in full size

Click the Launch instance button and wait until the instance status becomes running.

Press enter or click to view image in full size

✅ This instance will receive only a Private IP.

Press enter or click to view image in full size

Section 3: Test Connectivity Between Two VPCs (Before Peering)

At this point, we have:

  • VPC-A_EC2 (Public bastion) → Has a public IP, accessible from the internet.
  • VPC-B_EC2 (Private) → Has only a private IP, no internet access.

👉 Our goal is to SSH into the private EC2 through the bastion using its private IP. But since peering is not yet configured, this connection will fail.

1. Connect to Bastion

First, connect to the bastion server (VPC-A_EC2) from your local machine via SSH:

chmod 400 ec2_ssh_key.pem
ssh -i ec2_ssh_key.pem ec2-user@<VPC-A_EC2_PUBLIC_IP>

👉 This command logs you into the bastion.

2. Copy the Key to Bastion

Since we will use the same key to connect to the private EC2, we need to copy the .pem file from our local machine to the bastion:

scp -i ec2_ssh_key.pem ec2_ssh_key.pem ec2-user@3.89.19.3:/home/ec2-user

After logging into the bastion via SSH, tighten the file permissions:

ssh -i ec2_ssh_key.pem ec2-user@3.89.19.3
Press enter or click to view image in full size
chmod 400 ec2_ssh_key.pem

3. Try Connecting to the Private EC2

Now, from the bastion, attempt to SSH into the private EC2 using its private IP (for example 20.0.1.25):

ssh -i ec2_ssh_key.pem ec2-user@20.0.1.37
Press enter or click to view image in full size

✅ At this point, the connection will fail, because there is no communication path (peering) between the two VPCs yet.

Section 4: Create VPC Peering and Update Route Tables

Now it’s time to build the bridge that will let the two VPCs communicate: the VPC Peering Connection.

In the AWS Console, go to VPC → Peering Connections → Create Peering Connection.

  • Peering connection name tag: VPC-A_to_VPC-B
  • VPC ID (Requester): VPC-A
  • VPC ID (Accepter): VPC-B
  • Region: Select the same region (e.g., us-east-1)
  • Account: Leave as default
Press enter or click to view image in full size

Click the Create Peering Connection button.

Press enter or click to view image in full size

🔸 The status will initially be Pending Acceptance.

2. Accept the Peering Request

In the VPC → Peering Connections menu, select the connection you created.
Click Actions → Accept request.
The peering status should now be Active.

Press enter or click to view image in full size

The status should be Active (refresh the page if you don’t see it).

Press enter or click to view image in full size

Even though the peering between VPC-A and VPC-B is active, traffic will only flow once routes are added.

3. Update Route Tables

Although the peering connection is active, we need to update the route tables on both VPCs to allow traffic.

Go to Route tables → PublicRT-A → Edit routes.

Press enter or click to view image in full size

PublicRT-A (VPC-A):

  • Destination: 20.0.0.0/16
  • Target: Peering connection (VPC-A_to_VPC-B)

Route tables→PrivateRT→edit routes

Press enter or click to view image in full size

PrivateRT-B (VPC-B):

  • Destination: 10.0.0.0/16
  • Target: Peering connection (VPC-A_to_VPC-B)

🔑 This way, traffic between the two VPCs will be routed in both directions.

✅ You can now SSH from the bastion in VPC-A to the private EC2 in VPC-B:

ssh -i ec2_ssh_key.pem ec2-user@20.0.0.37
Press enter or click to view image in full size

Section 5: Cleanup

Every resource we create in a lab environment is a real AWS resource, and they may start incurring costs within minutes if left running. Especially EC2 instances and VPC Peering Connections can generate unnecessary charges if they remain active for a long time.

Therefore, after completing the lab, follow these steps to clean up your environment:

  1. Terminate EC2 Instances
  • VPC-A_EC2 (public bastion)
  • VPC-B_EC2 (private server)
  1. Delete Subnets
  • Public-Subnet-A
  • Private-Subnet-B
  1. Delete Route Tables
  • PublicRT-A
  • PrivateRT
  1. Delete Internet Gateway
  • IGW-A
  1. Delete VPC Peering Connection
  • VPC-A_to_VPC-B
  1. Delete VPCs
  • VPC-A
  • VPC-B

🎯 Conclusion

In this lab, we learned:

  • How to establish secure communication between two VPCs using VPC Peering.
  • How to test SSH connectivity from Public Bastion → Private EC2.
  • That peering works only after updating both route tables.
  • How to clean up resources to avoid unexpected costs.

💡 Takeaway: VPC Peering is simple and effective for small/medium-scale setups. However, if you need to connect many VPCs, a more centralized solution like Transit Gateway is recommended.

--

--

No responses yet