Private endpoint resolves to a public IP: fix private DNS step by step
When a private endpoint resolves to a public IP, or to NXDOMAIN, the endpoint itself is usually fine and DNS never reached your private zone. Five checks in order, from nslookup to the zone link, the A record and custom DNS servers, with the fix for each.
Networking & VMsFix a problem8 min read
You created a private endpoint, the portal says Approved, and the app still connects to the public address, or gets a 403 from a service whose public access is switched off. When a private endpoint resolves to a public IP, the endpoint is almost never the problem. The name your app looks up never reached the private DNS zone that holds the endpoint's private address. This guide walks the lookup from the client outwards: what the name resolves to now, whether the endpoint is approved, whether the zone has the right record, whether that zone is linked to the network asking, and whether a custom DNS server is in the way. Each check points to one fix.
- App looks up mystorage.blob.core.windows.net
- Public DNS answers with a CNAME to mystorage.privatelink.blob.core.windows.net
- Azure DNS checks the private zone linked to the network
- The zone's A record returns the endpoint's private IP
How the lookup is meant to work
Azure doesn't change your connection string when you add a private endpoint. The service's public name gets a CNAME in public DNS that points to a privatelink name, for example mystorage.blob.core.windows.net to mystorage.privatelink.blob.core.windows.net. A private DNS zone with that privatelink name holds an A record with the endpoint's private address. Clients inside a network that's linked to the zone, and that use Azure's own DNS (168.63.129.16), get the private address. Everyone else follows the CNAME on to the public address.
Two consequences matter for troubleshooting:
- Anyone on the internet can resolve the
privatelinkCNAME. That's by design, and it says nothing about access: with public network access disabled, the service refuses connections that arrive at its public front door whatever DNS said. - The zone name has to be the one Microsoft recommends for the service and sub-resource, or the records aren't created for you. A few common ones:
| Service (sub-resource) | Private DNS zone |
|---|---|
| Storage (blob) | privatelink.blob.core.windows.net |
| Storage (Data Lake, dfs) | privatelink.dfs.core.windows.net |
| Azure SQL Database (sqlServer) | privatelink.database.windows.net |
| Key Vault (vault) | privatelink.vaultcore.azure.net |
| Cosmos DB (Sql) | privatelink.documents.azure.com |
| Web Apps and Functions (sites) | privatelink.azurewebsites.net |
Step 1: what does the name resolve to now?
Run the lookup from the machine that fails, not from your laptop. For a cloud app, that's a VM, a console or a Kudu shell in the same network.
nslookup mystorage.blob.core.windows.net
| You see | It means | Go to |
|---|---|---|
A private address (10.x, 172.16 to 172.31, 192.168.x) | DNS works. The fault is routing, a network security group, or the service's firewall | Look at the network path, not DNS |
| A public address | The private zone wasn't consulted | Step 2 onwards |
NXDOMAIN | A private zone answered, but has no record for this name | The A record, then NXDOMAIN for everything else |
connection timed out; no servers could be reached | The DNS server itself is down, often a custom DNS VM | Fix the DNS server first |
Windows caches answers. Run ipconfig /flushdns after every change before you test again.
Step 2: is the private endpoint approved?
In the portal, open the private endpoint and check Connection status, or ask the CLI:
az network private-endpoint show --name <endpoint> --resource-group <rg> \
--query "{state:privateLinkServiceConnections[0].privateLinkServiceConnectionState.status, ip:customDnsConfigs}"
Pending means the owner of the target resource hasn't approved the connection yet; Rejected means they turned it down. Only Approved endpoints carry traffic. Note the private IP address here; the A record should hold exactly this.
Step 3: does the zone have the right record?
az network private-dns record-set a list --zone-name privatelink.blob.core.windows.net \
--resource-group <zone-rg> --query "[].{name:name, ip:aRecords[0].ipv4Address}" -o table
Look for a record named after the first label of the failing name (mystorage for mystorage.blob.core.windows.net) whose address matches Step 2. A missing or different record is the next fix; a correct record means the zone is fine and the question is who's asking.
The zone isn't linked to the network asking
This is the usual cause. A private DNS zone only answers networks that have a virtual network link to it. If the app's network isn't linked, Azure DNS never looks in the zone and returns the public answer.
az network private-dns link vnet list --zone-name privatelink.blob.core.windows.net \
--resource-group <zone-rg> -o table
If the app's network isn't in the list, add a link (with auto-registration off; that's for VM records, not private endpoints):
az network private-dns link vnet create --name link-to-app-vnet \
--zone-name privatelink.blob.core.windows.net --resource-group <zone-rg> \
--virtual-network <app-vnet-resource-id> --registration-enabled false
Allow a minute or two, then rerun Step 1. In a hub-and-spoke design, every spoke whose workloads need the name has to be linked, or has to send its DNS to something in a linked network; peering on its own doesn't share private DNS zones.
The A record is missing or stale
Records go missing when an endpoint was created without a DNS zone group, when a zone was made by hand without its record, or when another endpoint sharing the zone was deleted and took the record with it. A stale record points at an address the endpoint no longer has.
The lasting fix is a DNS zone group on the endpoint. Azure then creates the record, and updates or removes it with the endpoint:
az network private-endpoint dns-zone-group create --endpoint-name <endpoint> \
--resource-group <rg> --name default --zone-name privatelink.blob.core.windows.net \
--private-dns-zone <zone-resource-id>
Adding the A record by hand works immediately, but nothing keeps it right when the endpoint changes. One more rule from Microsoft: don't let one zone serve private endpoints for two different services. Give each kind of service its own recommended zone.
A custom DNS server never asks Azure
If the network uses its own DNS servers (domain controllers, a DNS VM, appliances), check them:
az network vnet show --name <vnet> --resource-group <rg> --query dhcpOptions.dnsServers
An empty list means Azure DNS, and the zone link should be enough. A list of addresses means every lookup goes to those servers first, and they must forward the privatelink zones to Azure DNS. On a Windows DNS server, add a conditional forwarder for privatelink.blob.core.windows.net (and each zone you use) pointing to 168.63.129.16.
168.63.129.16 only answers from inside an Azure network. A DNS server in your own data centre can't use it, which is why on-premises clients fail even with a healthy VPN or ExpressRoute. They need a forwarder that sits in Azure; the on-premises guide covers DNS Private Resolver, the managed way to do it.
NXDOMAIN for everything else in the namespace
Once a network is linked to privatelink.blob.core.windows.net, that zone answers for every storage account's privatelink name the network looks up. An account without a private endpoint has no record there, so it returns NXDOMAIN instead of its public address. This breaks third-party storage accounts and your own accounts that are still public.
The fix Microsoft recommends is fallback to internet on the zone's virtual network link: when the private zone has no answer, Azure DNS resolves the name publicly.
az network private-dns link vnet update --name <link-name> \
--zone-name privatelink.blob.core.windows.net --resource-group <zone-rg> \
--resolution-policy NxDomainRedirect
The alternative, typing the other account's public address into the private zone, works until that address changes, and nothing updates it.
When DNS is right and it still fails
If Step 1 returns the private address, stop looking at DNS. Check, in order: a network security group or firewall between the client and the endpoint's subnet; a route that sends the traffic somewhere else; and the service's own settings. A Blob client that resolves privately but still gets 403 needs the checks in Blob storage 403 not authorized. Data Lake clients also need a dfs endpoint next to the blob one.
Sources
- Troubleshoot private endpoint DNS resolution failure in Azure Private Link, Microsoft Learn (the causes, the five checks, the four resolutions)
- Azure Private Endpoint private DNS zone values, Microsoft Learn (the recommended zone names, the CNAME chain, one zone per service, fallback to internet)
- Azure Private Endpoint DNS integration scenarios, Microsoft Learn
- Virtual network links on private DNS zones, Microsoft Learn
- What is IP address 168.63.129.16?, Microsoft Learn
Did this fix it?
Comments
No comments yet. Share what worked for you.
Sign in to comment. Comments show under a display name you choose, never your email.