AWS for DevOps — VPC Peering & Endpoints
4.5 — VPC Peering & Endpoints
VPC Peering connects two VPCs so they can communicate using private IPs. VPC Endpoints let your private subnets access AWS services (S3, DynamoDB, ECR) without going through the internet.
VPC Peering
VPC-A (10.0.0.0/16) VPC-B (10.1.0.0/16)
┌──────────────────┐ ┌──────────────────┐
│ App Servers │◄──Peering──►│ Shared Services │
│ 10.0.10.0/24 │ │ 10.1.10.0/24 │
└──────────────────┘ └──────────────────┘
- CIDRs must not overlap
- Traffic stays on AWS private network (never crosses the internet)
- Works across accounts and across regions
- Not transitive — if A peers with B and B peers with C, A cannot reach C through B
Creating a VPC Peering Connection
# Request peering (from VPC-A's account)
$ aws ec2 create-vpc-peering-connection \
--vpc-id vpc-a123 \
--peer-vpc-id vpc-b456 \
--peer-region ap-south-1
{
"VpcPeeringConnection": {
"VpcPeeringConnectionId": "pcx-abc123",
"Status": {"Code": "initiating-request"}
}
}
# Accept peering (from VPC-B's account, or same account)
$ aws ec2 accept-vpc-peering-connection \
--vpc-peering-connection-id pcx-abc123
# Add routes in BOTH VPCs
# VPC-A route table: send 10.1.0.0/16 traffic to the peering connection
$ aws ec2 create-route \
--route-table-id rtb-vpca \
--destination-cidr-block 10.1.0.0/16 \
--vpc-peering-connection-id pcx-abc123
# VPC-B route table: send 10.0.0.0/16 traffic to the peering connection
$ aws ec2 create-route \
--route-table-id rtb-vpcb \
--destination-cidr-block 10.0.0.0/16 \
--vpc-peering-connection-id pcx-abc123
Don’t forget: Update security groups in both VPCs to allow traffic from the peer CIDR.
VPC Endpoints
Without endpoints, an EC2 instance in a private subnet talks to S3 like this:
Private Subnet → NAT Gateway → IGW → Internet → S3
With a VPC Endpoint:
Private Subnet → VPC Endpoint → S3 (stays on AWS network)
Benefits: No NAT costs, lower latency, more secure (traffic never leaves AWS).
Two Types of VPC Endpoints
| Type | How It Works | Services |
|---|---|---|
| Gateway Endpoint | Route table entry | S3, DynamoDB |
| Interface Endpoint | ENI with private IP (powered by PrivateLink) | Almost everything else (ECR, CloudWatch, STS, SSM, etc.) |
Gateway Endpoint (for S3)
$ aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123 \
--service-name com.amazonaws.ap-south-1.s3 \
--route-table-ids rtb-prv123 rtb-prv456
{
"VpcEndpoint": {
"VpcEndpointId": "vpce-s3abc",
"ServiceName": "com.amazonaws.ap-south-1.s3",
"VpcEndpointType": "Gateway"
}
}
AWS automatically adds a route in the specified route tables:
Destination: pl-abc123 (S3 prefix list)
Target: vpce-s3abc
Gateway endpoints are free. Always create them for S3 and DynamoDB.
Interface Endpoint (for ECR, CloudWatch, etc.)
# Create an interface endpoint for ECR (Docker pull)
$ aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123 \
--service-name com.amazonaws.ap-south-1.ecr.dkr \
--vpc-endpoint-type Interface \
--subnet-ids subnet-prv1a subnet-prv1b \
--security-group-ids sg-endpoint123 \
--private-dns-enabled
{
"VpcEndpoint": {
"VpcEndpointId": "vpce-ecr123",
"VpcEndpointType": "Interface",
"DnsEntries": [
{"DnsName": "vpce-ecr123.ecr.dkr.ap-south-1.vpce.amazonaws.com"}
]
}
}
With --private-dns-enabled, the standard ECR DNS name (*.ecr.ap-south-1.amazonaws.com) resolves to the endpoint’s private IP. No code changes needed.
Endpoints Needed for ECS Fargate in a Private Subnet
ECS Fargate tasks in private subnets need these endpoints (or a NAT Gateway):
| Endpoint | Service Name | Why |
|---|---|---|
| ECR (API) | com.amazonaws.REGION.ecr.api |
Pull image metadata |
| ECR (Docker) | com.amazonaws.REGION.ecr.dkr |
Pull image layers |
| S3 (Gateway) | com.amazonaws.REGION.s3 |
ECR stores layers in S3 |
| CloudWatch Logs | com.amazonaws.REGION.logs |
Write container logs |
| SSM | com.amazonaws.REGION.ssm |
ECS Exec |
Endpoint Policies
You can restrict what a VPC endpoint allows:
{
"Statement": [
{
"Sid": "AllowSpecificBucket",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-deploy-bucket/*"
}
]
}
This ensures that even if an instance has broad S3 permissions, it can only access my-deploy-bucket through this endpoint.
Common Mistakes
| Mistake | Why It Hurts | Fix |
|---|---|---|
| Overlapping CIDRs between peered VPCs | Peering request rejected | Plan CIDR allocation before creating VPCs |
| Assuming peering is transitive | Traffic between A→C doesn’t flow through B | Create direct peering or use Transit Gateway |
| Not creating S3 gateway endpoint | All S3 traffic goes through NAT (costs money) | Always create free gateway endpoints |
| Forgetting security group for interface endpoint | Endpoint rejects connections | Allow port 443 from your subnets |
When to Use Which
| Scenario | Solution |
|---|---|
| Connect 2 VPCs | VPC Peering |
| Connect 10+ VPCs (hub-spoke) | Transit Gateway |
| S3 / DynamoDB from private subnet | Gateway Endpoint (free) |
| ECR / CloudWatch / SSM from private subnet | Interface Endpoint |
| Avoid NAT Gateway costs for AWS services | VPC Endpoints |
Quick Reference
| Task | Command |
|---|---|
| Create peering | aws ec2 create-vpc-peering-connection --vpc-id VPC_A --peer-vpc-id VPC_B |
| Accept peering | aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id PCX |
| Create gateway endpoint | aws ec2 create-vpc-endpoint --vpc-id VPC --service-name SVC --route-table-ids RT |
| Create interface endpoint | aws ec2 create-vpc-endpoint --vpc-id VPC --service-name SVC --vpc-endpoint-type Interface --subnet-ids SUBNET --security-group-ids SG |
| List endpoints | aws ec2 describe-vpc-endpoints --filters Name=vpc-id,Values=VPC |
Next → Networking Exercises