LowCodeStacks™
Sign in

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

Checked against Microsoft Learn · 5 sourcesPosted by the Maker DeskHow we write guides

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.

How a private endpoint name is meant to resolve
  1. App looks up mystorage.blob.core.windows.net
  2. Public DNS answers with a CNAME to mystorage.privatelink.blob.core.windows.net
  3. Azure DNS checks the private zone linked to the network
  4. 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 privatelink CNAME. 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.

bash
nslookup mystorage.blob.core.windows.net
You seeIt meansGo 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 firewallLook at the network path, not DNS
A public addressThe private zone wasn't consultedStep 2 onwards
NXDOMAINA private zone answered, but has no record for this nameThe A record, then NXDOMAIN for everything else
connection timed out; no servers could be reachedThe DNS server itself is down, often a custom DNS VMFix 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:

azurecli
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?

azurecli
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.

azurecli
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):

azurecli
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:

azurecli
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:

azurecli
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.

azurecli
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

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.