Why this matters
Private DNS tells a service where to send traffic. When you run workloads in AWS, Azure and Google Cloud, each cloud starts with its own resolver and its own private zone model. Leave those defaults alone and a host in one cloud cannot resolve a private name in another. It may even resolve a public record by mistake.
The fix is one namespace with clear ownership and a resolver path that keeps private queries off the public internet. Do that and you can move a workload between clouds without renaming every service. DNS also carries policy: query logs show who looks up what, zone delegation decides who can create records, and TTLs decide how fast a bad record stops hurting you.
Before you start
Inventory every private zone you have. Include cloud-native zones, Active Directory DNS zones and zones on Linux BIND or CoreDNS hosts. For each one, note the domain, the owner, the networks that can query it, and whether it is authoritative or a forwarder. You will find duplicates. Pick one owner for each name before you change records.
Choose an authority model. One cloud or one self-hosted server can host the parent zone and other clouds forward to it. Or each cloud can host a child zone such as aws.corp.example or azure.corp.example, with NS records in the parent. A single authority is easier to audit. Delegation limits the blast radius of a provider outage but gives you more places to check.
Plan resolver endpoints and IP ranges. AWS Route 53 Resolver uses inbound and outbound endpoints in your VPCs. Azure DNS Private Resolver uses inbound and outbound endpoints in a subnet. Google Cloud DNS uses private zones attached to VPC networks plus forwarding zones. Reserve non-overlapping address space for these endpoints, and do not put them in a subnet that autoscales.
Decide how records get created. Manual console changes do not scale across clouds. Use Terraform, Pulumi or your GitOps pipeline, and keep zone definitions in one repository. Set a short default TTL during migration, then raise it once names are stable. Turn on query logging in every cloud before cutover.
How to do it
How to run private DNS across several clouds starts with a namespace you control. Pick a private parent domain such as corp.example, not a name you also use on the public internet. Create the parent as a private zone in one place. If you split authority, create child zones under it, for example aws.corp.example, gcp.corp.example and azure.corp.example. Write the naming rule down: service.region.cloud.corp.example or service.env.corp.example.
Wire resolution in one direction first. Pick a hub network in each cloud and connect those hubs with your private interconnect or VPN. Attach the private zones to the hubs. On AWS, associate the Route 53 private hosted zone with the hub VPC. On Azure, link the private DNS zone to the hub virtual network. On Google Cloud, attach the private zone to the hub VPC. Then add forwarding rules for the parent zone.
Avoid forwarding loops. A resolver should forward only zones it does not own. If every cloud forwards corp.example to every other cloud, queries bounce or time out. Use conditional forwarders with explicit zone names and explicit target IPs. On AWS, use an outbound endpoint rule for the remote zone and an inbound endpoint for local queries. On Azure, use a forwarding ruleset. On Google Cloud, use a forwarding zone. Publish records through code so the zone that owns a name is the only place it changes.
- Define the private namespace. Pick one private parent domain and write the pattern for services, regions and clouds. Record who approves new child zones. Put the rule in your repository README.
- Choose the authoritative home. Decide whether one cloud hosts the parent or each cloud hosts a child zone. A single authority is easier to reason about for a first cut. Move to delegation when you need to survive a provider outage.
- Reserve resolver addresses. Allocate a small subnet in each cloud for resolver endpoints. Check that the range does not overlap with any VPC, VNet or on-premises network. Document the IPs in your address management system.
- Create and link private zones. Create the private zone in the authoritative account and link it to the hub network in each cloud. Add VPC associations or virtual network links for every network that must resolve the name. Do not rely on the default resolver alone.
- Add conditional forwarding. Create forwarder rules for zones hosted in another cloud, with that zone's name server IPs as targets. Forward specific child zones, not the whole parent domain. Test one zone at a time.
- Automate record changes. Put zone and record definitions in Terraform or another declarative tool. Run changes in a staging account first. Require peer review for NS, CNAME and MX changes.
- Turn on query logging. Enable DNS query logs in each cloud and send them to one log store. Log the source IP, query name, record type and response code. Use the logs to find clients that still send private names to public DNS.
- Test from every network. Run lookups from a host in each VPC, VNet and on-premises segment. Compare the answer with the record you expect. Test a name that should fail and confirm it does.
How to check it worked
Start with the resolver, not the app. From a test host in each cloud, run dig app.corp.example and nslookup app.corp.example. Then query the authoritative server directly, for example dig app.corp.example @10.20.0.10. The answers should match. If they differ, you have a cache, a forwarder or a split-horizon problem.
Check remote zones in both directions. From AWS, resolve a name hosted in Azure. From Azure, resolve a name hosted in Google Cloud. From Google Cloud, resolve a name hosted in AWS. Use dig with the resolver IP and read the authority section. Do not use dig +trace inside a private resolver.
Test negative answers. Ask for a name that does not exist and confirm you get NXDOMAIN, not a public address. Ask for a record type that should not exist, such as AAAA for an IPv4-only service. Check that response time is stable. A jump in latency often means a forwarder is timing out and falling back.
Review the logs after the test. You should see queries from each cloud, the expected response codes, and no public DNS traffic for private names. If a cloud shows no queries, its resolver is not using your forwarder. If it shows public answers, a private zone is not linked to that network.
Mistakes to avoid
The first mistake is creating the same zone in two places and expecting the records to merge. They do not. The resolver picks the most specific match and follows that cloud's association rules. If two private zones share a name, one wins. Use delegation or one authority instead.
The second mistake is forwarding the whole parent domain to a remote resolver. That sends every lookup over the WAN and adds a single point of failure. Forward only the child zones that live in the other cloud. Keep a local cache for names each cloud uses most.
The third mistake is forgetting the link between zone and network. A private zone that is not associated with a VPC or VNet will not answer. The same applies to resolver endpoints in the wrong subnet. Check associations before you debug application code.
The fourth mistake is leaving TTLs high during a migration. A long TTL on a record that points to the wrong load balancer costs you a day. Lower TTLs before the change, test, then raise them after the old record is gone. For a design review, you can keep it in-house or hire a network consultant. Another option is the Cloudverg Expert architecture session at $450 one-time.
Frequently asked questions
Can I use one private DNS zone across AWS, Azure and Google Cloud?
Yes, if one cloud hosts the zone and the others forward queries to its authoritative name servers. You still need resolver endpoints and network links in each cloud. A single zone is easier to audit, but it adds a dependency on the hosting cloud.
Should I use cloud-native private DNS or run my own BIND servers?
Cloud-native private DNS removes server patching and ties into each cloud's network links. Self-hosted BIND, CoreDNS or Windows DNS gives you one zone file and one place to edit, but you own uptime and upgrades. Pick based on who will operate it.
How do I stop private names from leaking to public DNS?
Keep private zones linked to private networks only, and use conditional forwarders for every private suffix. Block outbound DNS from workloads except through your resolver. Test from each subnet with a name that does not exist and confirm you get NXDOMAIN.