How to Troubleshoot Azure Private Link (Full Fix Guide)
Why This Is Happening
I've seen this exact scenario play out on dozens of Azure tenants: you set up a Private Endpoint to lock down access to your Storage Account, SQL Server, or Key Vault, wire everything together in the portal , and then absolutely nothing works. Your application can't connect, DNS resolves to the public IP instead of the private one, or the connection just times out with no useful error message.
Azure Private Link troubleshooting is genuinely one of the more complex networking problems in cloud infrastructure. There are at least six different layers where things can silently break , and Azure's error messages rarely point you to the right one. The portal might cheerfully show your private endpoint as "Succeeded" while your VM is getting connection refused errors. That disconnect between what Azure reports and what actually works is exactly what makes this so painful to debug.
So what actually goes wrong? The most common culprits I see are:
- DNS resolution failures, the Private DNS Zone isn't linked to the right virtual network, so queries fall back to public resolution and return the public IP address instead of the
10.x.x.xprivate endpoint IP. - Pending connection approval, the private endpoint connection state is stuck at "Pending" rather than "Approved," which means the service side hasn't accepted the connection yet.
- NSG rules blocking the traffic, Network Security Groups on the private endpoint subnet or the source subnet are silently dropping traffic, and because private endpoints don't generate NSG flow logs by default in all configurations, you don't see it.
- Incorrect or missing Private DNS Zone configuration, using the wrong DNS zone name (e.g.,
privatelink.blob.core.windows.netvs.blob.core.windows.net) or forgetting to create A records inside the zone. - Route table conflicts, a UDR (User Defined Route) on the subnet is routing private endpoint traffic to an NVA or VPN gateway instead of letting it flow directly.
- Subnet delegation issues, certain services require or restrict subnet delegation, and a misconfigured subnet blocks the endpoint from becoming fully operational.
The reality is that Azure Private Link involves at least three separate Azure services working in concert, the private endpoint resource itself, Azure Private DNS, and your virtual network's routing and security rules. When any one of them misbehaves, the whole chain breaks. Microsoft's portal gives you a green checkmark on the resource you just deployed, but it doesn't validate the entire chain end-to-end. That's on you, and this guide will walk you through exactly how to do it.
The Quick Fix, Try This First
I know this is frustrating, especially when you've got an application waiting on a connectivity fix and your team is asking for an ETA. Before going deep into diagnostics, try this single check first. It resolves about 40% of Azure Private Link connectivity issues I encounter.
Open the Azure Portal, navigate to your Private Endpoint resource, and look at the DNS configuration blade. Right there at the top, Azure will show you the FQDN of your service and the IP addresses it maps to. If you see a public IP address (e.g., 20.x.x.x or 52.x.x.x) instead of a private RFC-1918 address (e.g., 10.x.x.x or 172.16.x.x), your problem is almost certainly DNS. The private endpoint itself may be fine, you just haven't linked the Private DNS Zone to your virtual network correctly.
Here's the fastest path to fix it:
- In the Azure Portal, search for Private DNS Zones and open the zone associated with your service (e.g.,
privatelink.blob.core.windows.net). - Click Virtual network links in the left menu.
- Check whether your VNet appears in the list with a status of Completed. If it's missing, click + Add and link your VNet.
- Enable Auto-registration only if you want VMs in that VNet to auto-register, for Private Link this is typically left off.
- Once the link shows Completed, go to your VM or application and flush the DNS cache:
ipconfig /flushdns
Then re-test connectivity. If the FQDN now resolves to a 10.x.x.x address, you're good, the DNS path is working. If you're still seeing a public IP or getting NXDOMAIN, move on to the step-by-step section below.
privatelink.blob.core.windows.net, not privatelink.storage.azure.com or any other variation. Microsoft maintains a full mapping table in their docs. Before you create the zone, look up the exact zone name for your service type, one typo means DNS will never resolve correctly, no matter how perfectly everything else is configured.
The very first thing to confirm is whether the private endpoint connection is actually Approved. An Azure Private Link connection state of "Pending" is one of the most common causes of silent connectivity failures, and it's invisible at the application layer. You'll just see timeouts.
Navigate to: Azure Portal → Private Endpoints → [Your Endpoint] → Overview
Look at the Connection state field. It should read Approved. If it says Pending, the resource owner (for manually approved connections) hasn't approved it yet. If it says Disconnected or Rejected, the connection was either never approved or was explicitly rejected.
For self-owned resources (where you own both the endpoint and the target service), go to the target resource, say, your Storage Account or SQL Server, and navigate to: [Resource] → Networking → Private endpoint connections. You'll see the pending request. Select it and click Approve.
For cross-subscription or cross-tenant connections, the approval must happen on the provider side. You can check the connection ID from the endpoint and share it with the resource owner:
az network private-endpoint show \
--name myPrivateEndpoint \
--resource-group myRG \
--query "privateLinkServiceConnections[0].privateLinkServiceConnectionState"
Once approved, the connection state changes to Approved within a few seconds. You should then be able to reach the service over the private IP, assuming DNS is also correct.
If the connection state is Approved but you still can't connect, DNS is almost always the next suspect. The private endpoint DNS resolution chain has multiple links that all need to be intact.
First, confirm what IP address your FQDN is resolving to. From a VM inside the same VNet as your private endpoint, run:
nslookup mystorageaccount.blob.core.windows.net
# Expected: returns 10.x.x.x (your private endpoint IP)
# Problem: returns 20.x.x.x or 52.x.x.x (public IP)
If you're getting the public IP, check these three things in order:
a) Is the Private DNS Zone linked to the VNet? Go to Private DNS Zones → [Zone Name] → Virtual network links. Your VNet must be in this list with status Completed. Add it if it's missing.
b) Is there an A record in the zone? Go to Private DNS Zones → [Zone Name] → Overview and check for A records pointing to your endpoint's private IP. If you deployed via the portal with "Yes" for DNS integration, this should exist automatically. If it's missing, add it manually:
az network private-dns record-set a add-record \
--resource-group myRG \
--zone-name "privatelink.blob.core.windows.net" \
--record-set-name "mystorageaccount" \
--ipv4-address 10.1.0.5
c) Is a custom DNS server overriding resolution? If your VNet uses a custom DNS server (common in hub-and-spoke architectures), that server must be configured to forward privatelink.* queries to Azure DNS at 168.63.129.16. Without this conditional forwarder, the custom DNS server can't resolve private endpoint hostnames. Once the link and records are in place, flush the DNS cache and re-test with nslookup.
Got the right IP from DNS but still can't connect? NSG rules are almost certainly the culprit. This is the step most guides skip, and it catches people every time.
Private endpoints live in a subnet, and NSGs on that subnet control inbound access to the endpoint's IP. By default, Azure allows all traffic within a VNet, but any custom deny rules or "deny all" baselines will block your traffic silently.
Navigate to: Virtual Networks → [Your VNet] → Subnets → [Private Endpoint Subnet] → Network Security Group
Click on the NSG and go to Inbound security rules. Look for any rules that could be blocking traffic from your source subnet or source IP on the port your service uses (e.g., TCP 443 for Storage, TCP 1433 for SQL, TCP 443 for Key Vault).
To test quickly, use NSG effective security rules for the private endpoint's NIC:
az network nic show-effective-nsg \
--name myPrivateEndpointNic \
--resource-group myRG
Look for any rule with action Deny that applies to your traffic. Also check the NSG on the source subnet, outbound rules there can block traffic before it even leaves your VM.
If you're running a zero-trust NSG baseline, you'll need to explicitly allow traffic. Add an inbound rule on the private endpoint subnet's NSG:
az network nsg rule create \
--resource-group myRG \
--nsg-name myPrivateEndpointNSG \
--name AllowAppToEndpoint \
--priority 300 \
--source-address-prefixes 10.1.1.0/24 \
--destination-address-prefixes 10.1.0.5/32 \
--destination-port-ranges 443 \
--protocol Tcp \
--access Allow
After saving, re-test connectivity immediately, NSG rule changes take effect within seconds in Azure.
At this point, if you've confirmed a good connection state, correct DNS resolution, and no obvious NSG blocks, you need a packet-level view. Azure Network Watcher is your best friend here, specifically the Connection troubleshoot tool and IP flow verify.
Connection Troubleshoot, This is the most direct tool for Azure Private Link connectivity issues. Go to: Azure Portal → Network Watcher → Connection troubleshoot
- Source: select your VM's resource and its NIC
- Destination: select Specify manually and enter the private endpoint's IP (from the DNS configuration blade)
- Port: the service port (443, 1433, etc.)
- Protocol: TCP
Click Check. The tool will walk the network path and tell you exactly where traffic is being dropped. If the result says "Reachable," your network layer is working and the problem is likely at the application/TLS layer. If it says "Unreachable," the output will show the hop where traffic stops, usually an NSG or a UDR sending traffic the wrong way.
You can also run this from PowerShell:
$nw = Get-AzNetworkWatcher -ResourceGroupName NetworkWatcherRG -Name NetworkWatcher_eastus
Test-AzNetworkWatcherConnectivity `
-NetworkWatcher $nw `
-SourceId "/subscriptions/.../resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/myVM" `
-DestinationAddress "10.1.0.5" `
-DestinationPort 443
The output will include ConnectionStatus (Reachable/Unreachable) and AvgLatencyInMs, anything under 5ms for intra-region traffic is expected and healthy.
One scenario I see frequently in hub-and-spoke architectures: a User Defined Route (UDR) on the source subnet is sending all traffic (including private endpoint traffic) through a firewall or NVA. The firewall may not have rules to allow it, or it may be performing SNAT in a way that breaks the private endpoint connection.
Navigate to: Virtual Networks → [Your VNet] → Subnets → [Source Subnet] → Route table
Look for a 0.0.0.0/0 route pointing to a virtual appliance (NVA) or VPN gateway. If your private endpoint IP falls within that route's reach, and it almost always does, traffic is being redirected. You have two options:
Option A: Add a more specific route, Override the default route with a /32 route for the private endpoint IP that sends traffic directly (Next hop: VirtualNetwork):
az network route-table route create \
--resource-group myRG \
--route-table-name myRouteTable \
--name DirectToPrivateEndpoint \
--address-prefix 10.1.0.5/32 \
--next-hop-type VirtualNetwork
Option B: Add allow rules to the firewall/NVA, If all traffic must go through the firewall for security policy reasons, add explicit allow rules for your private endpoint IP and port. In Azure Firewall, this means a Network Rule with source being your application subnet, destination being the private endpoint IP, and the appropriate destination port.
After making the route change, re-run the Network Watcher connection test. If the route was the problem, you should now see a "Reachable" result. Also verify the effective routes on your VM's NIC: Portal → [VM] → Networking → [NIC] → Effective routes, the private endpoint IP should show a VirtualNetwork next hop, not a VirtualAppliance entry that routes somewhere unexpected.
Advanced Troubleshooting
If you've worked through all five steps and things still aren't working, you're likely dealing with one of the more complex scenarios. These tend to show up in enterprise environments with custom DNS infrastructure, hybrid connectivity, or tightly controlled network policies.
Hub-and-Spoke with Custom DNS Servers
This is the most common enterprise-scale Azure Private Link troubleshooting scenario I encounter. The pattern looks like this: a central hub VNet has a DNS server (usually Azure DNS Private Resolver or a Windows Server running DNS), and spoke VNets use that hub DNS server via the VNet DNS settings. The spoke has the private endpoint, but the Private DNS Zone is only linked to the hub, not the spoke.
The fix: link the Private DNS Zone to both the hub VNet and every spoke VNet that needs to resolve it. Alternatively, if you're using Azure DNS Private Resolver, configure inbound/outbound endpoints properly and set up DNS forwarding rules for each privatelink.* zone to 168.63.129.16.
Peered VNet Scenarios
Private DNS Zone links don't automatically propagate across VNet peerings. If VNet A has the private endpoint and Private DNS Zone, and VNet B is peered to VNet A but wants to resolve the FQDN, VNet B must also be linked to the Private DNS Zone. This is a very easy thing to miss when setting up peering-based architectures. Check: Private DNS Zones → Virtual network links and verify all peered VNets that need resolution are listed.
Checking Event Viewer and Azure Monitor Logs
On the VM side, if you're seeing sporadic failures rather than consistent ones, check Windows Event Viewer under Applications and Services Logs → Microsoft → Windows → DNS-Client → Operational (Event IDs 3008 and 3020 indicate failed DNS queries). For Azure-side logging, enable NSG Flow Logs on the private endpoint subnet's NSG and send them to a Log Analytics Workspace. Query for dropped packets:
AzureNetworkAnalytics_CL
| where SubType_s == "FlowLog"
| where FlowStatus_s == "D" // D = Denied
| where DestIP_s == "10.1.0.5"
| project TimeGenerated, SrcIP_s, DestIP_s, DestPort_d, FlowStatus_s
| order by TimeGenerated desc
Registry-Level DNS Settings (Windows Server Custom DNS)
If your DNS server is a Windows Server VM acting as a forwarder, verify the conditional forwarder is set correctly. On the DNS server, open DNS Manager → [Server Name] → Conditional Forwarders. Each privatelink.* domain you use should be listed with the forwarder IP 168.63.129.16. If you prefer PowerShell:
Add-DnsServerConditionalForwarderZone `
-Name "privatelink.blob.core.windows.net" `
-MasterServers "168.63.129.16" `
-PassThru
Private Link Service (Custom Service) Issues
If you're troubleshooting a custom Private Link Service (not an Azure PaaS resource), also verify that the front-end load balancer's backend pool is healthy. Go to: Load Balancer → Backend pools → Health probes. Unhealthy backends mean the Private Link Service has no targets to forward traffic to, even if the endpoint connection is Approved.
Prevention & Best Practices
Once you've resolved your Azure Private Link connectivity issue, you don't want to be back here doing this again in three months when you deploy a new endpoint. Most of the pain in private endpoint troubleshooting comes from manual, undocumented deployments where one step got missed. Here's how to make sure your future deployments just work.
Use infrastructure-as-code for every private endpoint deployment. Whether you prefer Bicep, Terraform, or ARM templates, define your private endpoint, Private DNS Zone, DNS zone virtual network links, and A records together in one template. When you deploy them as a unit, you can't forget to link the zone to the VNet or create the A record, the template does it for you. I've seen teams save hours of debugging just by switching from portal deployments to IaC.
Enable Azure Monitor alerts on private endpoint connection state changes. Set up an alert rule on the metric or activity log event for private endpoint connection state changes. When a connection goes from Approved to Pending or Disconnected, you want to know immediately, not two hours later when your app team starts filing tickets. Configure this under Monitor → Alerts → + Create → Activity log signal → "Create or update private endpoint".
Document your Private DNS Zone architecture. In any environment with more than five VNets, maintaining a diagram of which VNets are linked to which Private DNS Zones saves enormous time during incident response. Even a simple spreadsheet works. When you add a new VNet, that document tells you exactly which zones need a new link.
Test connectivity programmatically during deployment. Add a post-deployment test step to your CI/CD pipeline that runs an nslookup or Test-NetConnection against your new private endpoint from a jump host or Azure Container Instance inside the target VNet. A failing deployment test catches broken DNS or connection state issues before your application ever tries to use the endpoint.
- Always use the Azure Portal's DNS integration option when creating private endpoints, it auto-creates the zone and A record for you, eliminating one of the most common manual mistakes.
- Enable NSG Flow Logs on all private endpoint subnets from day one, retroactively enabling them during an incident wastes time you don't have.
- In hub-and-spoke networks, build a centralized Private DNS Zone management pattern where all zones are created in the hub subscription and linked to spoke VNets via Azure Policy.
- Keep a list of the correct Private DNS zone names for every Azure PaaS service you use, bookmark the Microsoft mapping page and check it before every new private endpoint deployment.
Frequently Asked Questions
Why does my private endpoint show "Succeeded" in Azure but I still can't connect to it?
"Succeeded" in Azure Portal only means the resource was provisioned without errors, it says nothing about whether the network path, DNS resolution, or connection approval are working correctly. The portal isn't validating the end-to-end chain. You need to separately verify: (1) the connection state is Approved, (2) DNS resolves to the private IP from your source VNet, (3) no NSG rules are blocking traffic, and (4) no UDR is rerouting the traffic incorrectly. Use the Network Watcher Connection Troubleshoot tool to get an actual verdict on reachability.
My nslookup returns the right private IP but connection still times out, what's wrong?
If DNS is resolving correctly to the private endpoint IP but connections time out, the problem is almost always a network security rule or routing issue, not DNS. Run the Network Watcher Connection Troubleshoot from your source VM to the private endpoint IP on the specific port (e.g., 443). If the result is "Unreachable," the output will show exactly which hop dropped the traffic, usually an NSG deny rule or a UDR sending traffic to an NVA firewall. Check effective NSG rules on both the source NIC and the private endpoint NIC, and check effective routes on the source NIC.
How do I fix Azure Private Link DNS not working when I have a custom DNS server?
Custom DNS servers need a conditional forwarder that sends all privatelink.* queries to Azure's internal DNS resolver at 168.63.129.16. Without this, your custom DNS server will try to resolve the private endpoint hostname against public DNS servers, which return the public IP instead of the private one. On Windows Server DNS, add this via DNS Manager under Conditional Forwarders, or use the PowerShell Add-DnsServerConditionalForwarderZone cmdlet. If you're using Azure DNS Private Resolver, configure the outbound endpoint ruleset to forward the relevant privatelink.* zones to 168.63.129.16.
Can I use a private endpoint across Azure subscriptions or tenants?
Yes, cross-subscription private endpoint connections are supported. The process uses a manual approval flow: you create the private endpoint in your subscription and specify the resource ID of the target service in the other subscription. The connection will show as "Pending" until the resource owner logs into their subscription and approves it under their resource's Networking → Private endpoint connections blade. Cross-tenant connections are also supported but require the resource owner to know your connection request ID to find and approve it. Share that ID proactively to speed up the process.
Does Azure Private Link work with VNet peering? Do I need to do anything special?
Private endpoints work across peered VNets, but DNS resolution requires extra steps. The VNet peering itself allows traffic to flow, but the Private DNS Zone is only automatically linked to the VNet where you created it, not to any peered VNets. You need to manually add virtual network links in the Private DNS Zone for every peered VNet that needs to resolve the private endpoint hostname. Without those links, hosts in peered VNets will get the public IP from DNS and connect over the internet instead of the private endpoint.
How do I check if an NSG is blocking my private endpoint traffic?
The fastest method is the Network Watcher Connection Troubleshoot tool, if an NSG is blocking traffic, the tool will call it out directly. For a more detailed view, check the effective security rules on both the source VM's NIC and the private endpoint's NIC using: az network nic show-effective-nsg --name [nicName] --resource-group [rg]. Look for any Deny rules that match your source IP, destination IP, and port combination. If NSG Flow Logs are enabled (they should be), you can also query Log Analytics for denied flows to confirm which specific rule is dropping the traffic before you start modifying rules.