Secure, Private Connectivity for Your AWS Architecture
AWS PrivateLink is a highly available, scalable technology that enables you to privately connect your VPC to supported AWS services, services hosted by other AWS accounts (VPC endpoint services), and supported AWS Marketplace partner services. It provides a secure way to access services without requiring the traffic to traverse the public internet.
Traffic never leaves the AWS network, eliminating exposure to internet-based threats
Reduced latency and increased bandwidth through private connectivity
No need for NAT gateways, internet gateways, or VPN connections
Reduce data transfer costs and eliminate NAT gateway charges
Consumer VPC: Your private network containing EC2 instances and Lambda functions that need to access services privately.
VPC Endpoint: The entry point in your VPC that enables private connectivity. Acts as a gateway for all PrivateLink traffic.
PrivateLink Connection: The secure, private tunnel that routes traffic without traversing the public internet.
Provider Side: Services hosted by other AWS accounts or third parties, fronted by Network Load Balancers for high availability.
AWS Services: Native AWS services like S3, DynamoDB, and EC2 API that can be accessed privately through PrivateLink.
VPC Endpoints come in two types:
Network Load Balancer Role: For custom endpoint services, the NLB acts as the front-end that receives traffic from PrivateLink and distributes it to your backend services. It operates at Layer 4 (TCP/UDP) and can handle millions of requests per second while maintaining ultra-low latencies.
Security Benefits: All communication flows through AWS's private network backbone, eliminating exposure to DDoS attacks, data interception, and other internet-based threats. Traffic is encrypted in transit and never touches public routing tables.
Multi-AZ Deployment: Interface endpoints can be deployed across multiple Availability Zones for high availability. Each AZ gets its own network interface, ensuring service continuity even if an entire AZ becomes unavailable.
Step 1-2: Your application makes an API call which is intercepted by the VPC Endpoint and routed through the private PrivateLink network.
Step 3-4: PrivateLink forwards the request to the Network Load Balancer, which distributes traffic across healthy service instances.
Step 5-8: The response follows the reverse path, ensuring all traffic remains within the AWS private network backbone.
Key Benefit: At no point does traffic traverse the public internet, providing enhanced security, performance, and reliability.
DNS Resolution Process: When your application makes a call to an AWS service (e.g., s3.amazonaws.com), the VPC's DNS resolver redirects the request to the VPC endpoint's private IP address instead of the public IP. This happens transparently without any code changes.
Connection Establishment: The VPC endpoint establishes a persistent connection to the PrivateLink service. This connection is multiplexed, meaning multiple requests from different sources can share the same underlying connection for efficiency.
Load Balancing Strategy: The Network Load Balancer uses flow hash algorithm (source IP, source port, destination IP, destination port, protocol) to ensure session affinity. This means requests from the same source will consistently route to the same backend target, which is crucial for stateful applications.
Health Checking: The NLB continuously monitors the health of backend targets using configurable health checks. Unhealthy targets are automatically removed from the rotation, and traffic is redistributed to healthy instances.
Performance Characteristics: PrivateLink typically reduces latency by 10-50ms compared to internet routing, depending on geographic location. Bandwidth is not limited by NAT gateway constraints, often resulting in 2-3x better throughput for data-intensive operations.
Failure Scenarios: If the primary AZ's endpoint becomes unavailable, traffic automatically fails over to endpoints in other AZs within seconds. The service maintains connection state and can resume operations transparently.
Securely connect services across different AWS accounts without exposing them to the internet. Perfect for organizations with separate development, staging, and production accounts.
Provide customers with private access to your SaaS application, enabling them to consume your services without internet exposure.
Connect analytics workloads to AWS services like S3, Redshift, or Kinesis privately, ensuring sensitive data never traverses the public internet.
Extend on-premises infrastructure to AWS services securely through AWS Direct Connect and PrivateLink.
A common enterprise scenario where the production database resides in a separate AWS account from the application servers for security and compliance reasons.
Application Account: Contains the web tier and application logic, isolated from sensitive data infrastructure.
Database Account: Houses the RDS instances and data infrastructure with strict access controls.
PrivateLink Connection: Enables private connectivity between accounts without VPC peering or transit gateways.
RDS Proxy: Provides connection pooling, failover handling, and enhanced security for database connections.
Security Benefits: Database traffic never leaves AWS backbone, supports fine-grained IAM controls, and maintains network isolation.
Amazon RDS Proxy is a fully managed database proxy service that sits between your applications and RDS databases. It's particularly powerful when combined with PrivateLink for cross-account or private database access scenarios.
Connection Pooling: Maintains a pool of database connections and multiplexes application requests, reducing connection overhead and improving performance.
Health Monitoring: Continuously monitors database health and automatically removes unhealthy instances from the connection pool.
Failover Management: Handles RDS failover scenarios transparently, maintaining application connections during database failover events.
IAM Integration: Supports IAM database authentication, eliminating the need to manage database passwords in application code.
Secrets Manager Integration: Automatically retrieves and rotates database credentials from AWS Secrets Manager.
Scalability: Supports thousands of concurrent connections while maintaining optimal database connection counts.
Availability: Reduces failover time from 2-3 minutes to under 30 seconds by maintaining persistent connections.
Security: Enforces SSL/TLS encryption and integrates with IAM for fine-grained access control.
Performance: Reduces database CPU utilization by up to 50% through connection reuse and query optimization.
Serverless Compatibility: Perfect for Lambda functions and other serverless workloads that create many short-lived connections.
# Step 1: Create IAM role for RDS Proxy
aws iam create-role \
--role-name rds-proxy-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "rds.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# Attach policy for Secrets Manager access
aws iam attach-role-policy \
--role-name rds-proxy-role \
--policy-arn arn:aws:iam::aws:policy/AmazonRDSProxyRole
# Step 2: Create database credentials in Secrets Manager
aws secretsmanager create-secret \
--name prod-db-credentials \
--description "Production database credentials" \
--secret-string '{
"username": "dbadmin",
"password": "SecurePassword123!",
"engine": "postgres",
"host": "prod-db.cluster-xyz.us-west-2.rds.amazonaws.com",
"port": 5432,
"dbname": "productiondb"
}'
IAM Role: RDS Proxy needs permissions to access Secrets Manager and RDS resources
Secrets Manager: Stores database credentials securely with automatic rotation capabilities
Policy Attachment: AmazonRDSProxyRole provides the minimum required permissions
# Step 3: Create RDS Proxy
aws rds create-db-proxy \
--db-proxy-name production-db-proxy \
--engine-family POSTGRESQL \
--auth '{
"AuthScheme": "SECRETS",
"SecretArn": "arn:aws:secretsmanager:us-west-2:123456789012:secret:prod-db-credentials-AbCdEf",
"IAMAuth": "REQUIRED"
}' \
--role-arn arn:aws:iam::123456789012:role/rds-proxy-role \
--vpc-subnet-ids subnet-db-1a subnet-db-1b subnet-db-1c \
--vpc-security-group-ids sg-rds-proxy \
--require-tls \
--idle-client-timeout 1800 \
--max-connections-percent 100 \
--max-idle-connections-percent 50
# Step 4: Register RDS target with proxy
aws rds register-db-proxy-targets \
--db-proxy-name production-db-proxy \
--target-group-name default \
--db-cluster-identifiers prod-db-cluster
--engine-family: Specifies database engine (MYSQL, POSTGRESQL, SQLSERVER)
IAMAuth REQUIRED: Enforces IAM authentication for enhanced security
--require-tls: Ensures all connections are encrypted
--max-connections-percent: Percentage of max_connections that proxy can use
--idle-client-timeout: Seconds before idle connections are closed (1800 = 30 minutes)
# Step 5: Create Network Load Balancer targeting RDS Proxy
# Get RDS Proxy endpoint
aws rds describe-db-proxies \
--db-proxy-name production-db-proxy \
--query 'DBProxies[0].Endpoint' \
--output text
# Create target group for RDS Proxy
aws elbv2 create-target-group \
--name rds-proxy-targets \
--protocol TCP \
--port 5432 \
--vpc-id vpc-database-account \
--target-type ip \
--health-check-protocol TCP \
--health-check-port 5432 \
--health-check-interval-seconds 30 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 2
# Register RDS Proxy IP with target group
# Note: Get RDS Proxy IP using: nslookup
aws elbv2 register-targets \
--target-group-arn arn:aws:elasticloadbalancing:us-west-2:222222222222:targetgroup/rds-proxy-targets/abcdef1234567890 \
--targets Id=10.0.1.100,Port=5432
Target Type IP: Allows targeting RDS Proxy by its private IP address
Health Check Settings: Configured for database-specific health monitoring
TCP Protocol: Uses TCP health checks suitable for database connections
Proxy IP Discovery: Use nslookup or dig to find the RDS Proxy's private IP address
This is a common question that deserves a detailed explanation. The Network Load Balancer isn't for traditional load balancing in this scenario - it serves as the required entry point for PrivateLink endpoint services.
AWS Requirement: VPC Endpoint Services can ONLY be created with Network Load Balancers as targets. You cannot create an endpoint service that directly targets RDS, EC2 instances, Lambda functions, or other AWS services.
Gateway Function: The NLB acts as a "gateway" or "proxy" that receives traffic from PrivateLink and forwards it to your actual service (RDS Proxy in this case).
Single Point of Entry: Even if you have only one backend service, the NLB provides a consistent entry point that PrivateLink can connect to.
Future Flexibility: The NLB allows you to add multiple targets, implement health checks, and provide high availability without changing the PrivateLink configuration.
Pattern 1 - Single Service: NLB forwards all traffic to one backend (our RDS Proxy example). Still provides health checking and consistent endpoint.
Pattern 2 - Load Distribution: NLB distributes traffic across multiple identical services for performance and availability.
Pattern 3 - Multi-Service: Single PrivateLink endpoint can expose multiple services on different ports through one NLB.
Key Insight: Even with one backend target, the NLB is mandatory for PrivateLink but provides valuable health monitoring and future scalability.
# Scenario 1: Simple pass-through NLB (our RDS Proxy case)
aws elbv2 create-load-balancer \
--name rds-proxy-gateway \
--type network \
--scheme internal \
--subnets subnet-db-1a subnet-db-1b
# Create listener that simply forwards traffic
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/net/rds-proxy-gateway/xyz \
--protocol TCP \
--port 5432 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/rds-proxy-targets/abc
--scheme internal: NLB only accessible within AWS network (required for PrivateLink)
TCP Protocol: Layer 4 forwarding with minimal processing overhead
Port Mapping: Client connects to port 5432 on NLB, traffic forwarded to port 5432 on RDS Proxy
No Load Balancing: Even with one target, NLB provides the required PrivateLink interface
# Scenario 2: Multi-port service exposure
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/net/multi-service-nlb/xyz \
--protocol TCP \
--port 5432 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/postgres-targets/abc
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/net/multi-service-nlb/xyz \
--protocol TCP \
--port 3306 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/mysql-targets/def
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/net/multi-service-nlb/xyz \
--protocol TCP \
--port 6379 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/redis-targets/ghi
Multiple Listeners: One NLB can expose multiple services on different ports through a single PrivateLink endpoint
Port-based Routing: Traffic to port 5432 goes to PostgreSQL, port 3306 to MySQL, port 6379 to Redis
Cost Efficiency: Single PrivateLink endpoint serves multiple services instead of creating separate endpoints
Simplified Management: Consumers only need to connect to one endpoint with different ports
# Check NLB health and status
aws elbv2 describe-load-balancers \
--load-balancer-arns arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/net/rds-proxy-gateway/xyz
# Monitor target health
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/rds-proxy-targets/abc
# View NLB metrics in CloudWatch
aws cloudwatch get-metric-statistics \
--namespace AWS/NetworkELB \
--metric-name ActiveFlowCount \
--dimensions Name=LoadBalancer,Value=net/rds-proxy-gateway/xyz \
--start-time 2025-01-01T00:00:00Z \
--end-time 2025-01-01T01:00:00Z \
--period 300 \
--statistics Average
Load Balancer Status: Verify NLB is active and healthy across all AZs
Target Health: Confirm backend services (RDS Proxy) are passing health checks
Flow Metrics: Monitor active connections and data transfer through the NLB
Performance Insight: NLB adds minimal latency (<1ms) while providing the required PrivateLink interface
# Configure IAM database authentication
aws rds modify-db-proxy \
--db-proxy-name production-db-proxy \
--auth '{
"AuthScheme": "SECRETS",
"SecretArn": "arn:aws:secretsmanager:us-west-2:123456789012:secret:prod-db-credentials-AbCdEf",
"IAMAuth": "REQUIRED",
"ClientPasswordAuthType": "POSTGRES_SCRAM_SHA_256"
}'
# Create IAM policy for database access
aws iam create-policy \
--policy-name DatabaseAccessPolicy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "rds-db:connect",
"Resource": "arn:aws:rds-db:us-west-2:123456789012:dbuser:prod-db-proxy/*"
}]
}'
# Example application connection with IAM auth
# Python boto3 example:
# import boto3
# import psycopg2
#
# rds_client = boto3.client('rds')
# token = rds_client.generate_db_auth_token(
# DBHostname='production-db-proxy.proxy-xyz.us-west-2.rds.amazonaws.com',
# Port=5432,
# DBUsername='app_user'
# )
#
# conn = psycopg2.connect(
# host='production-db-proxy.proxy-xyz.us-west-2.rds.amazonaws.com',
# port=5432,
# database='productiondb',
# user='app_user',
# password=token,
# sslmode='require'
# )
IAM Authentication: Applications use IAM credentials instead of database passwords
Token-Based Access: 15-minute authentication tokens eliminate long-lived passwords
SCRAM Authentication: Advanced password hashing for PostgreSQL connections
Fine-Grained Permissions: IAM policies control database user access at the resource level
Audit Trail: All database connections are logged through CloudTrail with IAM user identity
# Monitor RDS Proxy performance
aws logs create-log-group \
--log-group-name /aws/rds/proxy/production-db-proxy
# Enable detailed monitoring
aws rds modify-db-proxy \
--db-proxy-name production-db-proxy \
--debug-logging
# CloudWatch metrics to monitor:
# - DatabaseConnections
# - DatabaseConnectionsCurrentlyBorrowed
# - DatabaseConnectionsCurrentlyInTransaction
# - DatabaseConnectionsCurrentlySessionPinned
# - MaxDatabaseConnectionsAllowed
# - QueryDatabaseResponseLatency
# - QueryRequestsNoTLS
Connection Metrics: Track active, borrowed, and pinned connections for optimization
Latency Monitoring: Measure query response times through the proxy
Security Monitoring: Alert on non-TLS connections for security compliance
Debug Logging: Detailed logs for troubleshooting connection and query issues
Capacity Planning: Monitor connection utilization to plan scaling activities
# Database Account (Provider): Create NLB targeting RDS Proxy
aws elbv2 create-load-balancer \
--name database-access-nlb \
--type network \
--scheme internal \
--subnets subnet-db-1a subnet-db-1b \
--security-groups sg-database-nlb
# Create target group for RDS Proxy
aws elbv2 create-target-group \
--name rds-proxy-targets \
--protocol TCP \
--port 5432 \
--vpc-id vpc-database-account \
--target-type ip \
--health-check-protocol TCP \
--health-check-port 5432
--scheme internal: NLB is only accessible within AWS network, not from internet
--target-type ip: Allows targeting RDS Proxy by IP address for flexibility
--health-check-protocol TCP: Simple TCP health checks for database connectivity
# Register RDS Proxy with target group
aws elbv2 register-targets \
--target-group-arn arn:aws:elasticloadbalancing:us-west-2:222222222222:targetgroup/rds-proxy-targets/abcdef1234567890 \
--targets Id=10.0.1.100,Port=5432
# Create VPC Endpoint Service
aws ec2 create-vpc-endpoint-service-configuration \
--network-load-balancer-arns arn:aws:elasticloadbalancing:us-west-2:222222222222:loadbalancer/net/database-access-nlb/xyz123 \
--acceptance-required \
--tag-specifications 'ResourceType=vpc-endpoint-service,Tags=[{Key=Name,Value=DatabaseService},{Key=Environment,Value=Production}]'
# Allow Application Account to connect
aws ec2 modify-vpc-endpoint-service-permissions \
--service-id vpce-svc-database123 \
--add-allowed-principals arn:aws:iam::111111111111:root
# Application Account (Consumer): Create VPC Endpoint
aws ec2 create-vpc-endpoint \
--vpc-id vpc-application-account \
--service-name com.amazonaws.vpce.us-west-2.vpce-svc-database123 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-app-1a subnet-app-1b \
--security-group-ids sg-database-client \
--policy-document file://database-endpoint-policy.json
--add-allowed-principals: Grants specific AWS account permission to create endpoints
Service Name Format: Uses the VPC endpoint service ID in the standard AWS service name format
Multi-Subnet Deployment: Ensures high availability across multiple AZs
A SaaS provider offering private API access to enterprise customers, eliminating internet exposure and providing better performance.
Customer Benefits: Private connectivity eliminates data egress charges, improves latency, and meets compliance requirements for data isolation.
Provider Benefits: Differentiates service offering, enables premium pricing for private access, and reduces support burden from connectivity issues.
Dual Access Patterns: API Gateway for REST APIs with built-in features, or direct service access for custom protocols and maximum performance.
Scalability: NLB can handle millions of requests per second, auto-scaling backend services based on demand.
# SaaS Provider: Create API Gateway VPC Link for private APIs
aws apigatewayv2 create-vpc-link \
--name saas-private-api-link \
--subnet-ids subnet-saas-1a subnet-saas-1b \
--security-group-ids sg-api-gateway-link
# Create private API Gateway
aws apigatewayv2 create-api \
--name saas-private-api \
--protocol-type HTTP \
--description "Private API for enterprise customers" \
--cors-configuration AllowOrigins="*",AllowMethods="GET,POST,PUT,DELETE",AllowHeaders="*"
VPC Link: Connects API Gateway to resources in your VPC privately
Protocol HTTP: Modern HTTP API with better performance and lower cost than REST API
CORS Configuration: Enables browser-based applications to access the API
# Create NLB for direct service access
aws elbv2 create-load-balancer \
--name saas-service-nlb \
--type network \
--scheme internal \
--subnets subnet-saas-1a subnet-saas-1b
# Create endpoint service for customer access
aws ec2 create-vpc-endpoint-service-configuration \
--network-load-balancer-arns arn:aws:elasticloadbalancing:us-west-2:444444444444:loadbalancer/net/saas-service-nlb/xyz789 \
--acceptance-required \
--supported-platforms EC2-VPC
# Set up customer-specific access control
aws ec2 modify-vpc-endpoint-service-permissions \
--service-id vpce-svc-saas123 \
--add-allowed-principals arn:aws:iam::333333333333:root \
--add-allowed-principals arn:aws:iam::555555555555:root
--acceptance-required: Allows manual approval of each customer connection for security
--supported-platforms EC2-VPC: Ensures compatibility with customer VPC environments
Granular Permissions: Each customer account is explicitly allowed, enabling audit trails and access control
# Customer Side: Create endpoint with custom DNS
aws ec2 create-vpc-endpoint \
--vpc-id vpc-customer-production \
--service-name com.amazonaws.vpce.us-west-2.vpce-svc-saas123 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-customer-1a subnet-customer-1b \
--security-group-ids sg-saas-client \
--private-dns-enabled \
--policy-document file://saas-access-policy.json
# Create Route 53 private zone for custom domain
aws route53 create-hosted-zone \
--name api.yourcompany.internal \
--vpc VPCRegion=us-west-2,VPCId=vpc-customer-production \
--caller-reference customer-saas-$(date +%s)
--private-dns-enabled: Enables automatic DNS resolution for AWS service endpoints
Custom Domain: Provides branded endpoint (api.yourcompany.internal) for easier application integration
Policy Control: Customer retains control over which services and actions are allowed through the endpoint
# Example saas-access-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalVpc": "vpc-customer-production"
},
"DateGreaterThan": {
"aws:CurrentTime": "2025-01-01T00:00:00Z"
},
"DateLessThan": {
"aws:CurrentTime": "2025-12-31T23:59:59Z"
}
}
}
]
}
VPC Restriction: Ensures only traffic from customer's VPC can use the endpoint
Time-based Access: Automatically expires access based on contract terms
Audit Trail: All access attempts are logged with source VPC information
Compliance: Meets enterprise requirements for network isolation and access control
# Create a VPC endpoint for S3
aws ec2 create-vpc-endpoint \
--vpc-id vpc-12345678 \
--service-name com.amazonaws.us-west-2.s3 \
--vpc-endpoint-type Gateway \
--route-table-ids rtb-12345678
--vpc-id: Specifies the VPC where the endpoint will be created
--service-name: The AWS service endpoint (format: com.amazonaws.region.service)
--vpc-endpoint-type: Gateway type for S3 and DynamoDB, Interface for other services
--route-table-ids: Route tables that will be updated with the endpoint routes
# Create an interface endpoint for EC2 API
aws ec2 create-vpc-endpoint \
--vpc-id vpc-12345678 \
--service-name com.amazonaws.us-west-2.ec2 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-12345678 subnet-87654321 \
--security-group-ids sg-12345678 \
--policy-document file://endpoint-policy.json \
--private-dns-enabled
--subnet-ids: Subnets where endpoint network interfaces will be created (use multiple for HA)
--security-group-ids: Security groups controlling access to the endpoint
--policy-document: IAM policy controlling which actions are allowed through the endpoint
--private-dns-enabled: Enables private DNS names for the endpoint
# Create endpoint-policy.json file
cat > endpoint-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeImages",
"ec2:DescribeSnapshots"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalVpc": "vpc-12345678"
}
}
}
]
}
EOF
This policy restricts endpoint access to specific EC2 actions and ensures only resources within your VPC can use the endpoint. The condition aws:PrincipalVpc ensures that only requests originating from the specified VPC are allowed.
# Create security group for VPC endpoint
aws ec2 create-security-group \
--group-name vpc-endpoint-sg \
--description "Security group for VPC endpoints" \
--vpc-id vpc-12345678
# Add inbound rule for HTTPS traffic
aws ec2 authorize-security-group-ingress \
--group-id sg-12345678 \
--protocol tcp \
--port 443 \
--cidr 10.0.0.0/16
Port 443: Required for HTTPS communication to AWS services
CIDR Block: Allow traffic from your VPC's CIDR range
Protocol: TCP is required for HTTPS connections
Private DNS is a critical component of PrivateLink that enables seamless integration without code changes. Understanding how DNS resolution works in PrivateLink environments is essential for successful implementations.
Application Layer: Your code makes standard AWS API calls using public service names like s3.amazonaws.com - no code changes required.
VPC DNS Resolver: AWS-provided DNS resolver at VPC CIDR +2 address (e.g., 10.0.0.2) handles all DNS queries within your VPC.
Private DNS Decision: When private DNS is enabled, Route 53 Resolver intercepts queries for AWS service domains and redirects them to private IP addresses.
Private Hosted Zone: AWS automatically creates and manages private hosted zones for each service endpoint, mapping service names to endpoint IPs.
Traffic Routing: With private DNS, traffic flows through the VPC endpoint; without it, traffic goes through the internet gateway.
Option 1 - AWS Managed: Zero configuration, works automatically with existing code. AWS creates private hosted zones and manages DNS records.
Option 2 - Custom Private DNS: Provides branded endpoints for better user experience and easier service discovery in enterprise environments.
Option 3 - Manual Management: Direct endpoint access, useful for troubleshooting or when you need explicit control over DNS resolution.
Best Practice: Use Option 1 for AWS services, Option 2 for custom endpoint services, and Option 3 for debugging only.
VPC DNS Resolver Check: Ensure your VPC uses AWS-provided DNS (enableDnsSupport and enableDnsHostnames both true).
Private DNS Verification: Confirm that --private-dns-enabled is set when creating interface endpoints.
Security Group Rules: DNS requires outbound access on port 53 (UDP/TCP) to the VPC resolver.
Route 53 Records: For custom domains, verify A/CNAME records point to the correct endpoint addresses.
DHCP Options: Custom DHCP option sets may override default DNS servers - ensure domain-name-servers points to VPC resolver.
# Enable DNS support and hostnames on VPC (prerequisite)
aws ec2 modify-vpc-attribute \
--vpc-id vpc-12345678 \
--enable-dns-support
aws ec2 modify-vpc-attribute \
--vpc-id vpc-12345678 \
--enable-dns-hostnames
# Create interface endpoint with private DNS enabled
aws ec2 create-vpc-endpoint \
--vpc-id vpc-12345678 \
--service-name com.amazonaws.us-west-2.s3 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-12345678 subnet-87654321 \
--security-group-ids sg-12345678 \
--private-dns-enabled \
--dns-options DnsRecordIpType=ipv4
--enable-dns-support: Enables AWS DNS resolver for the VPC at the reserved IP address (VPC CIDR +2)
--enable-dns-hostnames: Enables DNS hostnames for EC2 instances to resolve to private IP addresses
--private-dns-enabled: Creates private hosted zone entries for the AWS service domain
DnsRecordIpType: Specifies IPv4 records (ipv4), IPv6 records (ipv6), or both (dualstack)
# Create custom private hosted zone for endpoint service
aws route53 create-hosted-zone \
--name api.internal.company.com \
--vpc VPCRegion=us-west-2,VPCId=vpc-12345678 \
--caller-reference dns-setup-$(date +%s) \
--hosted-zone-config Comment="Private zone for internal API access",PrivateZone=true
# Get VPC endpoint DNS names
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids vpce-12345678 \
--query 'VpcEndpoints[0].DnsEntries[*].[DnsName,HostedZoneId]' \
--output table
# Create A record pointing to VPC endpoint
aws route53 change-resource-record-sets \
--hosted-zone-id Z1D633PJN98FT9 \
--change-batch '{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api.internal.company.com",
"Type": "A",
"AliasTarget": {
"DNSName": "vpce-12345678-abcdefg.s3.us-west-2.vpce.amazonaws.com",
"EvaluateTargetHealth": false,
"HostedZoneId": "Z7HUB22UULQXV"
}
}
}]
}'
Private Hosted Zone: Creates a DNS zone that's only accessible from within the associated VPC
Caller Reference: Unique identifier for the request, using timestamp ensures uniqueness
Alias Target: Points to the VPC endpoint using AWS Route 53 alias functionality for better performance
Hosted Zone ID: Use the zone ID from the VPC endpoint description for alias records
# Verify DNS resolution is working correctly # Run these commands from an EC2 instance in your VPC: # Test AWS service DNS resolution nslookup s3.amazonaws.com dig s3.amazonaws.com # Test custom domain resolution nslookup api.internal.company.com dig api.internal.company.com # Verify the resolver being used cat /etc/resolv.conf # Test connectivity to resolved IP curl -I https://s3.amazonaws.com telnet $(nslookup s3.amazonaws.com | grep Address | tail -1 | cut -d' ' -f2) 443
nslookup/dig: Verify that service domains resolve to private IP addresses (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
/etc/resolv.conf: Should show your VPC's DNS resolver IP address (typically VPC CIDR +2)
Connectivity Test: Confirms that the resolved IP addresses are reachable on the expected ports
Expected Results: Private IPs for AWS services indicate successful PrivateLink resolution
# Scenario 1: Multi-region endpoint with failover DNS
aws route53 change-resource-record-sets \
--hosted-zone-id Z1D633PJN98FT9 \
--change-batch '{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api-primary.company.com",
"Type": "A",
"SetIdentifier": "primary-us-west-2",
"Failover": "PRIMARY",
"TTL": 60,
"ResourceRecords": [{"Value": "10.0.1.100"}]
}
}, {
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api-primary.company.com",
"Type": "A",
"SetIdentifier": "secondary-us-east-1",
"Failover": "SECONDARY",
"TTL": 60,
"ResourceRecords": [{"Value": "10.1.1.100"}]
}
}]
}'
# Scenario 2: Weighted routing for A/B testing
aws route53 change-resource-record-sets \
--hosted-zone-id Z1D633PJN98FT9 \
--change-batch '{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api-test.company.com",
"Type": "A",
"SetIdentifier": "version-a",
"Weight": 80,
"TTL": 300,
"ResourceRecords": [{"Value": "10.0.1.100"}]
}
}, {
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api-test.company.com",
"Type": "A",
"SetIdentifier": "version-b",
"Weight": 20,
"TTL": 300,
"ResourceRecords": [{"Value": "10.0.1.101"}]
}
}]
}'
Failover Routing: Automatically routes traffic to secondary endpoint if primary becomes unhealthy
Weighted Routing: Distributes traffic between different endpoint versions for A/B testing or gradual rollouts
Health Checks: Can be combined with Route 53 health checks for automated failover based on endpoint health
TTL Considerations: Lower TTLs enable faster failover but increase DNS query load
# First, create a Network Load Balancer
aws elbv2 create-load-balancer \
--name my-private-service-nlb \
--type network \
--scheme internal \
--subnets subnet-12345678 subnet-87654321
# Create the endpoint service
aws ec2 create-vpc-endpoint-service-configuration \
--network-load-balancer-arns arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/net/my-private-service-nlb/abcdef1234567890 \
--acceptance-required \
--tag-specifications 'ResourceType=vpc-endpoint-service,Tags=[{Key=Name,Value=MyPrivateService}]'
--network-load-balancer-arns: ARN of the NLB that will handle traffic
--acceptance-required: Requires manual approval for endpoint connections
--scheme internal: NLB is only accessible within AWS network
# Allow specific AWS accounts to create endpoints
aws ec2 modify-vpc-endpoint-service-permissions \
--service-id vpce-svc-12345678 \
--add-allowed-principals arn:aws:iam::111122223333:root \
--add-allowed-principals arn:aws:iam::444455556666:root
# Create endpoint in consumer account
aws ec2 create-vpc-endpoint \
--vpc-id vpc-consumer123 \
--service-name com.amazonaws.vpce.us-west-2.vpce-svc-12345678 \
--vpc-endpoint-type Interface \
--subnet-ids subnet-consumer123
Service Provider: Grants permission to specific AWS accounts
Service Consumer: Creates endpoint using the service name format
The service name follows the pattern: com.amazonaws.vpce.region.vpce-svc-xxxxx
# Create Route 53 private hosted zone
aws route53 create-hosted-zone \
--name myservice.internal \
--vpc VPCRegion=us-west-2,VPCId=vpc-12345678 \
--caller-reference $(date +%s)
# Create DNS record pointing to VPC endpoint
aws route53 change-resource-record-sets \
--hosted-zone-id Z1234567890 \
--change-batch file://dns-change.json
# dns-change.json content
{
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "api.myservice.internal",
"Type": "A",
"AliasTarget": {
"DNSName": "vpce-12345678-abcdefg.ec2.us-west-2.vpce.amazonaws.com",
"EvaluateTargetHealth": false,
"HostedZoneId": "Z1HI4U5UVQT65I"
}
}
}
]
}
Creates a private hosted zone for internal service discovery. The alias record points your custom domain to the VPC endpoint DNS name, enabling easy service discovery within your VPC.
# Create IAM role for Flow Logs
aws iam create-role \
--role-name flowlogsRole \
--assume-role-policy-document file://flowlogs-trust-policy.json
# Create flow logs for VPC endpoint monitoring
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-12345678 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name VPCFlowLogs \
--deliver-logs-permission-arn arn:aws:iam::123456789012:role/flowlogsRole
VPC Flow Logs help monitor traffic to and from your VPC endpoints, enabling you to troubleshoot connectivity issues and analyze traffic patterns.
# Create CloudWatch alarm for endpoint connectivity
aws cloudwatch put-metric-alarm \
--alarm-name "VPCEndpoint-HighErrorRate" \
--alarm-description "VPC Endpoint error rate is high" \
--metric-name ClientErrorCount \
--namespace AWS/PrivateLink \
--statistic Sum \
--period 300 \
--threshold 10 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 2
This alarm monitors client errors to the VPC endpoint. Key metrics to monitor include ClientErrorCount, PacketDropCount, and ConnectionCount for comprehensive endpoint health monitoring.
# Check endpoint status and configuration
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids vpce-12345678
# Verify route table entries
aws ec2 describe-route-tables \
--filters "Name=vpc-id,Values=vpc-12345678"
# Test connectivity from EC2 instance
# Run on EC2 instance:
nslookup s3.amazonaws.com
curl -I https://s3.amazonaws.com
# Check security group rules
aws ec2 describe-security-groups \
--group-ids sg-12345678
1. Verify Endpoint Status: Ensure the endpoint is in "available" state
2. Check Route Tables: Confirm routes are properly configured
3. Test DNS Resolution: Verify private DNS is working correctly
4. Security Groups: Ensure proper inbound/outbound rules