Skip to main content
  1. Posts/

How AWS PrivateLink and Private DNS Actually Work

Every cloud security architecture starts with the same question: how do I connect to a service without going over the public internet? The answer is almost always PrivateLink + private DNS. But most engineers treat this as a checkbox — “enable PrivateLink, done.” This article explains what actually happens at the network and DNS level.

The Problem: Public Endpoints
#

By default, when your application connects to a cloud service (a database, an API, a SaaS platform), the traffic flow looks like this:

┌──────────────────────┐          PUBLIC INTERNET          ┌──────────────────────┐
│   Your VPC           │                                    │   Service Provider   │
│                      │                                    │                      │
│   ┌──────────┐       │    ┌─────────────────────────┐    │   ┌──────────┐       │
│   │ App      │───────┼───▶│  Internet Gateway        │───┼──▶│ Service  │       │
│   │ Server   │       │    │  Public IP routing       │    │   │ Endpoint │       │
│   └──────────┘       │    │  Anyone can reach this   │    │   └──────────┘       │
│                      │    └─────────────────────────┘    │                      │
└──────────────────────┘                                    └──────────────────────┘

Problems with this:

  • Traffic traverses the public internet — potentially inspectable, interceptable
  • The service endpoint has a public IP — visible to anyone, attackable
  • Your VPC needs an Internet Gateway or NAT — broader attack surface
  • Compliance frameworks (DORA, NIS2, PCI-DSS) may prohibit public internet paths for sensitive data

What PrivateLink Does#

PrivateLink creates a private, one-way connection between your VPC and a service provider’s VPC — without either side exposing anything to the public internet.

┌──────────────────────┐                                    ┌──────────────────────┐
│   Your VPC           │         AWS Backbone (private)     │   Provider VPC       │
│                      │                                    │                      │
│   ┌──────────┐       │    ┌─────────────────────────┐    │   ┌──────────┐       │
│   │ App      │───────┼───▶│  VPC Endpoint (ENI)      │    │   │ Service  │       │
│   │ Server   │       │    │  10.0.1.47               │────┼──▶│ (NLB)    │       │
│   └──────────┘       │    │  Private IP in YOUR VPC  │    │   └──────────┘       │
│                      │    └─────────────────────────┘    │                      │
└──────────────────────┘                                    └──────────────────────┘
                                  No Internet Gateway
                                  No public IP
                                  No NAT required

How It Works Step by Step
#

  1. Service provider creates a VPC Endpoint Service backed by a Network Load Balancer (NLB)
  2. You create a VPC Interface Endpoint in your VPC, specifying the service
  3. AWS provisions an Elastic Network Interface (ENI) in your subnet with a private IP from your CIDR
  4. Traffic from your app → the ENI → AWS internal backbone → provider’s NLB → their service
  5. The connection is unidirectional — the provider cannot initiate connections back into your VPC

Key insight: the endpoint gets an IP address from your own VPC’s subnet. From your application’s perspective, the service looks like it’s running inside your network.

Interface Endpoint vs Gateway Endpoint
#

AWS has two types:

Interface Endpoint (PrivateLink)Gateway Endpoint
MechanismENI with private IP in your subnetRoute table entry
ServicesAlmost everything (400+ services, third-party SaaS)S3 and DynamoDB only
CostPer-hour + per-GB data processingFree
DNSPrivate DNS zonesPrefix lists in route tables
Cross-regionNo (same region)No (same region)

For this article, we focus on Interface Endpoints — they’re what people mean when they say “PrivateLink.”

The DNS Layer: Making It Transparent
#

Creating a PrivateLink endpoint gives you a private IP. But your application connects to service.example.com, not 10.0.1.47. How does the DNS hostname resolve to the private endpoint instead of the public IP?

This is where Private DNS comes in.

Without Private DNS
#

Your App                        Public DNS (Route 53)
   │                                   │
   │  "What is the IP of              │
   │   analytics.snowflake.com?"       │
   │──────────────────────────────────▶│
   │                                   │
   │  "52.xx.xx.xx" (public IP)       │
   │◀──────────────────────────────────│
   │                                   │
   │  Connect to 52.xx.xx.xx          │
   │  ──── goes over the internet ──▶  │

Even with PrivateLink configured, your app still resolves the public IP and bypasses the endpoint entirely.

With Private DNS Enabled
#

When you enable Private DNS on an Interface Endpoint, AWS automatically:

  1. Creates a Private Hosted Zone (PHZ) for the service’s domain
  2. Associates the PHZ with your VPC
  3. Adds DNS records pointing the service hostname to your endpoint’s private IPs
Your App                        Private Hosted Zone (in your VPC)
   │                                   │
   │  "What is the IP of              │
   │   analytics.snowflake.com?"       │
   │──────────────────────────────────▶│
   │                                   │
   │  "10.0.1.47" (private endpoint)  │  ← PHZ intercepts before public DNS
   │◀──────────────────────────────────│
   │                                   │
   │  Connect to 10.0.1.47            │
   │  ──── stays inside AWS ─────────▶ │

The application code doesn’t change. Same hostname, same port, same TLS certificate. But the traffic now flows privately.

How DNS Resolution Order Works
#

When an instance in your VPC makes a DNS query, the resolution order is:

1. VPC DNS Resolver (AmazonProvidedDNS at VPC CIDR + 2)
   ├── Check Private Hosted Zones associated with this VPC
   │   └── Match found? → Return private IP (done)
   ├── Check Route 53 Resolver Rules (forwarding rules)
   │   └── Rule matches? → Forward to on-prem DNS / custom resolver
   └── No match? → Forward to public DNS
       └── Return public IP

The Private Hosted Zone always wins over public DNS for any domain it covers. This is how PrivateLink transparently redirects traffic without application changes.

The Full Picture: PrivateLink + DNS Together#

Let’s trace a complete request from application to service:

┌─────────────────────────────────────────────────────────────────────────────┐
│  Your VPC (10.0.0.0/16)                                                     │
│                                                                              │
│  ┌──────────┐    ① DNS Query           ┌──────────────────────────────┐    │
│  │          │──────────────────────────▶│  VPC DNS Resolver            │    │
│  │  App     │                           │  (10.0.0.2)                  │    │
│  │  Server  │    ② Response: 10.0.1.47 │                              │    │
│  │          │◀──────────────────────────│  Checks Private Hosted Zone  │    │
│  │  10.0.0.5│                           │  → finds endpoint IP         │    │
│  └────┬─────┘                           └──────────────────────────────┘    │
│       │                                                                      │
│       │ ③ TCP connect to 10.0.1.47                                          │
│       │                                                                      │
│  ┌────▼──────────────────────────┐                                          │
│  │  VPC Interface Endpoint (ENI) │                                          │
│  │  10.0.1.47                    │                                          │
│  │  Subnet: 10.0.1.0/24         │                                          │
│  └────────────┬──────────────────┘                                          │
│               │                                                              │
└───────────────┼──────────────────────────────────────────────────────────────┘
                │ ④ AWS internal backbone (never touches the internet)
┌───────────────┼──────────────────────────────────────────────────────────────┐
│  Provider VPC │                                                              │
│  ┌────────────▼──────────────────┐     ┌─────────────────┐                 │
│  │  Network Load Balancer        │────▶│  Service         │                 │
│  │  (Endpoint Service)           │     │  (your data)     │                 │
│  └───────────────────────────────┘     └─────────────────┘                 │
└──────────────────────────────────────────────────────────────────────────────┘

Steps:

  1. App queries DNS for service.example.com
  2. VPC resolver checks the Private Hosted Zone → returns 10.0.1.47
  3. App connects to 10.0.1.47 — a local ENI in the same subnet
  4. Traffic flows over AWS backbone to the provider’s NLB

Zero public internet exposure. The app, the endpoint, and the DNS resolution all stay private.

Cross-Account and Cross-Region Patterns
#

Cross-Account (Same Region)
#

PrivateLink works natively across AWS accounts in the same region. The provider shares the endpoint service name (e.g., com.amazonaws.vpce.eu-west-1.vpce-svc-abc123), and you create an endpoint in your account.

Cross-Region
#

PrivateLink does not work across regions directly. Common patterns:

Pattern 1: Inter-Region VPC Peering + PrivateLink

Region A (your app)  ──peering──▶  Region B (endpoint)  ──PrivateLink──▶  Provider

Pattern 2: Transit Gateway + PrivateLink

Region A ──TGW peering──▶ Region B ──PrivateLink──▶ Provider

Pattern 3: Provider offers regional endpoints Most major services (Snowflake, Databricks, etc.) deploy endpoint services in each region, so you connect locally.

On-Premises Connectivity
#

For hybrid environments, the pattern extends to on-premises:

On-Premises                     AWS VPC                          Provider
┌──────────┐                  ┌────────────────┐              ┌──────────┐
│ App      │──Direct Connect──│ Route 53       │              │          │
│ Server   │  or VPN          │ Resolver       │              │ Service  │
│          │                  │ Inbound        │              │          │
│          │──DNS query──────▶│ Endpoint       │              │          │
│          │◀─private IP──────│                │              │          │
│          │                  │                │              │          │
│          │──traffic────────▶│ VPC Endpoint───┼──PrivateLink─│          │
└──────────┘                  └────────────────┘              └──────────┘

You need a Route 53 Resolver Inbound Endpoint so your on-premises DNS can forward queries to the VPC resolver, which then returns the private endpoint IPs.

Azure Private Link and GCP Private Service Connect#

The same concepts exist on other clouds:

ConceptAWSAzureGCP
Private endpointVPC Interface EndpointPrivate EndpointPrivate Service Connect Endpoint
Service exposureEndpoint Service + NLBPrivate Link Service + Standard LBService Attachment
DNS integrationPrivate Hosted ZonePrivate DNS ZoneCloud DNS
Cross-regionNo (use peering)Yes (Global Private Access)Yes (Global access option)

Azure and GCP both support cross-region private connectivity natively — AWS requires peering or Transit Gateway.

Security Considerations
#

1. Endpoint Policies
#

VPC Endpoint Policies control which principals can use the endpoint and which resources they can access. Without a policy, anyone in the VPC can use the endpoint to access any resource in the target service.

{
  "Statement": [
    {
      "Principal": {"AWS": "arn:aws:iam::123456789012:role/AppRole"},
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Effect": "Allow"
    }
  ]
}

2. Security Groups on Endpoints
#

Interface Endpoints have security groups. Lock them down:

  • Allow inbound only from your application subnets
  • Allow only the required ports (443 for HTTPS, etc.)

3. DNS Leakage
#

If Private DNS isn’t configured correctly, DNS queries may fall through to public DNS, and traffic will bypass the endpoint. Monitor with VPC Flow Logs and DNS query logging.

4. Data Exfiltration via Endpoints
#

An overly permissive endpoint policy could allow an attacker to exfiltrate data to an external account’s resources. Use endpoint policies + S3 bucket policies + SCPs to restrict cross-account access.

Cost
#

PrivateLink isn’t free:

ComponentCost (us-east-1, approximate)
Interface Endpoint$0.01/hour per AZ ($7.30/month)
Data processed~$0.01/GB
Cross-AZ data transferStandard rates apply

For a typical deployment across 2 AZs: ~$15/month + data processing fees. Often negligible compared to the security and compliance value.

When to Use PrivateLink#

Use CasePrivateLink?
Connecting to SaaS (Snowflake, Databricks, etc.) from a regulated VPCYes
Internal microservices across accountsYes
Public-facing API consumed by external customersNo (use API Gateway)
S3 access from VPCUse Gateway Endpoint (free)
Compliance requires no public internet pathsYes
Cost-sensitive dev/test environmentsMaybe not (public endpoint + TLS is usually fine)

Related Articles#

Kevin Keller
Author
Kevin Keller
Personal blog about AI, Observability & Data Sovereignty. Snowflake-related articles explore the art of the possible and are not official Snowflake solutions or endorsed by Snowflake unless explicitly stated. Opinions are my own. Content is meant as educational inspiration, not production guidance.
Share this article

Related