Private DNS Zones in Azure
Private DNS zones give your Azure VNets their own internal namespace. VMs register their names automatically, Private Endpoints resolve to private IPs instead of public IPs, and cross-VNet hostname resolution works without custom DNS servers. Private DNS zones are the foundation of private networking in Azure — if you use Private Endpoints, you need to understand them.
What private DNS zones are
A Private DNS zone is a DNS zone that is only resolvable from VNets you explicitly link it to. The zone is hosted by Azure and managed through the Azure control plane, but unlike Azure DNS (public zones), it is completely invisible to the public internet.
You can name a private zone anything — internal.mycompany.com, prod.azure.local, or privatelink.blob.core.windows.net. The name does not need to be a registered public domain. VMs in linked VNets can query the zone through Azure-provided DNS at 168.63.129.16. No external configuration is needed — the link between the zone and the VNet is what activates resolution.
Creating a private DNS zone
Private DNS zones are global resources — they are not tied to a specific region. They live in a resource group, but the zone itself has no region requirement:
# Create a private DNS zone for internal services
az network private-dns zone create \
--name internal.mycompany.com \
--resource-group rg-dnsAfter creating the zone, it has no VNet links and resolves nothing. You must link it to at least one VNet for it to function.
Linking zones to VNets
A VNet link associates a private zone with a VNet. Once linked, VMs in that VNet can resolve records in the zone through Azure-provided DNS. Link with optional auto-registration enabled:
# Link the zone to the hub VNet (without auto-registration)
az network private-dns link vnet create \
--name link-hub-vnet \
--resource-group rg-dns \
--zone-name internal.mycompany.com \
--virtual-network vnet-hub-eastus \
--registration-enabled false
# Link the zone to the spoke VNet (with auto-registration)
# VMs in this VNet will auto-register their names
az network private-dns link vnet create \
--name link-spoke-app-vnet \
--resource-group rg-dns \
--zone-name internal.mycompany.com \
--virtual-network vnet-spoke-app-eastus \
--registration-enabled trueWith auto-registration enabled, every VM in vnet-spoke-app-eastus automatically gets an A record in internal.mycompany.com when it is created, and the record is removed when the VM is deleted. Only one zone per VNet can have auto-registration enabled.
A single private DNS zone can be linked to up to 1000 VNets. A single VNet can be linked to up to 1000 private DNS zones. These limits are high enough for most architectures but can be hit in very large hub-and-spoke deployments with many zones per service type.
Adding records manually
Add A records for services that are not VMs (load balancers, internal APIs, private endpoints):
# Add an A record for an internal API service
az network private-dns record-set a add-record \
--resource-group rg-dns \
--zone-name internal.mycompany.com \
--record-set-name "api" \
--ipv4-address 10.0.2.50
# Add a CNAME alias
az network private-dns record-set cname set-record \
--resource-group rg-dns \
--zone-name internal.mycompany.com \
--record-set-name "db" \
--cname "sql-prod-001.database.windows.net"
# List all records in the zone
az network private-dns record-set list \
--resource-group rg-dns \
--zone-name internal.mycompany.com \
--output tablePrivate Endpoints and the privatelink zones
When you create a Private Endpoint for an Azure service (Storage, SQL Database, Key Vault, etc.), that endpoint gets a private IP in your VNet. For name resolution to work correctly — so the service’s hostname resolves to the private IP rather than the public IP — you need a Private DNS zone with a specific name matching the service’s privatelink subdomain.
| Azure service | Required private DNS zone name |
|---|---|
| Azure Blob Storage | privatelink.blob.core.windows.net |
| Azure File Storage | privatelink.file.core.windows.net |
| Azure SQL Database | privatelink.database.windows.net |
| Azure Key Vault | privatelink.vaultcore.azure.net |
| Azure Container Registry | privatelink.azurecr.io |
| Azure Cosmos DB (SQL) | privatelink.documents.azure.com |
Practical example — Private Endpoint for a Storage Account:
# 1. Create the private endpoint for the storage account
STORAGE_ID=$(az storage account show \
--name mystorageaccount \
--resource-group rg-prod \
--query id \
--output tsv)
az network private-endpoint create \
--name pe-mystorageaccount-blob \
--resource-group rg-networking \
--vnet-name vnet-spoke-app-eastus \
--subnet snet-private-endpoints \
--private-connection-resource-id $STORAGE_ID \
--group-id blob \
--connection-name conn-mystorageaccount-blob
# 2. Create the Private DNS zone
az network private-dns zone create \
--name privatelink.blob.core.windows.net \
--resource-group rg-dns
# 3. Link the zone to the VNet
az network private-dns link vnet create \
--name link-spoke-app-to-blobdns \
--resource-group rg-dns \
--zone-name privatelink.blob.core.windows.net \
--virtual-network vnet-spoke-app-eastus \
--registration-enabled false
# 4. Get the private endpoint's network interface private IP
PE_NIC=$(az network private-endpoint show \
--name pe-mystorageaccount-blob \
--resource-group rg-networking \
--query "networkInterfaces[0].id" \
--output tsv)
PRIVATE_IP=$(az network nic show \
--ids $PE_NIC \
--query "ipConfigurations[0].privateIPAddress" \
--output tsv)
# 5. Add the A record to the private DNS zone
az network private-dns record-set a add-record \
--resource-group rg-dns \
--zone-name privatelink.blob.core.windows.net \
--record-set-name "mystorageaccount" \
--ipv4-address $PRIVATE_IPAfter these steps, queries for mystorageaccount.blob.core.windows.net from within the linked VNet resolve to the private endpoint IP (10.x.x.x) instead of the public IP.
Multiple VNet linking and DNS resolution flow
One private DNS zone can serve multiple VNets by adding additional VNet links. This is the pattern for hub-and-spoke architectures: create the zone once, link it to the hub VNet and all spoke VNets.
DNS resolution flow for a VM in a spoke VNet querying a private endpoint hostname:
1. VM queries Azure-provided DNS at 168.63.129.16.
2. Azure-provided DNS checks for private DNS zones linked to the VM’s VNet.
3. It finds a zone link for privatelink.blob.core.windows.net.
4. It queries that zone and finds an A record for mystorageaccount pointing to 10.0.5.4.
5. The VM receives 10.0.5.4 and connects directly to the private endpoint.
Split-brain DNS with private zones
Split-brain (or split-horizon) DNS returns different answers for the same hostname depending on where the query originates. Create a private zone with the same name as a public zone to achieve this.
Example: api.mycompany.com should resolve to a private IP for VMs inside Azure but to a public IP for external users. Create a private zone named api.mycompany.com, add an A record pointing to the private IP, and link it to the application VNets. The public Azure DNS zone for api.mycompany.com continues to serve the public IP. Internal queries find the private zone link and get the private IP. External queries never see the private zone and get the public IP from Azure DNS.
Common mistakes
- Creating the private zone without linking it to the VNet. A private DNS zone that is not linked to a VNet is completely invisible to all VMs. The zone can have perfectly configured records, but until a VNet link exists, no VM can resolve them. After creating a zone, always verify the VNet link is in “Succeeded” state before testing DNS resolution.
- Forgetting to create the private DNS zone when deploying Private Endpoints via IaC. The Azure portal’s private endpoint wizard offers to create the DNS zone and VNet link automatically. The az CLI private-endpoint create command and Terraform azurerm_private_endpoint resource do not create DNS resources automatically. You must create the private DNS zone, VNet link, and DNS zone group (or A record) as separate resources in your IaC code.
- Using auto-registration on the wrong VNet. Auto-registration is appropriate for VNets containing VMs that need their names registered. If you enable it on a VNet that has Private Endpoints (not VMs), the auto-registration does nothing useful and you may exhaust your limit of one auto-registration zone per VNet, preventing future use. Reserve auto-registration for VNets with workload VMs that need dynamic name registration.
Summary
- Private DNS zones are internal-only DNS zones, resolvable only from VNets they are linked to. They have no presence on the public internet.
- VNet links activate the zone for a VNet. Enable auto-registration on links to VNets containing workload VMs for automatic A record management.
- Private Endpoints require a specific privatelink.* private DNS zone, a VNet link, and an A record mapping the service hostname to the private endpoint IP.
- One zone can be linked to multiple VNets — link the zone to every VNet whose resources need to resolve private endpoint hostnames.
- When deploying private endpoints via CLI or Terraform, DNS resources (zone, link, record) are not created automatically — you must provision them explicitly.
Frequently asked questions
What is the difference between Azure DNS and Azure Private DNS zones?
Azure DNS (public) hosts zones for public internet domains — anyone can query it. Private DNS zones are only queryable from VNets they are linked to. A private zone named internal.mycompany.com is invisible to the public internet. It is used for internal service discovery, Private Endpoint name resolution, and VM hostname registration within VNets.
What does auto-registration do in Private DNS zones?
When auto-registration is enabled on a VNet link, Azure automatically creates A records in the private zone for every VM deployed into that VNet. The record maps the VM's name to its private IP. When the VM is deleted, the record is removed. Only one private DNS zone per VNet can have auto-registration enabled.
Why does my private endpoint not resolve to the private IP?
Three things must all be in place: a Private DNS zone with the correct privatelink.* name, a VNet link from the zone to the VNet containing the private endpoint, and an A record in the zone mapping the resource's hostname to the private endpoint IP. If any one of these is missing, DNS returns the public IP and traffic bypasses the private endpoint. When creating private endpoints via CLI or Terraform, these DNS resources are not created automatically — you must create them or use a DNS zone group.