Private DNS Zones Created by ARO
When you deploy a private ARO cluster, Azure automatically creates two private DNS zones in the ARO-managed resource group — one for the API server and one for application ingress. You own neither; they are managed by the ARO service, but you are responsible for linking them to every VNet that needs to resolve them.

The Two Zones ARO Creates
ARO creates both zones inside the ARO managed resource group — the resource group whose name starts with aro- that Azure creates automatically alongside your cluster. You cannot modify or delete these zones without breaking the cluster.
Zone 1 — API Server
Zone name: cluster.<unique-id>.<region>.aroapp.ioExample: cluster.a1b2c3d4.eastus.aroapp.ioA records: api → 10.1.0.8 (private endpoint NIC IP)Full FQDN: api.cluster.a1b2c3d4.eastus.aroapp.io:6443
Zone 2 — Application Ingress
Zone name: cluster.<unique-id>.<region>.aroapp.io (same parent zone, different record)A records: *.apps → 10.1.1.100 (internal load balancer frontend IP)Example resolutions: my-app.apps.cluster.a1b2c3d4.eastus.aroapp.io → 10.1.1.100 console.apps.cluster.a1b2c3d4.eastus.aroapp.io → 10.1.1.100 grafana.apps.cluster.a1b2c3d4.eastus.aroapp.io → 10.1.1.100
Inspecting the Zones After Deployment
# Get the ARO managed resource groupMANAGED_RG=$(az aro show \ --resource-group rg-aro \ --name aro-prod \ --query clusterProfile.resourceGroupId \ --output tsv | xargs basename)# List all private DNS zones ARO createdaz network private-dns zone list \ --resource-group $MANAGED_RG \ --query "[].{Zone:name, Records:numberOfRecordSets}" \ --output table# Output:# Zone Records# ─────────────────────────────────────────── ─────────# cluster.a1b2c3d4.eastus.aroapp.io 4# List all A records in the zoneaz network private-dns record-set a list \ --resource-group $MANAGED_RG \ --zone-name cluster.a1b2c3d4.eastus.aroapp.io \ --output table# Output:# Name TTL ARecords# ────── ──── ──────────────────────# api 300 [{'ipv4Address': '10.1.0.8'}]# *.apps 300 [{'ipv4Address': '10.1.1.100'}]# List VNet links on the zoneaz network private-dns link vnet list \ --resource-group $MANAGED_RG \ --zone-name cluster.a1b2c3d4.eastus.aroapp.io \ --output table# Output:# Name VirtualNetwork RegistrationEnabled# ─────────────────────── ────────────────────── ───────────────────# aro-spoke-vnet-link aro-spoke-vnet false ← auto-created
The VNet Linking Problem
This is the most common post-deployment mistake. ARO automatically links the private DNS zone to only one VNet — the ARO spoke VNet. Every other VNet that needs to resolve the API server or app routes must be manually linked:
ZONE_NAME="cluster.a1b2c3d4.eastus.aroapp.io"# Link to hub VNet (required for jump host, Bastion, CI/CD runners)az network private-dns link vnet create \ --resource-group $MANAGED_RG \ --zone-name $ZONE_NAME \ --name "link-to-hub-vnet" \ --virtual-network $(az network vnet show \ --resource-group rg-hub \ --name hub-vnet \ --query id -o tsv) \ --registration-enabled false# Link to other spoke VNets if they need to call ARO-hosted APIsaz network private-dns link vnet create \ --resource-group $MANAGED_RG \ --zone-name $ZONE_NAME \ --name "link-to-spoke2-vnet" \ --virtual-network $(az network vnet show \ --resource-group rg-spoke2 \ --name spoke2-vnet \ --query id -o tsv) \ --registration-enabled false
registration-enabled false is correct here — you are linking for resolution only, not to auto-register VM hostnames into the zone.
On-Premises DNS Conditional Forwarding
On-premises DNS servers cannot be linked to Azure private DNS zones directly — they resolve through the DNS Private Resolver inbound endpoint using conditional forwarding:
On-premises Windows DNS Server: Conditional Forwarder: Domain: aroapp.io Forward to: 10.0.5.4 (DNS Private Resolver inbound endpoint)On-premises BIND (Linux): zone "aroapp.io" { type forward; forwarders { 10.0.5.4; }; };
With this in place, an on-premises developer running oc login gets the full resolution chain:
1. oc login https://api.cluster.a1b2c3d4.eastus.aroapp.io:64432. Laptop DNS → corp DNS server3. Corp DNS: aroapp.io → forward to 10.0.5.44. DNS Private Resolver checks linked private DNS zones5. Finds: api.cluster.a1b2c3d4.eastus.aroapp.io → 10.1.0.86. Returns 10.1.0.8 to laptop7. oc connects to 10.1.0.8:6443 via ExpressRoute / VPN tunnel8. Login succeeds ✅
OpenShift Console DNS
The OpenShift web console runs as an application on the cluster, so it resolves through the *.apps wildcard record:
# Get console URLaz aro show \ --resource-group rg-aro \ --name aro-prod \ --query consoleProfile.url \ --output tsv# Output:# https://console-openshift-console.apps.cluster.a1b2c3d4.eastus.aroapp.io# DNS resolution:# console-openshift-console.apps.cluster.a1b2c3d4.eastus.aroapp.io# → matched by *.apps wildcard A record# → returns 10.1.1.100 (internal LB)# → browser connects via VPN/ER or Bastion proxy
Custom Domain — Replacing aroapp.io
If you want your own domain (e.g. openshift.contoso.com) instead of aroapp.io, you create a custom private DNS zone and manage the records yourself:
# Create your own private DNS zoneaz network private-dns zone create \ --resource-group rg-aro-network \ --name openshift.contoso.com# Add API server A recordaz network private-dns record-set a add-record \ --resource-group rg-aro-network \ --zone-name openshift.contoso.com \ --record-set-name api \ --ipv4-address 10.1.0.8# Add wildcard apps A recordaz network private-dns record-set a add-record \ --resource-group rg-aro-network \ --zone-name openshift.contoso.com \ --record-set-name "*.apps" \ --ipv4-address 10.1.1.100# Link to all VNetsaz network private-dns link vnet create \ --resource-group rg-aro-network \ --zone-name openshift.contoso.com \ --name link-hub \ --virtual-network /subscriptions/.../hub-vnet \ --registration-enabled false
Then update the ARO cluster to use the custom domain during deployment:
az aro create \ --resource-group rg-aro \ --name aro-prod \ --vnet aro-spoke-vnet \ --master-subnet master-subnet \ --worker-subnet worker-subnet \ --apiserver-visibility Private \ --ingress-visibility Private \ --domain openshift.contoso.com \ # ← custom domain --pull-secret @pull-secret.txt
With a custom domain the API server becomes api.openshift.contoso.com and apps become *.apps.openshift.contoso.com — owned and managed entirely by you, with no dependency on aroapp.io.
Key Takeaway
ARO automatically creates a private DNS zone under aroapp.io with two critical records — api pointing to the API server private endpoint IP and *.apps pointing to the internal load balancer frontend IP. The zone is auto-linked only to the ARO spoke VNet — you must manually link it to the hub VNet, any other spoke VNets, and configure on-premises DNS conditional forwarding to the DNS Private Resolver for the complete name resolution chain to work end-to-end across your hub and spoke estate.