AWS PrivateLink vs. VPC Peering: When to Use Each for EC2 Communication
Manish Kumar
Cloud & IT Infrastructure Consultant | Architecting Secure, Scalable Solutions for Digital Transformation
When architecting AWS environments, securely connecting EC2 instances across different VPCs is crucial. Two commonly used methods are AWS PrivateLink and VPC Peering. Both provide private network connectivity, but they serve different use cases.
In this blog, we’ll break down AWS PrivateLink and VPC Peering, highlight their differences, advantages, and limitations, and help you decide when to use each with real-world examples with terraform implementation too.?
1. Understanding VPC Peering
VPC Peering enables direct communication between two VPCs using AWS’s internal network. The traffic stays within AWS, reducing latency and improving security.
? Key Features:
How VPC Peering Works (Example)
Imagine you have two VPCs:
To allow the web app in VPC B to communicate with the API in VPC A:
Limitations of VPC Peering
? Not Scalable – You must manually set up peering for each new VPC (N-to-N relationship). ? No Transitive Routing – If you peer VPC A ? VPC B and VPC B ? VPC C, A cannot talk to C. ? Increased Complexity – Managing multiple peering connections becomes difficult as VPC count increases.
?
VPC Peering Implementation in Terraform
Scenario: You need to enable bidirectional private communication between two VPCs.
Terraform Code: VPC Peering Between Two VPCs
provider "aws" {
region = "us-east-1"
}
# Create VPC A
resource "aws_vpc" "vpc_a" {
cidr_block = "10.0.0.0/16"
tags = { Name = "VPC-A" }
}
# Create VPC B
resource "aws_vpc" "vpc_b" {
cidr_block = "192.168.0.0/16"
tags = { Name = "VPC-B" }
}
# Create a VPC Peering Connection
resource "aws_vpc_peering_connection" "peer_vpcs" {
vpc_id = aws_vpc.vpc_a.id
peer_vpc_id = aws_vpc.vpc_b.id
auto_accept = true
}
# Update route table for VPC A
resource "aws_route_table" "vpc_a_rt" {
vpc_id = aws_vpc.vpc_a.id
}
resource "aws_route" "vpc_a_to_b" {
route_table_id = aws_route_table.vpc_a_rt.id
destination_cidr_block = aws_vpc.vpc_b.cidr_block
vpc_peering_connection_id = aws_vpc_peering_connection.peer_vpcs.id
}
resource "aws_route_table_association" "vpc_a_assoc" {
subnet_id = aws_subnet.subnet_a.id
route_table_id = aws_route_table.vpc_a_rt.id
}
# Update route table for VPC B
resource "aws_route_table" "vpc_b_rt" {
vpc_id = aws_vpc.vpc_b.id
}
resource "aws_route" "vpc_b_to_a" {
route_table_id = aws_route_table.vpc_b_rt.id
destination_cidr_block = aws_vpc.vpc_a.cidr_block
vpc_peering_connection_id = aws_vpc_peering_connection.peer_vpcs.id
}
resource "aws_route_table_association" "vpc_b_assoc" {
subnet_id = aws_subnet.subnet_b.id
route_table_id = aws_route_table.vpc_b_rt.id
}
# Create Subnets for VPC A and VPC B
resource "aws_subnet" "subnet_a" {
vpc_id = aws_vpc.vpc_a.id
cidr_block = "10.0.1.0/24"
}
resource "aws_subnet" "subnet_b" {
vpc_id = aws_vpc.vpc_b.id
cidr_block = "192.168.1.0/24"
}
# Launch EC2 instance in VPC A
resource "aws_instance" "instance_a" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.subnet_a.id
tags = { Name = "Instance-VPC-A" }
}
# Launch EC2 instance in VPC B
resource "aws_instance" "instance_b" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.subnet_b.id
tags = { Name = "Instance-VPC-B" }
}
Deployment Steps
?
2. Understanding AWS PrivateLink
AWS PrivateLink allows services in one VPC to be securely accessed by other VPCs and AWS accounts via interface endpoints.
? Key Features:
?
How AWS PrivateLink Works (Example)
Imagine:
Instead of exposing an Internet-facing API, you:
?
Limitations of AWS PrivateLink
? One-way access – Consumer can talk to Provider, but not vice versa. ? Only TCP-based services – No support for ICMP, UDP, or VPC-to-VPC full networking. ? Additional cost – PrivateLink endpoints are charged per hour + data transfer fees.
?
AWS PrivateLink Implementation in Terraform
Scenario: You want to expose an internal service in one VPC (Provider) and allow another VPC (Consumer) to access it securely via PrivateLink.
Terraform Code: AWS PrivateLink Between Two VPCs
provider "aws" {
region = "us-east-1"
}
# Provider VPC
resource "aws_vpc" "provider_vpc" {
cidr_block = "10.1.0.0/16"
tags = { Name = "Provider-VPC" }
}
# Consumer VPC
resource "aws_vpc" "consumer_vpc" {
cidr_block = "10.2.0.0/16"
tags = { Name = "Consumer-VPC" }
}
# Create PrivateLink Endpoint Service (Provider Side)
resource "aws_security_group" "pl_sg" {
vpc_id = aws_vpc.provider_vpc.id
}
resource "aws_network_interface" "pl_interface" {
subnet_id = aws_subnet.provider_subnet.id
}
resource "aws_vpc_endpoint_service" "pl_service" {
acceptance_required = false
network_load_balancer_arns = [aws_lb.provider_nlb.arn]
}
# Consumer Endpoint (VPC Endpoint)
resource "aws_vpc_endpoint" "consumer_endpoint" {
vpc_id = aws_vpc.consumer_vpc.id
service_name = aws_vpc_endpoint_service.pl_service.service_name
vpc_endpoint_type = "Interface"
security_group_ids = [aws_security_group.pl_sg.id]
subnet_ids = [aws_subnet.consumer_subnet.id]
}
# Subnets
resource "aws_subnet" "provider_subnet" {
vpc_id = aws_vpc.provider_vpc.id
cidr_block = "10.1.1.0/24"
}
resource "aws_subnet" "consumer_subnet" {
vpc_id = aws_vpc.consumer_vpc.id
cidr_block = "10.2.1.0/24"
}
# Create EC2 in Provider VPC (Private Service)
resource "aws_instance" "provider_instance" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.provider_subnet.id
tags = { Name = "Private-API-Server" }
}
# Create EC2 in Consumer VPC (Accessing Service)
resource "aws_instance" "consumer_instance" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.consumer_subnet.id
tags = { Name = "Consumer-App" }
}
# Create a Network Load Balancer for PrivateLink
resource "aws_lb" "provider_nlb" {
name = "private-nlb"
internal = true
load_balancer_type = "network"
subnets = [aws_subnet.provider_subnet.id]
}
Deployment Steps
?
3. VPC Peering vs. AWS PrivateLink: Key Differences
?4. When to Use VPC Peering vs. PrivateLink?
? Use VPC Peering When:
?? You need full network access (e.g., EC2 instances communicating across VPCs).
?? Both VPCs belong to the same organization.
?? Low latency & high bandwidth are critical.
?? You don’t need multi-account access.
Example:
?
? Use AWS PrivateLink When:
?? You want to expose a service securely without Internet exposure.
?? You need multi-account or third-party access.
?? You want scalability without managing peering connections.
?? You’re connecting to AWS-managed services like S3, RDS, or Lambda privately.
Example:
5. Real-World Scenarios
Scenario 1: Multi-Region Microservices
A company runs two microservices in two different regions and needs them to communicate securely.
Solution: ? Use Inter-Region VPC Peering for direct, low-latency connectivity.
?
Scenario 2: SaaS Provider Exposing a Private API
A fintech company offers a private payment API to multiple clients across different AWS accounts.
Solution: ? Use AWS PrivateLink to provide secure, scalable access without Internet exposure.
?
Scenario 3: Hybrid Cloud with On-Premises Connectivity
An enterprise wants on-prem servers to access EC2 instances in AWS securely.
Solution: ? Use AWS Direct Connect with PrivateLink for secure, private communication.
?
Final Thoughts: Which One Should You Choose?
For most modern, multi-account, or SaaS architectures, AWS PrivateLink is the preferred solution due to its scalability, security, and ease of management.
Which method do you use in your AWS architecture? Let’s discuss in the comments! ??
?
?