An Azure DNS resolution failure is rarely the dramatic outage it feels like at three in the morning. A name that worked yesterday returns nothing, an application throws a host-not-found exception, and a deployment that depended on a private endpoint suddenly cannot reach storage. The instinct is to treat the whole thing as broken and start guessing. That instinct is what turns a five minute fix into a two hour incident. Azure DNS resolution is not a single component you can declare healthy or unhealthy. It is a path, a short ordered chain of hops, and a query either traverses that chain cleanly or stalls at one specific hop. The skill that separates a calm engineer from a frantic one is the ability to find the hop that broke and repair only that, rather than rebuilding the whole chain and hoping the symptom disappears.
This article gives you that path and the method to walk it. By the end you will be able to take a failing name, run two or three confirming queries, place the failure at the exact hop where it lives, and apply the tested fix for that hop. You will stop reaching for the hosts file, which is the universal sign that an engineer has given up on understanding the resolution chain, and you will start reading the chain the way it actually behaves.

What an Azure DNS Resolution Failure Actually Means
The phrase covers a wide spread of symptoms, and conflating them is the first mistake. A name that returns the wrong address is a different problem from a name that returns no address at all, which is different again from a query that hangs until it times out. Before you touch a configuration, decide which of these you are looking at, because each points at a different part of the chain.
A query that times out usually means the resolver your client is configured to use is unreachable or is itself stuck waiting on an upstream it cannot reach. The client sent the question and never heard back. A query that returns an immediate failure, the kind that comes back in milliseconds with a non-existent-domain or a server-failure code, means the resolver answered, and it answered with a refusal. That distinction alone cuts the search space in half. A timeout points at reachability, at a firewall or a dead forwarder or a resolver address that no longer exists. A fast negative answer points at the records themselves, at a zone that is not linked, a record that was never created, or a forwarder that resolves public names happily but has no path to the private ones.
The third pattern, a name that resolves to an address but the wrong one, is the most deceptive because resolution technically succeeded. The client got an answer, connected, and failed somewhere the engineer was not looking. This is the signature of a split view, where the same name carries one address inside the virtual network and another outside it, and the client landed in the wrong view. It is also the signature of a stale cache holding an address that was correct an hour ago.
Holding these three patterns clearly in mind changes how you read every later signal. The first question on any name-resolution incident is never what is wrong, it is which of these three shapes the failure has, because the shape tells you which hop to suspect first.
What is the difference between a timeout and a fast negative answer?
A timeout means the query reached no answering resolver, so suspect reachability: an unreachable resolver address, a firewall dropping port 53, or a forwarder stuck on a dead upstream. A fast negative answer means a resolver replied with a refusal, so suspect the records: an unlinked zone, a missing entry, or a forwarder with no route to private names.
The reason this matters operationally is that the two shapes lead you to different tools. For a timeout you reach for connectivity tests, you confirm the resolver address is what you expect and that traffic can actually reach it. For a fast negative you reach for the records and the zones, you ask which resolver answered and whether it had any business knowing the name. Chasing records when the real problem is reachability, or chasing reachability when the resolver is plainly answering, is how engineers burn an afternoon. Read the shape first.
How to Read the Failure and Gather the Signal
The single most useful habit in a name-resolution incident is to stop assuming you know which resolver is answering, and to make the client tell you. Every operating system has a way to ask a name and report not just the address it received but the resolver that supplied it, and that second piece of information is the one that breaks most incidents open.
On a Linux host inside the virtual network, the most direct tool is dig, because it prints the answering server in its output. A query of the form dig contoso.privatelink.blob.core.windows.net returns an answer section with the address and, just as importantly, a line naming the server that responded. If that server is 168.63.129.16, the query went to the Azure-provided platform resolver. If it is a private address in your own range, the query went to a custom resolver you or someone before you configured. Knowing which of those answered tells you instantly whether the next thing to inspect is the platform path or your own server.
On Windows, the equivalent is Resolve-DnsName in PowerShell, or the older nslookup, both of which report the server they queried. The pattern Resolve-DnsName -Name myapp.database.windows.net -Server 168.63.129.16 lets you force the query at a specific resolver and compare its answer against whatever the client gets by default. That comparison is the heart of the method. If the platform resolver answers correctly when you query it directly, but the client gets a different answer or no answer through its configured resolver, you have just localized the failure to the gap between the client and the platform, which almost always means a custom resolver or a forwarder in the middle.
The order of operations matters. First, ask the name the way the application asks it, with no server override, and record what comes back and which server answered. Second, ask the same name directly at the platform resolver. Third, if a custom server sits in the path, ask the same name directly at that custom server. Three queries, three answers, and the place where the answers diverge is the hop that broke. This is not guesswork dressed up as method, it is a binary search across a known chain, and it converges fast.
Why does nslookup work but my application still cannot resolve the name?
A command line lookup and an application often use different resolvers or different caches. The shell query may hit a resolver you specified on the command line while the application uses the host’s configured resolver, or the application holds a cached negative answer from before the fix. Always query the name with no server override to match what the application sees, then check the application’s own resolver cache.
This divergence trips up more engineers than almost anything else in the resolution path. You run a lookup, it succeeds, you declare victory, and the application keeps failing. The explanation is usually one of two things. Either your manual lookup specified a resolver that the application does not use, so you tested a path the application never travels, or the application or its runtime has cached a failure and will keep returning it until the cache entry expires or the process restarts. A Java virtual machine, for one well known example, can cache a negative lookup for the lifetime of the process unless its caching policy is changed. The lesson is to reproduce the failure exactly as the application experiences it, with the same resolver and an awareness of caching, rather than running a cleaner test that happens to pass.
The InsightCrunch DNS Path Table
Every name your client asks travels the same ordered chain, and a failure lives at one hop on it. The table below is the findable artifact of this article, the InsightCrunch DNS path table. Each row names a hop, the check that confirms whether that hop is the broken one, and the fix when it is. Read it top to bottom on any incident and you will land on the responsible hop without rebuilding anything that was already working.
| Hop | What it does | Check that confirms it is the broken hop | Fix when it is broken |
|---|---|---|---|
| VNet DNS setting | Decides which resolver every VM in the network is handed | Inspect the VNet DNS servers property; confirm it is Azure-provided or your intended custom set | Set the VNet DNS servers to the correct value, then force the VM to renew so it picks up the change |
| VM lease and renewal | Delivers the VNet setting to the guest at lease renewal | Compare the resolver the guest is using against the VNet setting; they differ after a recent change | Renew the lease or restart the guest networking so the new resolver address is applied |
| NIC-level DNS override | A per-interface setting that overrides the VNet default | Check the network interface for an explicit DNS server that differs from the VNet | Clear the NIC override or correct it so the interface uses the intended resolver |
| Azure-provided resolver | Answers public names and linked private zones at 168.63.129.16 | Query the name directly at 168.63.129.16 and confirm the answer | If it answers correctly, the platform hop is healthy; move downstream to the client path |
| Custom DNS forwarder | Forwards unmatched queries from your own server back to Azure | Query a private name at the custom server; public names work but private ones fail | Add a forwarder from the custom server to 168.63.129.16 for private zones |
| Private DNS zone link | Lets the platform resolver answer a private zone for a network | List the virtual network links on the zone; the failing network is absent | Create a virtual network link from the private zone to the failing network |
| Private zone record | The actual entry the zone returns for the name | Query the name at the linked resolver; the zone exists but the record is missing | Create or correct the record, or fix the auto-registration that should create it |
| On-premises forwarding | Sends Azure private names from on-premises resolvers into Azure | From on-premises, the private name returns nothing or a public address | Forward the private zone suffix to an inbound resolver endpoint or a forwarder in the network |
The discipline the table enforces is sequence. You do not jump to the private zone link because that is where the problem was last time. You start at the top, confirm or eliminate each hop in order, and stop at the first one that fails its check. Most incidents resolve within the first four rows, and the ones that reach the bottom are nearly always custom resolver or hybrid scenarios, which is exactly where the path is longest and the assumptions are weakest.
The VNet DNS Setting: Azure-Provided Versus Custom
Every virtual network carries a single decision that shapes resolution for everything inside it: which resolver its machines are told to use. By default, a network hands its machines the Azure-provided resolver, and for a large number of workloads that default is correct and complete. The platform resolver answers public internet names, it answers the names of Azure services, and it answers private zones that have been linked to the network. The moment someone changes that setting to point at a custom server, the entire character of the network’s resolution changes, and most private-name failures in larger environments trace back to that change.
The setting lives on the virtual network itself, in the DNS servers property, and it can hold either the default Azure-provided value or an explicit list of custom resolver addresses. When it holds custom addresses, every machine that joins the network, and every machine already in it after its next lease renewal, will send its queries to those custom servers instead of to the platform. This is the right design when you run your own resolvers, perhaps domain controllers that need to answer internal names, but it carries an obligation that the next section covers in detail: those custom servers must have a path back to Azure for the private names the platform would otherwise have answered.
To read the current setting from the command line, query the network’s DNS servers directly. The pattern az network vnet show --resource-group rg-network --name vnet-core --query "dhcpOptions.dnsServers" returns either an empty result, which means the network is using the Azure-provided resolver, or a list of the custom addresses configured. That single command tells you which world you are in, and the answer reframes everything downstream. An empty result and a failing private name points you toward the zone link and the record. A list of custom addresses and a failing private name points you toward the forwarder on those servers.
Why does a VM not pick up a changed VNet DNS setting?
The DNS server list is delivered to the guest at lease renewal, not the instant you change the network property. A machine keeps using its old resolver until the lease renews, which can take time, or until you force it by restarting the guest networking or rebooting. Changing the setting and expecting an immediate effect is a common and avoidable misdiagnosis.
This single behavior produces a disproportionate share of confused incidents. An engineer changes the network’s resolver list, confirms the new value is saved, then logs into a machine and finds it still using the old address. Nothing is broken. The guest received its current resolver address through a lease, and it will continue to honor that lease until the lease comes up for renewal, at which point it will receive and apply the new value. The fix is to stop waiting and force the renewal. On a Linux guest, restarting the network service or releasing and renewing the lease applies the new value at once. On Windows, ipconfig /renew does the same, and a reboot is the blunt instrument that always works. The deeper lesson is that the network setting and the guest’s live configuration are two separate facts that converge over time, and during the window between a change and a renewal they will disagree. Always check what the guest is actually using, not only what the network was told to hand out.
There is a related trap worth naming. Some teams set the resolver inside the guest operating system directly, hard-coding it in the network configuration rather than letting the platform deliver it. When they later change the network setting, the guest ignores it entirely, because a value set inside the guest takes precedence over what the lease offers. If a machine stubbornly refuses to honor the network setting even after a clean renewal, inspect the guest’s own network configuration for a hard-coded resolver that someone set and forgot. That value is overriding everything you change at the network level, and it will keep doing so until it is cleared.
The Azure-Provided Resolver at 168.63.129.16
If there is one address every Azure engineer should be able to recite from memory, it is 168.63.129.16. This is the Azure-provided resolver, a platform endpoint reachable from inside every virtual network, and it is the quiet workhorse behind a large fraction of all resolution in Azure. It answers public internet names on behalf of your machines, it answers the names of Azure platform services, and, critically for the failures this article addresses, it answers the private zones that have been linked to the network the query came from. Understanding what this address is and what it can and cannot do is the foundation for diagnosing the rest of the chain.
The address is not a normal internet host. It is a virtual public address that the platform makes available to every machine inside a virtual network, and it is only reachable from inside Azure. You cannot query it from your laptop at home, and you should not try, because its reachability from on-premises is a separate problem solved by inbound resolver endpoints rather than by talking to the address directly. From inside the network, though, it is always there, it does not need a route you configure, and it answers on the standard resolution port. When a machine uses the Azure-provided resolver, this is the address it is talking to, whether the engineer running the machine knows the number or not.
What is the 168.63.129.16 Azure DNS resolver?
It is the Azure-provided platform resolver, a virtual address reachable from inside any virtual network that answers public names, Azure service names, and private zones linked to the querying network. It is the upstream that custom resolvers must forward to in order to resolve private zones, and it is reachable only from inside Azure, not from on-premises networks.
Because the address is the platform’s answering endpoint, querying it directly is the single most valuable diagnostic step in the whole chain. When a private name fails and you are not sure whether the platform itself can answer it, force a query straight at the address and read the result. From a Linux guest, dig @168.63.129.16 myapp.privatelink.database.windows.net bypasses whatever resolver the client is configured to use and asks the platform directly. From Windows, Resolve-DnsName -Name myapp.privatelink.database.windows.net -Server 168.63.129.16 does the same. If the platform answers correctly, you have proven that the zone is linked, the record exists, and the platform path is healthy, which means the failure lives between the client and the platform, in a custom resolver or a missing forwarder. If the platform itself returns nothing, the problem is upstream of the client entirely, in the zone link or the record, and you can stop suspecting the client side.
This is the pivot point of the entire method. A direct query at 168.63.129.16 partitions the chain into two halves. A healthy answer means the platform and the zone are fine and the fault is on the client side of the path. A failed answer means the platform cannot resolve the name and the fault is in the zone link or the record. There is no faster way to cut the problem in half, and engineers who skip this step routinely spend an hour on the wrong half of the chain.
A word of caution about reachability. Because the address only answers from inside a virtual network, a machine that is not in a network, or a network where an aggressive firewall rule is dropping traffic to platform addresses, will see queries to 168.63.129.16 time out. A timeout here, as opposed to a refusal, points back at reachability rather than records, and it is worth confirming that no security rule is blocking the platform address before concluding the resolver is unhealthy. The platform resolver almost never fails on its own. When it appears to, the cause is usually a rule someone added that severs the path to it.
The Forwarder-to-Azure Rule for Custom DNS
Here is the central claim of this article, the one rule that resolves the largest single category of private-name failures in environments that run their own resolvers. When a virtual network is configured to use custom resolvers, those resolvers answer the names they know, public internet names and any internal names they host, but they do not automatically know how to answer Azure private zones. The platform resolver knew those zones because it is the platform. A custom server you stood up has no inherent knowledge of them. For a custom server to resolve a private zone, it must forward the queries it cannot answer back to the platform resolver at 168.63.129.16. This is the forwarder-to-Azure rule, and a private name that fails on a custom resolver while public names succeed is, in the overwhelming majority of cases, a missing forwarder.
The mechanism is straightforward once you hold the model. A query arrives at the custom server for a private zone name. The custom server checks its own zones, finds nothing, and then does what resolvers do with names they cannot answer: it forwards the query upstream. The question is where upstream points. If it points at a public internet resolver, that resolver has never heard of your private zone and returns nothing, and the private name fails. If it points at 168.63.129.16, the platform receives the forwarded query, recognizes the private zone linked to the network, and answers. The single difference between a working private name and a failing one, in a custom-resolver network, is whether the upstream forwarder is the platform address or something else.
This is why the diagnostic signature is so clean. On a custom resolver with a missing or wrong forwarder, public names resolve perfectly, because the upstream public resolver knows them, while private names fail uniformly, because the upstream has no path to them. When an engineer reports that everything resolves except the Azure private endpoints, the forwarder is the first and usually the last place to look. You confirm it by querying a public name and a private name at the same custom server. The public name succeeds, the private name fails, and the contrast names the cause.
The fix is to configure the custom resolver to forward to the platform address. On a Windows resolver running the DNS Server role, this is a conditional forwarder or a general forwarder pointing at 168.63.129.16, depending on whether you want all unmatched queries or only the private zone suffix sent there. On a BIND resolver, it is a forwarders directive in the configuration. The precise syntax varies by product, but the target never does. It is always 168.63.129.16, because that is the only endpoint that knows the private zones linked to the network. Forwarding to any other address, however reasonable it looks, will not resolve Azure private zones, because no other resolver holds them.
A subtlety that catches careful engineers is the difference between a general forwarder and a conditional one. A general forwarder sends every unmatched query to the platform, which works but routes your public name resolution through the platform as well. A conditional forwarder sends only queries for a specific suffix, such as the private zone suffix for a service, to the platform, while everything else goes wherever your normal upstream points. Conditional forwarding is the cleaner design for most environments because it keeps the routing intentional, but it carries its own failure mode: if you add a conditional forwarder for one private suffix and later start using a service with a different private suffix, the new suffix has no forwarder and its names fail. When a custom-resolver network resolves some private services but not a newly added one, suspect a conditional forwarder that covers the old suffixes but not the new one.
Once you have internalized the forwarder rule, a whole class of incidents collapses into a single check. A private name fails. You ask whether the network uses custom resolvers. If it does, you query a public and a private name at the custom server, confirm the public-works-private-fails pattern, and look at the forwarder. The fix is to point it at the platform address. This is the most repeatable diagnosis in the entire resolution chain, and naming it explicitly, the forwarder-to-Azure rule, is what makes it stick in memory for the next incident. The series treats name resolution the way it treats every other failure, as a path with a broken hop, and this hop is the one that breaks most often in real environments. For the underlying zone model that makes this rule necessary, the deeper treatment lives in Azure DNS and Private DNS Zones Explained, which lays out how the platform holds and answers private zones in the first place.
Private DNS Zones and the VNet Link
When the platform resolver itself cannot answer a private name, even on a direct query at 168.63.129.16, the cause is no longer the client path. It is in the zone. A private zone in Azure is a container of records for a private namespace, and the platform answers it for a given network only when that network is explicitly linked to the zone. The link is the permission slip. Without it, the platform treats the network as a stranger to the zone and returns nothing, no matter how correct the records inside the zone are. The most common reason a direct platform query fails for a private name is a zone that exists, holds the right record, and is simply not linked to the network the query came from.
The model is worth holding precisely because it is easy to half-understand. Creating a private zone does not make any network able to resolve it. Putting a record in the zone does not make any network able to resolve it. The zone becomes resolvable for a network only when you create a virtual network link connecting that specific network to that specific zone. A zone can be linked to many networks, and a record in it will resolve from every linked network and from none of the unlinked ones. When a name resolves perfectly from one network and fails from another, the difference is almost always the link: the working network is linked to the zone and the failing one is not.
To confirm, list the links on the zone and check whether the failing network is present. The pattern az network private-dns link vnet list --resource-group rg-dns --zone-name privatelink.blob.core.windows.net --query "[].virtualNetwork.id" returns the networks linked to the zone. If the failing network’s identifier is absent from that list, you have found the hop. The fix is to create the missing link with az network private-dns link vnet create, supplying the zone, the network, and a name for the link. Once the link exists, the platform will answer the zone for that network, and the direct query at 168.63.129.16 that failed a moment ago will succeed.
Why does a Private DNS zone name fail to resolve?
The most frequent cause is a missing virtual network link: the zone exists and holds the record, but it is not linked to the network the query came from, so the platform refuses to answer it for that network. The next most frequent is a missing record inside an otherwise linked zone, often because auto-registration was expected to create it and did not. Confirm by listing the zone’s links and its records.
The auto-registration angle deserves attention because it produces a particularly confusing failure. A private zone can be configured to automatically register the names of machines in a linked network, so that when a machine joins, its name appears in the zone without anyone creating a record by hand. This is convenient until it is not. Auto-registration applies only to machines in a network linked with registration enabled, and it registers machine names, not the service names of private endpoints. Engineers sometimes expect a private endpoint’s name to appear through auto-registration and are puzzled when it does not, because private endpoint records are created by the endpoint’s zone group, a separate mechanism, not by auto-registration. When a machine name is missing, look at registration on the link. When a private endpoint name is missing, look at the endpoint’s zone group, which is the subject of the closely related endpoint failure covered in Fix Private Endpoint DNS Not Resolving.
There is a layered structure to these zone failures that rewards a methodical check. First confirm the zone exists. Then confirm the failing network is linked to it. Then confirm the specific record is present in the zone. Each step eliminates a hop, and the failure lives at the first step that comes back empty. A zone that does not exist is a provisioning gap. A zone that exists but is not linked is a link gap. A zone that is linked but lacks the record is a record gap, which loops back to whether auto-registration or a zone group was supposed to create it. Walking these three checks in order turns a vague private-name failure into a precise, named cause every time.
When the NIC Overrides the VNet
A failure mode that defeats otherwise thorough engineers is the network interface that carries its own resolver setting, overriding the network default for the single machine attached to it. The network’s DNS setting is the default handed to every machine, but a network interface can carry an explicit resolver list of its own, and when it does, that list wins for that interface. You can configure the network perfectly, point it at the right resolvers, confirm every other machine resolves correctly, and still have one machine that fails, because its interface carries an old or wrong resolver address that overrides everything you set at the network level.
The reason this is so easy to miss is that the network setting looks authoritative. You inspect the network, see the correct resolvers, and reasonably conclude that every machine in the network uses them. But the per-interface setting is a more specific configuration, and more specific configuration wins. A machine whose interface specifies a resolver will use that resolver and ignore the network default entirely. If one machine in a network resolves differently from all its peers, the interface is the first place to look, before you touch anything at the network level, because nothing you change at the network level will affect a machine that is overriding the network on its interface.
To confirm, inspect the interface’s DNS settings directly. The pattern az network nic show --resource-group rg-compute --name nic-app-01 --query "dnsSettings.dnsServers" returns the resolver list configured on the interface, or an empty result if the interface defers to the network. An empty result means the interface is using the network default, which is what you usually want. A populated result means the interface is overriding, and that list, not the network setting, is what the machine is actually using. The fix is to clear the override so the interface falls back to the network default, or to correct the override to the intended resolvers if a per-machine setting is genuinely required, which is rare.
The operational lesson generalizes beyond resolution. Azure layers configuration from the broad to the specific, and the specific wins. A network-level default is overridden by an interface-level setting, which can in turn be overridden by a setting inside the guest. When you diagnose any setting that seems not to apply, walk the layers from specific to broad and find the most specific one that is set, because that is the one in force. For resolution specifically, the order is the guest’s own configuration first, then the interface, then the network. Check them in that order and you will find the override that the network-level view was hiding. The broader model of how these network-level defaults are assigned is laid out in Azure Networking Fundamentals for Engineers, which covers where the VNet DNS setting sits in the overall network design.
On-Premises Clients and Azure Private Names
Hybrid environments add a hop that pure-cloud environments never see, and it is the hop that produces the most stubborn private-name failures. An on-premises client, sitting in a corporate data center connected to Azure over a gateway, asks for an Azure private name. Its query goes to the corporate resolver, which has never heard of the Azure private zone and cannot reach the platform resolver at 168.63.129.16, because that address only answers from inside a virtual network. The query fails, or worse, it falls through to a public resolver and returns the public address of a service that is supposed to be reached privately. Either way, the on-premises client cannot resolve the name, and the cause is that the corporate resolver has no path into Azure’s private resolution.
The model to hold is that the platform resolver is unreachable from on-premises by design. You cannot simply point the corporate resolver at 168.63.129.16, because that address does not answer queries arriving from outside a virtual network. The bridge has to be something inside Azure that the on-premises resolver can reach and that can in turn ask the platform. Historically that bridge was a small resolver virtual machine running inside a network, configured to forward to 168.63.129.16, which the on-premises resolver would forward Azure private suffixes to. The on-premises resolver forwards the Azure suffix to the in-Azure forwarder, the forwarder asks the platform, the platform answers, and the answer travels back out to the on-premises client.
The cleaner modern bridge is an inbound resolver endpoint, a managed entry point inside a network that on-premises resolvers can forward to without you running and patching a forwarder virtual machine yourself. The on-premises resolver is configured with a conditional forward for the Azure private suffix, pointing at the inbound endpoint’s address, and the endpoint resolves the private zones linked to its network on the on-premises client’s behalf. The design removes the maintenance burden of a self-managed forwarder while keeping the same logical path: on-premises resolver, into Azure, to the platform, and back. When you stand up private endpoints that on-premises clients must reach, the resolver endpoint and its forwarding rules are part of the setup, which is why the end-to-end build is covered in Set Up Private Endpoints End to End.
The diagnostic signature of a hybrid resolution failure is specific enough to recognize on sight. The private name resolves correctly from a machine inside the Azure network but fails or returns a public address from an on-premises machine. That contrast localizes the failure to the on-premises forwarding hop immediately. If it resolved from inside Azure, the zone is linked and the record exists, so the platform side is healthy, and the only remaining variable is whether on-premises queries have a path to the platform. They do not by default, and the fix is to give them one, through a forwarder or an inbound resolver endpoint, with the on-premises resolver configured to forward the Azure private suffix to it.
A failure within this hop that deserves its own mention is the wrong-address-on-premises case, where the on-premises client resolves the name but receives the public address instead of the private one. This happens when the on-premises resolver forwards the suffix to a public resolver, or forwards nothing and lets the name fall through to its normal upstream, which returns the publicly registered address. The client then connects to the public endpoint, which may be blocked by the service firewall, producing a connection failure that looks like a network problem but is really a resolution problem. The name resolved, just to the wrong view, and the fix is the same forwarding configuration that gives on-premises clients a path to the private answer.
Split-Horizon and the Wrong View
The most intellectually slippery resolution failure is the one where resolution succeeds and the answer is wrong, because the same name carries two different addresses depending on where the query originates. A public service name and a private endpoint for the same service share the name, but the public answer points at the public address and the private zone answers with the private address. A client that should get the private answer but lands in the public view resolves the name perfectly and then connects to the wrong place. Nothing in the resolution itself looks broken, which is exactly why this failure consumes time.
The mechanism is a deliberate feature being experienced as a bug. Private Link and private endpoints work by overriding the public name of a service with a private address inside networks that are linked to the appropriate private zone. From inside such a network, the name resolves to the private address and traffic stays on the private path. From outside, or from inside a network that is not linked to the private zone, the same name resolves to the public address. This split is intentional, it is how the private path is made transparent to applications, but it means that a client’s view of a name depends entirely on whether its network is linked to the private zone. A client in the wrong view is not suffering a resolution failure in the strict sense, it is suffering a linkage failure that manifests as the wrong answer.
The way to confirm a split-horizon problem is to compare the answer the client receives against the address you expect. If the client should reach the service privately but the name resolves to a public address, the client is in the public view, and the cause is that its network is not linked to the private zone, or that a resolver in its path is answering with the public address before the private zone is consulted. Querying the name directly at the platform resolver from inside the client’s network settles it. If the platform returns the private address but the client receives the public one, a custom resolver or a forwarder in the path is answering with the public view before the platform’s private answer is reached, and the fix is to route the private suffix through the platform. If the platform itself returns the public address, the network is not linked to the private zone, and the fix is the link.
A particularly confusing variant arises when an organization runs its own public-facing records for a name that also has a private zone. The custom resolver answers the name from its own records with the public address and never forwards to the platform, so the private zone is never consulted. The client always lands in the public view, even from inside a network that is linked to the private zone, because the custom resolver short-circuits the lookup. The signature is a private name that resolves to the public address on the custom resolver but to the private address when queried directly at the platform. The resolution order on the custom server is answering the name locally before it would forward, and the fix is to remove the local answer or to ensure the private suffix forwards to the platform ahead of any local match.
The general principle for the wrong-view family is that resolution succeeding does not mean resolution is correct. When a connection fails after the name resolved, the next question is not whether the name resolved but what it resolved to, and whether that address is the one the client should have received. Comparing the client’s answer against a direct platform query is the fastest way to tell whether the client is in the right view, and the answer points straight at either a missing link or a resolver that is answering before the private zone is reached.
Reading the Diagnostic Signal in Detail
The three-query method depends on reading the output of a lookup tool with precision, and the difference between a casual glance and a careful read is often the difference between a correct diagnosis and a wrong one. The output of a query carries more than the address. It carries the answering server, the result code, the time the answer took, and sometimes the path the query traveled, and each of these is a clue that narrows the search.
The result code is the first thing to read after the address. A successful lookup carries a success code and an answer section with the address. A non-existent-domain code means the resolver definitively does not know the name and is sure no one does, which for a private zone points at an unlinked zone or a missing record rather than a forwarding problem, because a resolver that forwarded the query would not return a definitive non-existence for a name the platform could answer. A server-failure code means the resolver tried and could not complete, which on a custom server almost always means the forwarder it relied on failed or timed out. A refused code means the resolver declined to answer the query at all, which points at a configuration that does not permit the query rather than at the records. Reading the code before the address tells you whether you are chasing a missing record, a broken forwarder, or a policy that blocked the query.
The answering server line is the second thing to read, and it is the one engineers most often overlook. Every capable lookup tool reports which resolver supplied the answer, and that single line tells you whether the query reached the platform, a custom server, or something unexpected. When the answering server is not the one you expected, you have found a configuration surprise before you even look at the records: the machine is using a resolver you did not know about, which reframes the entire investigation. On a Linux guest, the server line appears near the bottom of a dig answer. On Windows, the same information appears in the output of a Resolve-DnsName query, which reports the server it consulted. Train the eye to read that line first, because a wrong answering server explains many failures on its own.
The timing of the answer separates a reachability problem from a records problem more reliably than almost any other signal. An answer that comes back in a few milliseconds, whether positive or negative, means the resolver was reachable and answered from what it knew or could quickly determine. An answer that takes several seconds and then fails means the resolver waited on an upstream that never replied, which is the signature of a forwarder pointing at an unreachable address or of a query being silently dropped somewhere in the path. When a private name fails slowly on a custom resolver, the forwarder is reaching for an address it cannot get to, and the slow failure is the resolver giving up after a timeout. When a private name fails instantly, the resolver answered from local knowledge, which means it had a definitive negative rather than a failed forward.
On Windows specifically, there are a few additional moves worth knowing. The resolver cache on the machine can be inspected directly, which lets you see whether a stale answer is being returned from cache rather than from a fresh query, and it can be cleared to force a fresh lookup. Forcing a query at a specific server with the server parameter lets you replicate the three-query method without changing the machine’s configuration, which is exactly what you want during a live incident where changing the machine’s resolver would itself be a risky operation. On Linux, the equivalent moves are reading and flushing whatever local caching service the distribution runs, and using the server-override form of dig to query a specific resolver without touching the machine’s configured resolver. The tools differ by platform, but the method is identical: read the code, read the answering server, read the timing, and force queries at specific resolvers to localize the divergence.
Conditional Forwarding in Hybrid Environments
The hybrid path deserves a closer look, because conditional forwarding is both the mechanism that makes on-premises resolution of Azure private names possible and the mechanism that most often misfires. A conditional forwarder is a rule on a resolver that says queries for a specific suffix go to a specific upstream, while everything else follows the resolver’s normal path. In a hybrid environment, the on-premises resolver carries a conditional forwarder for the Azure private suffix, pointing at a bridge inside Azure, and the Azure-side resolver carries a forwarder to the platform. The chain has two forwarding hops, and a failure can live at either one.
Consider the path a query travels from an on-premises client to an Azure private name. The client asks its corporate resolver. The corporate resolver matches the Azure private suffix against its conditional forwarder and sends the query to the bridge inside Azure, which is either an inbound resolver endpoint or a forwarder virtual machine. The bridge forwards the query to the platform resolver at 168.63.129.16. The platform recognizes the private zone linked to the bridge’s network and answers with the private address. The answer travels back the way it came, to the bridge, to the corporate resolver, to the client. Each hop in that path is a place the chain can break, and the diagnostic discipline is to test each hop in sequence rather than assuming you know which one failed.
The first hop to test is the conditional forwarder on the on-premises resolver. If the suffix in the rule does not exactly match the suffix of the failing name, the rule never fires, and the query falls through to the corporate resolver’s normal upstream, which returns the public address or nothing. A subtle version of this failure happens when an organization uses several Azure services with different private suffixes and the conditional forwarder covers only some of them. The services whose suffixes are covered resolve from on-premises, and a newly adopted service whose suffix is not covered fails, producing a failure that appears only for the new service while everything else works. The fix is to extend the conditional forwarder to cover the new suffix, or to forward the broad private suffix that encompasses the services in use.
The second hop to test is the bridge inside Azure. If the bridge is a forwarder virtual machine, it must itself forward to the platform, and it must be reachable from the on-premises resolver over the gateway. A bridge that is unreachable produces a timeout at the on-premises resolver, and a bridge that is reachable but lacks its own forwarder to the platform produces a failure even though the on-premises rule fired correctly. If the bridge is a managed inbound resolver endpoint, the forwarding to the platform is handled for you, and the remaining variable is whether the on-premises resolver can reach the endpoint’s address over the connection. Testing the bridge means querying the Azure private name directly at the bridge from a machine that can reach it, which confirms whether the bridge itself can resolve the name before you blame the on-premises rule.
The third hop is the platform and the zone, which is the same hop you would test in a pure-cloud failure. If the bridge can resolve the name, the platform and the zone are healthy, and the failure is upstream of the bridge, in the on-premises rule or the path to the bridge. If the bridge cannot resolve the name, the zone is not linked to the bridge’s network, or the record does not exist, and the hybrid path is irrelevant to the failure because the failure would occur from inside Azure too. This is why the very first test in a hybrid failure is whether the name resolves from inside the Azure network at all. If it does not, the hybrid path is a distraction, and the real problem is the zone link or the record. If it does, the hybrid path is where the failure lives, and you walk it hop by hop.
A reverse-resolution wrinkle deserves a brief note, because it surprises engineers who only think about forward lookups. Some workloads, and some security controls, perform a reverse lookup that maps an address back to a name, and reverse zones for private addresses need their own configuration and their own links. A forward lookup that succeeds while a reverse lookup fails points at a missing reverse zone or an unlinked one, which is a separate provisioning task from the forward zone. When an application or a control behaves oddly despite forward resolution working, check whether it depends on a reverse lookup, because the reverse path is configured independently and is easy to forget.
Declaring the Resolution Path as Code
The most durable prevention for the failures in this article is to declare the resolution-critical configuration as code, so that the zone, its link, and the endpoint that registers into it are created together and reviewed together rather than assembled by hand across separate operations. When the zone link and the endpoint live in the same template, the gap between creating a private endpoint and linking its zone, which is the single most common private-endpoint resolution failure, cannot occur, because the template will not deploy one without the other.
A template that provisions a private zone, links it to a network, and registers an endpoint into it captures the whole resolution path for that service in one reviewable unit. The zone is declared, the link from the zone to the network is declared with the network’s identifier, and the endpoint’s zone group is declared so that the endpoint registers its record automatically. The pattern below sketches the shape of such a declaration, naming the zone, the link, and the relationship to the network, so that the resolution path is provisioned atomically rather than in pieces that can drift apart.
resource zone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.blob.core.windows.net'
location: 'global'
}
resource zoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
parent: zone
name: 'link-to-core-vnet'
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: coreVnetId
}
}
}
The declaration above makes the zone and its link a single deployable fact. The zone is created, and the link binds it to the network identified by its resource identifier, with registration deliberately disabled because a private endpoint zone registers through its zone group rather than through auto-registration. Declaring the link with the network identifier rather than creating it by hand after the fact is what prevents the unlinked-zone failure, because the link is part of the same deployment as the zone and the two cannot exist without each other in the declared state.
The endpoint’s zone group is the companion declaration that registers the endpoint’s address into the zone. Without it, the endpoint exists, the zone exists, the link exists, and yet the name still resolves to the public address, because nothing put the private record into the zone. The zone group is the mechanism that does, and declaring it alongside the endpoint closes the last gap. When the endpoint, the zone, the link, and the zone group are all in one template, a deployment either creates the entire working resolution path or fails cleanly, and there is no intermediate state where the endpoint is reachable by address but not by name.
For custom resolvers, the forwarder configuration cannot live in a platform template because it lives inside a server you manage, but it can and should live in configuration management. A resolver build that ships with the platform forwarder already configured for the private suffixes the network uses turns the forwarder-to-Azure rule into a property of every resolver rather than a thing engineers add after the first failure. The configuration management code that builds the resolver declares the forwarder to 168.63.129.16, and every resolver built from that code carries the forwarder from the moment it comes online. This is the same principle as the zone-link template applied to the part of the path that lives outside the platform: declare the resolution-critical setting once, in code, and the failure it would otherwise cause cannot recur.
The review value of declaring the path as code is as important as the reproducibility. When the zone, the link, the endpoint, and the forwarder are all in code, a change to any of them is a change to a reviewed artifact, and the review is the moment to catch a link pointed at the wrong network or a forwarder pointed at the wrong address. A path assembled by hand has no such checkpoint, and its errors are discovered during incidents rather than during review. Moving the resolution path into code moves its failures from production into the pull request, which is exactly where you want them.
Two More Worked Diagnoses
A single worked example shows the method on one failure shape; two more show it generalizing across the others. The first additional case is the unlinked zone, which produces a failure that the platform itself cannot resolve. An application on a virtual machine cannot reach a database over a private endpoint, and the name in the error is the database’s private endpoint name. The engineer reproduces the failure as the application sees it, with no resolver override, and the name returns nothing. The answering server, this time, is 168.63.129.16, which means the machine is using the platform resolver directly, with no custom server in the path. That fact alone changes the suspect list. There is no custom resolver and therefore no forwarder to suspect, so the failure must live in the zone or the record.
The engineer queries the name directly at the platform resolver, which is the same server the machine already uses, and confirms the failure: the platform does not resolve the name. Because the platform itself cannot answer, the zone is either not linked to the network or is missing the record. The engineer lists the links on the database private zone and finds the network’s identifier absent. The zone exists, it holds the record for the endpoint, and it is simply not linked to the network the machine lives in. The fix is to create the virtual network link from the zone to the network, after which the platform answers the name for the network and the application reaches the database privately. The diagnosis took two queries and one list operation, and it landed on a precise cause: an unlinked zone, fixed by a link, not by widening anything or rebuilding the endpoint.
The second additional case is the single-machine anomaly, where one machine fails while every other machine in the same network resolves correctly. A monitoring agent on one virtual machine cannot reach its ingestion endpoint, while identical agents on peer machines in the same network work. The shared-fate of the peers is the first clue: if the network setting, the platform resolver, and the zone link were the problem, the peers would fail too, and they do not. The failure is specific to one machine, which points at machine-specific configuration rather than network-wide configuration. The engineer reproduces the failure on the affected machine and reads the answering server, which turns out to be a resolver address that none of the peer machines use.
That answering server is the surprise that breaks the case open. The affected machine is using a resolver the network does not hand out, which means something on the machine is overriding the network default. The engineer inspects the machine’s network interface and finds an explicit resolver list configured on the interface, pointing at an old resolver that no longer answers the private suffix. The interface override is winning over the network default, and because the override points at a stale resolver, the machine fails while its peers, which use the network default, succeed. The fix is to clear the interface override so the machine falls back to the network default, after which the machine resolves the name exactly as its peers do. Once more the diagnosis was a walk down the path, the single-machine shape pointed at machine-specific configuration, and the fix touched only the one hop that was broken.
A Worked Diagnosis From Symptom to Fix
To make the method concrete, walk through a single incident from the first symptom to the confirmed fix, applying the path table in order. An application running on a virtual machine throws a host-not-found error when it tries to reach a storage account over a private endpoint. The name in the error is the storage account’s blob endpoint. The on-call engineer has the error, the machine, and nothing else.
The first move is to reproduce the failure the way the application sees it, with no resolver override. From the machine, a plain query of the storage name returns nothing and reports that the answering server is a private address, not 168.63.129.16. That single line of output is already informative. The machine is using a custom resolver, not the platform, which means the network’s DNS setting points at custom servers and the path now includes a hop the platform-only path would not. The shape of the failure is a fast negative answer, not a timeout, so the suspicion shifts toward records and forwarding rather than reachability.
The second move is to query the name directly at the platform resolver. From the same machine, forcing the query at 168.63.129.16 returns the private address correctly. This is the pivot. The platform can resolve the name, which proves the zone is linked to the network and the record exists, so the entire upstream half of the chain is healthy. The failure lives between the client and the platform, in the custom resolver the machine is actually using.
The third move is to query the name directly at the custom resolver, and to query a public name there for contrast. The public name resolves cleanly. The private storage name fails. This is the unmistakable signature of the forwarder-to-Azure rule being violated. The custom resolver knows public names because its upstream is a public resolver, and it cannot resolve the private zone because it has no forwarder pointing at the platform. The diagnosis is complete, and it took three queries: the failure is a missing forwarder on the custom resolver, and the fix is to add a forwarder for the private zone suffix pointing at 168.63.129.16.
The repair is to configure the custom resolver to forward the storage private suffix to the platform address, then to confirm the fix by repeating the first query, the one the application uses, and watching it now return the private address. A final step that engineers often skip is to clear any cached negative answer the application holds, because the application may have cached the failure and will keep returning it until the cache expires or the process restarts. With the forwarder in place and the cache cleared, the application reaches the storage account privately, and the incident closes. The whole diagnosis was a walk down the path table, stopping at the first hop that failed its check, and the fix touched only that hop. Nothing was rebuilt, nothing was widened, and the cause was named precisely enough to prevent the next occurrence.
Prevention: Stopping the Failure From Recurring
A fix that closes an incident without preventing the next one is half a fix. The resolution failures in this article cluster around a small number of configuration gaps, and each has a durable prevention that costs far less than the incident it avoids. The most valuable preventive habit is to express the resolution-critical configuration as code rather than as clicks, so that the zone links, the forwarders, and the network settings are declared, reviewed, and reproducible rather than set by hand and forgotten.
The zone link is the first thing to codify. A private zone that is not linked to a network is a latent failure waiting for the first query, and the link is trivial to declare in a template. When you provision a network that must resolve a private zone, the link belongs in the same template as the network, so that the two are always created together and never drift apart. The same applies to the zone group on a private endpoint, which is the mechanism that registers the endpoint’s record in the zone, and which, when omitted, produces a name that resolves to the public address from inside a network that should have answered privately. Declaring the endpoint and its zone group together prevents the most common private-endpoint resolution gap before it can occur.
The custom resolver forwarder is harder to codify because it lives inside a server you manage rather than in the platform, but it is no less important to standardize. Every custom resolver in an Azure network needs a forwarder to 168.63.129.16 for the private zone suffixes the network uses, and that forwarder configuration should be part of the resolver’s baseline build, applied by configuration management rather than added by hand after the first failure. A resolver image or configuration template that ships with the platform forwarder already in place turns the forwarder-to-Azure rule from a thing engineers rediscover during incidents into a thing that is simply always true.
Monitoring closes the loop. Resolution failures are visible in the signals that flow into a Log Analytics workspace, and a query that surfaces them turns a silent failure into an alert. A workspace that collects the relevant logs can be queried for failed lookups and for connections that fell back to public addresses when private ones were expected. A starting query looks for resolution failures over a recent window and groups them by the name that failed, so that a single misconfigured zone or forwarder shows up as a cluster of failures on the same name rather than as scattered noise.
DnsEvents
| where TimeGenerated > ago(1h)
| where ResultCode != 0
| summarize Failures = count() by Name, ResultCode, ClientIP
| where Failures > 5
| order by Failures desc
The query above is a pattern rather than a finished artifact, because the exact table and columns depend on what your environment collects, but the shape is the point. You group failures by the failing name and the client, you threshold on a count that filters transient noise, and you surface the names that fail repeatedly. A name that suddenly starts appearing in this result is a zone that lost its link, a forwarder that broke, or a record that was deleted, and catching it as an alert rather than as an incident is the difference between a five minute correction and an outage. The practice and the drills that build this kind of diagnostic reflex are exactly what the companion platforms are for: you can run the hands-on Azure labs and command library on VaultBook to build and break a resolution path in a sandbox until the checks are second nature, and work through scenario-based troubleshooting drills on ReportMedic to rehearse the three-query diagnosis against incidents you have not seen before, so that the path table is a habit rather than a document you consult under pressure.
There is a final preventive discipline that pays for itself, and it is documentation of the resolution design itself. In a complex environment with custom resolvers, conditional forwarders, multiple private zones, and on-premises forwarding, the resolution path is a system in its own right, and an engineer who does not know its design will rediscover it during every incident. A short, current diagram of which networks use which resolvers, which resolvers forward which suffixes to the platform, and which zones are linked to which networks turns the three-query diagnosis from an exploration into a confirmation. You already know where the hops are, so you only have to find which one broke.
Related Failures Often Confused With Azure DNS
Resolution failures share symptoms with several other problems, and misattributing one to the other sends engineers down the wrong path. The most common confusion is between a resolution failure and a connectivity failure. An application cannot reach a service, and the engineer assumes a firewall or a route is blocking it, when in fact the name resolved to the wrong address and the connection is being refused at the wrong destination. The tell is to check what the name resolved to before checking whether the path to it is open. If the name resolved to a public address when a private one was expected, the problem is resolution, not connectivity, and no amount of firewall inspection will fix it. If the name resolved to the correct private address and the connection still fails, then the problem really is connectivity, and the investigation moves to security rules and routes.
A second confusion is between a resolution failure and an authentication or authorization failure. A service rejects a client, and because the rejection comes after the name resolved and the connection opened, the engineer correctly rules out resolution, but the rejection itself is sometimes a downstream effect of resolving to the wrong endpoint. A client that resolved a service to its public address and connected there may be rejected by a firewall on the service that only permits private access, and the rejection looks like an access problem when its root is a resolution problem. The discipline is the same: confirm what the name resolved to, and confirm it is the address the client was supposed to reach, before accepting that the failure is purely about access.
A third confusion is between a resolution failure and a caching problem, and this one cuts both ways. An engineer fixes a zone link or a forwarder, the platform now resolves the name correctly, and the client still fails, leading to the conclusion that the fix did not work. The fix did work, but the client is holding a cached failure from before the fix and will keep returning it until the cache entry expires. The inverse also happens: a name that should now fail keeps resolving because a positive answer is cached. Caching means that the live state of resolution and the answer a client returns can disagree for the duration of a cache entry, and any diagnosis that does not account for caching will occasionally reach the wrong conclusion. After any resolution fix, clear the relevant caches and reproduce the query fresh, both at the client and within the application runtime, before declaring success or failure.
A fourth area of confusion involves the platform service names themselves. Some Azure services present a name that resolves through a public path even when accessed from inside a network, and engineers sometimes expect a private resolution that the service does not provide without an explicit private endpoint and its zone. The absence of a private endpoint is not a resolution failure, it is a design gap, and the fix is to provision the private path rather than to chase the resolver. Distinguishing a service that lacks a private endpoint from a private endpoint whose resolution is broken keeps the investigation honest. The first is a provisioning task, the second is a path repair, and treating one as the other wastes the effort.
A fifth confusion is between a resolution failure and a record-propagation delay. When a record is newly created or changed, the answer can take time to become consistent across every resolver and cache that might serve it, and a query run in the window before consistency is reached can return the old answer or no answer at all. Engineers sometimes read this transient state as a permanent failure, change a configuration that was already correct, and then attribute the eventual success to their change rather than to the propagation completing on its own. The discipline is to give a newly created or changed record a moment to become consistent, and to query it fresh with caches cleared, before concluding that anything is broken. A failure that resolves itself within a short window without any change on your part was a propagation delay, not a misconfiguration, and chasing it as a misconfiguration teaches the wrong lesson for the next incident. The way to tell the two apart is repetition: a propagation delay clears on its own and does not return, while a real misconfiguration fails consistently on every fresh query until the broken hop is repaired. When a name fails, succeeds a few minutes later without intervention, and then stays healthy, the cause was timing, and the correct response is patience and a fresh query rather than a configuration change that takes undeserved credit for the recovery.
Why Peering Does Not Share Resolution
A failure that catches engineers building hub-and-spoke topologies is the assumption that peering two networks makes them share name resolution. Peering connects two networks at the traffic level, so machines in one can reach machines in the other, but it does not share the resolution configuration between them. A private zone linked to one network is not automatically resolvable from a peered network, because the link is per-network and peering does not extend it. A name that resolves perfectly from the network the zone is linked to will fail from a peered network that lacks its own link, even though the two networks are connected and traffic flows freely between them.
The model to hold is that connectivity and resolution are separate concerns that happen to travel together in the simple case. When a single network is linked to a zone, its machines both reach the address and resolve the name, so the two concerns appear unified. The moment you peer a second network and expect its machines to resolve the same private names, the concerns separate, because the second network was never linked to the zone. Its machines can reach the private address if they somehow obtain it, but they cannot obtain it through resolution, because the platform will not answer the zone for a network that is not linked to it. The fix is to link the zone to the second network as well, so that the platform answers the zone for both, after which the peered machines resolve the name as cleanly as the original network’s machines.
This is the common hub-and-spoke resolution mistake. A team centralizes private zones in a hub network, links the zones to the hub, peers each spoke to the hub, and expects the spokes to resolve the hub’s private zones. The spokes can reach the hub, but they cannot resolve the hub’s zones, because each spoke needs its own link to each zone it must resolve. The design that actually works either links every zone to every network that must resolve it, which scales poorly as the topology grows, or centralizes resolution through a forwarder or a resolver endpoint in the hub that every spoke’s resolver path leads to. The forwarder approach scales because the spokes forward to a single point that is linked to the zones, rather than each spoke carrying a link to every zone, and it is the pattern most large environments converge on. The broader topology in which this sits, and the way central network services are shared across spokes, is the subject of the wider networking model rather than of resolution alone.
The diagnostic signature of a peering-resolution failure is precise enough to recognize. A private name resolves from one network and fails from a peered network, while traffic between the two networks flows normally. The working network is linked to the zone, the peered network is not, and the peering that connects them at the traffic level does nothing for resolution. The fix is the link, or a resolution path that leads the peered network’s queries to a point that holds the link. When you see resolution that works in one network and fails in a connected one, do not investigate the peering, which is doing its job, investigate the zone links, which is where the resolution actually lives.
There is a related subtlety with multiple private zones for different services. A network that must resolve several private services needs a link to each of those services’ zones, not a single link that covers all of them, because each service’s private namespace is a separate zone. A network linked to the storage private zone but not the database private zone will resolve storage names privately and fail to resolve database names, producing a failure that appears only for the unlinked service. When a network resolves some private services and not others, the missing services point at missing zone links for those specific services, and the fix is to add the links the network lacks. This per-service, per-network linking is the reason the resolution design grows complex in large environments, and it is the reason centralizing the links behind a forwarder or resolver endpoint becomes the maintainable pattern as the number of services and networks rises.
The Verdict: Trace the Path, Fix the Hop
Azure DNS resolution is not a black box that is either working or broken. It is a path with named hops, and every failure lives at one of them. The VNet setting decides which resolver the machines use. The lease delivers that setting to the guest, with a delay that explains a surprising number of incidents. The interface can override the network for a single machine, hiding in plain sight. The platform resolver at 168.63.129.16 answers public names and the private zones linked to the querying network, and a direct query at it partitions the whole chain into a healthy half and a broken half in a single step. A custom resolver answers what it knows and must forward what it does not to the platform, which is the forwarder-to-Azure rule, the single most common cause of private-name failures in environments that run their own servers. The private zone answers a network only when that network is linked to it, and the record resolves only when it exists, whether placed by hand, by auto-registration, or by an endpoint’s zone group. On-premises clients reach Azure private names only through a forwarder or an inbound resolver endpoint that bridges into the platform. And a name that resolves to the wrong address is a view problem, a missing link or a resolver answering ahead of the private zone, not a resolution failure in the narrow sense.
The method that ties these together is the three-query diagnosis. Ask the name the way the application asks it and record the answering server. Ask it directly at the platform resolver. Ask it at the custom resolver if one is in the path. The place where the answers diverge is the broken hop, and the path table names the fix for that hop. This is a binary search across a known chain, not a hunt, and it converges in minutes on incidents that otherwise consume hours. The engineer who reaches for the hosts file has abandoned the chain and is treating the symptom on one machine while the broken hop continues to fail everywhere else. The engineer who walks the path fixes the cause once and prevents its recurrence.
The strategic takeaway is that name resolution rewards a model over a reflex. Memorize the platform address. Hold the path as an ordered chain. Know that the lease delays the setting, that the interface can override the network, that custom resolvers need a forwarder to the platform, that zones need links, and that the wrong answer is a view problem. With that model in place, the next resolution incident is not a crisis to survive but a path to walk, and the difference between those two experiences is the difference between an engineer who understands Azure resolution and one who merely uses it. The accuracy of the platform resolver address and the forwarder behavior described here are stable parts of the platform, but resolver and private-resolution features evolve, so it is worth confirming the current managed-resolver options against the platform documentation periodically and updating the design accordingly.
One closing habit ties the whole method together and outlasts any single incident. After you fix a resolution failure, write down which hop broke and why, and add it to the short resolution-design document the environment should keep. Over a few incidents that record becomes a map of the weak hops in your particular environment, the custom resolver that keeps losing its forwarder, the spoke that keeps missing a zone link, the on-premises rule that lags a new service. The next failure then starts not from a blank page but from a ranked list of the hops most likely to have broken, and the three-query diagnosis confirms which one it was this time. That is how a reactive skill becomes a proactive one: the path stays the same, but your knowledge of where it tends to break in your own estate grows with every incident you close.
Frequently Asked Questions
Why is DNS not resolving in my Azure VNet?
A name fails to resolve in a virtual network when one hop in the resolution path is broken: the network points at a resolver that cannot answer the name, a custom resolver lacks a forwarder to the platform, the private zone is not linked to the network, or the record does not exist. Identify which by asking the name the way the application does, then asking it directly at the platform resolver at 168.63.129.16, and noting where the answers diverge.
Does a custom DNS server need a forwarder to Azure?
Yes, for private zones. A custom resolver answers the names it hosts and the public names its upstream knows, but it has no inherent knowledge of Azure private zones. To resolve those, it must forward the unmatched queries, or the specific private suffix, to the platform resolver at 168.63.129.16. A private name that fails on a custom resolver while public names succeed is almost always a missing forwarder to that address.
What is the 168.63.129.16 Azure DNS resolver?
It is the Azure-provided platform resolver, a virtual address reachable from inside any virtual network. It answers public internet names, Azure service names, and the private zones linked to the network the query came from. It is reachable only from inside Azure, not from on-premises, and it is the address that custom resolvers must forward to in order to resolve private zones. Querying it directly is the fastest way to tell whether the platform can answer a name.
Why does a Private DNS zone name not resolve?
The most frequent cause is a missing virtual network link: the zone exists and holds the record, but the network the query came from is not linked to it, so the platform refuses to answer it for that network. The next most frequent is a missing record inside a linked zone, often because auto-registration or an endpoint zone group that was expected to create it did not. List the zone’s links and its records to confirm which gap you have.
Why does a VM not pick up the changed VNet DNS setting?
The resolver list is delivered to the guest at lease renewal, not the instant the network property changes, so a machine keeps using its old resolver until the lease renews. Force the renewal by restarting the guest networking, releasing and renewing the lease, or rebooting. If the machine still ignores the change after a clean renewal, look for a resolver hard-coded inside the guest, which overrides the value the network hands out.
How do I confirm an Azure DNS resolution problem?
Run three queries. Ask the name the way the application asks it, with no resolver override, and note the answering server and the answer. Ask the same name directly at 168.63.129.16. If a custom resolver is in the path, ask it there too. The place where the answers diverge is the broken hop. A healthy platform answer with a failing client answer points at the client path; a failing platform answer points at the zone link or the record.
Why does nslookup work but my application still cannot resolve the name?
The command line tool and the application often use different resolvers or different caches. A manual lookup may specify a resolver the application does not use, testing a path the application never travels, or the application may hold a cached failure from before the fix. Reproduce the query exactly as the application experiences it, with no resolver override, and clear the application runtime’s resolver cache before concluding the fix did not work.
Should I edit the hosts file to fix a name that will not resolve?
No, except as a momentary diagnostic test. A hosts entry overrides resolution on one machine and masks the broken hop, which continues to fail on every other machine and will reappear the moment anything changes. It also goes stale silently when the real address changes. Use the failing name to find the broken hop in the resolution path and fix that hop, so the name resolves correctly everywhere rather than on the one machine you patched.
Can a DNS server set on the NIC override the VNet DNS setting?
Yes. A network interface can carry its own resolver list, and when it does, that list overrides the network default for the machine attached to it. This is why one machine can resolve differently from every other machine in the same network. Inspect the interface’s DNS settings directly; an empty result means it defers to the network, while a populated result is what the machine actually uses, regardless of the network setting.
How long does a changed VNet DNS setting take to take effect?
The change is saved on the network immediately, but each machine applies it only at its next lease renewal, so the effective delay depends on the lease timing rather than on the change itself. Machines can therefore disagree with the network setting during the window before renewal. To apply the new resolver at once, force a lease renewal on the guest or restart its networking rather than waiting for the renewal to occur on its own schedule.
How do on-premises clients resolve Azure Private DNS zone names?
They cannot reach the platform resolver directly, because 168.63.129.16 only answers from inside a virtual network. The bridge is something inside Azure the on-premises resolver can reach and that forwards to the platform: an inbound resolver endpoint or a forwarder virtual machine in a network. Configure the on-premises resolver with a conditional forward for the Azure private suffix pointing at that bridge, and the private names resolve from on-premises through it.
Should I use Azure DNS Private Resolver instead of a custom DNS VM?
A managed resolver removes the burden of running, patching, and making highly available a forwarder virtual machine of your own, while providing inbound and outbound forwarding endpoints for hybrid resolution. A self-managed resolver gives more control over local zones and forwarding logic at the cost of that maintenance. For most environments whose only need is bridging on-premises to Azure private zones, the managed resolver is the lower-maintenance choice; confirm the current capabilities against the platform documentation before deciding.
Why does a query return SERVFAIL for a private zone name?
A server-failure response means a resolver tried to answer and could not complete the resolution, often because it forwarded the query upstream and the upstream failed or timed out. On a custom resolver, this commonly indicates a forwarder pointing at an address that cannot answer the private zone, or a forwarder to the platform that is being blocked by a firewall. Confirm the forwarder targets 168.63.129.16 and that traffic to it is permitted.
Can a wrong DNS server cause intermittent resolution failures?
Yes. When a machine is configured with more than one resolver and one of them cannot answer a given name, queries that happen to land on the broken resolver fail while queries that land on a working one succeed, producing failures that come and go without an obvious pattern. Inspect the full resolver list the machine uses, not just the first entry, and ensure every resolver in the list can resolve the names the machine needs.
Why does flushing the DNS cache not fix the resolution problem?
Flushing a cache only removes stored answers; it does nothing to repair a broken hop in the resolution path. If the underlying cause is a missing forwarder, an unlinked zone, or a wrong resolver, the cache will simply refill with the same failure on the next query. Flush the cache only to rule out a stale entry as the cause, then, if the failure persists, walk the path to find and fix the hop that is actually broken.
Does restarting the VM apply a DNS setting change faster than waiting?
A restart forces the guest networking to reinitialize, which applies the current network resolver setting immediately rather than waiting for the lease to renew on its own schedule, so it is faster than waiting. A lighter alternative that avoids the downtime is to restart the guest networking service or release and renew the lease, which applies the new resolver without a full reboot. Either approach closes the window in which the guest and the network setting disagree.