🔤 DNS and Domain Names Across the Clouds
How a name becomes an address, who answers for your domain, what each cloud's DNS service does (Route 53, Azure DNS, Cloud DNS, Oracle DNS), private names inside a cloud network, and a zone-file checker that catches the classic mistakes (run for real, like the live lookups)
💡 How this page was made. The live lookups were run for real on 6 October 2026, from this machine, with the dig tool (the site's dig page explains it in depth). They are read-only questions to public DNS, and they show real cloud names. The zone-file checker is a small teaching tool I wrote and ran; its sample zones are made up, and it checks only a handful of well-known mistakes, so it is no substitute for the provider's own validation. What each cloud's DNS service offers is from the providers' documentation, read on 6 October 2026 (Route 53, Azure DNS, Cloud DNS and Oracle's DNS service); nothing was run on a cloud account. One more real finding is in the debugging section: on this network, queries aimed at root servers were answered by something that is not a root server, so I could not show a true "trace" of a lookup, and the page says how to recognize that. Related pages: networking (the DNS addresses reserved in each subnet), IAM, cost control and Terraform.
What DNS does
DNS (the Domain Name System) turns a name into the things a program needs: an address, a mail server, a text note. Everything on the internet that you reach by name goes through it, which is why a DNS mistake looks like "the whole service is down". The pieces:
- A record: name to an IPv4 address. AAAA: name to IPv6.
- CNAME: "this name is an alias for that other name".
- MX: where mail for the domain goes. TXT: free text (used for ownership proofs and mail policies).
- NS: which servers are authoritative for the zone (who has the real answers).
- SOA: the zone's "header": the primary server, a contact, a serial number and timers.
- TTL (time to live): how many seconds an answer may be remembered by others.
Real answers from a public domain (as run):
user@lab:~$ dig +noall +answer example.com A example.com. 252 IN A 104.20.23.154 example.com. 252 IN A 172.66.147.243 user@lab:~$ dig +short NS example.com hera.ns.cloudflare.com. elliott.ns.cloudflare.com. user@lab:~$ dig +noall +answer example.com SOA example.com. 1800 IN SOA elliott.ns.cloudflare.com. dns.cloudflare.com. 2416374680 10000 2400 604800 1800
- example.com has two A records (two addresses), each with a TTL of 252 seconds left in the resolver's cache.
- Its NS records name the servers that answer for it: here two names at Cloudflare. The domain name and the DNS hosting are two different things: the owner registered the name somewhere, and hosts the records with a DNS provider.
- The SOA record reads, field by field: the primary name server, the contact address (dns.cloudflare.com. stands for dns@cloudflare.com: the first dot replaces the @), the serial 2416374680 (it rises with every change, so secondary servers know there is something new), refresh 10000, retry 2400, expire 604800 and the final 1800, the "negative caching" time (how long an "it does not exist" answer is remembered).
Two roles: the resolver and the authoritative server
- A resolver (the one your machine is configured to use, from your router, your company or your cloud network) looks things up for you and caches the answers.
- An authoritative server owns the answers for a zone. The world finds it through delegation: the .com servers know which servers are in charge of example.com, and say so.
You can ask the resolver who is in charge of a name, and the NS answer is that delegation:
user@lab:~$ dig +short NS com | head -3 a.gtld-servers.net. b.gtld-servers.net. c.gtld-servers.net. user@lab:~$ dig +short NS amazon.com ns-1447.awsdns-52.org. ns-1707.awsdns-21.co.uk. ns-264.awsdns-33.com. ns-521.awsdns-01.net. user@lab:~$ dig +short NS microsoft.com ns1-39.azure-dns.com. ns2-39.azure-dns.net. ns3-39.azure-dns.org. ns4-39.azure-dns.info. user@lab:~$ dig +short NS google.com ns1.google.com. ns3.google.com. ns4.google.com. ns2.google.com. user@lab:~$ dig +short NS oracle.com a11-66.akam.net. ns1.p201.dns.oraclecloud.net. ns3.p201.dns.oraclecloud.net. a1-160.akam.net. ns2.p201.dns.oraclecloud.net. ns4.p201.dns.oraclecloud.net. a13-65.akam.net. a18-67.akam.net.
- .com is run by the gtld-servers.net machines (the first three are shown).
- Where a domain's DNS is hosted shows in its name servers. amazon.com uses awsdns names (Amazon's Route 53 naming), microsoft.com uses azure-dns names (Azure DNS), google.com uses ns1.google.com (Google's own servers), and oracle.com uses a mix of oraclecloud.net and Akamai names. These names are a quick way to see who hosts a domain, which matters when you are asking "which dashboard do I change this in?"
TTL: the delay you cannot avoid
An answer is cached for its TTL. Asking twice, five seconds apart (as run):
user@lab:~$ dig +noall +answer example.com A; sleep 5; dig +noall +answer example.com A example.com. 231 IN A 104.20.23.154 example.com. 231 IN A 172.66.147.243 example.com. 226 IN A 104.20.23.154 example.com. 226 IN A 172.66.147.243
The TTL counted down from 231 to 226: the second answer came from the cache, with the remaining life. This is the single most important fact for changing DNS: after you change a record, anybody who cached the old one keeps using it until its TTL runs out. So:
- Lower the TTL a day or two before a planned change (to a few minutes), make the change, then raise it again.
- A very low TTL means more queries and slower answers for the world and more cost for you; a very high one (a day or more) means an emergency change takes that long to spread. A few minutes to an hour is typical.
- "It works for me but not for them" very often means different caches, different ages.
Names that the cloud gives you
Cloud services have names too, and DNS is how the cloud steers you. Real lookups of the public endpoints (as run):
user@lab:~$ dig +noall +answer management.azure.com management.azure.com. 3531 IN CNAME management.privatelink.azure.com. management.privatelink.azure.com. 3531 IN CNAME arm-frontdoor-edge-geo.trafficmanager.net. arm-frontdoor-edge-geo.trafficmanager.net. 53 IN CNAME edge-management-azure-com.arm-0001.arm-msedge.net. edge-management-azure-com.arm-0001.arm-msedge.net. 53 IN CNAME arm-0001.arm-msedge.net. arm-0001.arm-msedge.net. 53 IN A 4.150.240.10 user@lab:~$ dig +noall +answer storage.googleapis.com storage.googleapis.com. 170 IN A 142.250.98.207 storage.googleapis.com. 170 IN A 142.251.107.207 storage.googleapis.com. 170 IN A 74.125.196.207 storage.googleapis.com. 170 IN A 192.178.219.207 storage.googleapis.com. 170 IN A 173.194.215.207 user@lab:~$ dig +noall +answer s3.eu-west-1.amazonaws.com s3.eu-west-1.amazonaws.com. 1 IN A 3.5.74.107 s3.eu-west-1.amazonaws.com. 1 IN A 52.92.20.16 s3.eu-west-1.amazonaws.com. 1 IN A 3.5.66.33 s3.eu-west-1.amazonaws.com. 1 IN A 3.5.75.77 user@lab:~$ dig +noall +answer objectstorage.us-ashburn-1.oraclecloud.com objectstorage.us-ashburn-1.oraclecloud.com. 52 IN CNAME objectstorage.us-ashburn-1.oci.oraclecloud.com. objectstorage.us-ashburn-1.oci.oraclecloud.com. 892 IN A 134.70.28.1 objectstorage.us-ashburn-1.oci.oraclecloud.com. 892 IN A 134.70.32.1 objectstorage.us-ashburn-1.oci.oraclecloud.com. 892 IN A 134.70.24.1
- Azure's management endpoint is a chain of CNAMEs through trafficmanager.net (Azure's DNS-based load balancer) and then to one address. The answer can differ by where you are, and the TTLs are short (53 seconds on some links).
- Google's storage endpoint returns several addresses at once (a handful are shown).
- S3 in an AWS region returns a set of addresses with a TTL of 1 second.
- Oracle's object storage endpoint is a CNAME to a regional name, then several addresses.
- Lesson: never hard-code the addresses of a cloud service. They change, differ by place and rotate. Use the name. If a firewall rule needs addresses, use the provider's published address lists or private endpoints, not one lookup you did today.
- Servers get names too: from AWS's documentation, a public name looks like ec2-203-0-113-25.compute-1.amazonaws.com and a private one like ip-10-251-50-12.ec2.internal. Do not build anything on those names: put your own name (a CNAME or an alias) in front.
What each cloud's DNS service does
All four host your zone on their global name servers, so you can manage records with the same account, tools and billing as the rest of your cloud. From the documentation:
- AWS: Amazon Route 53. Three functions "in any combination": domain registration, DNS routing, and health checking (automated requests to your resource, to alert or to steer traffic away from unhealthy ones). Extras: VPC Resolver (recursive DNS for your VPCs, with conditional forwarding rules and endpoints to reach your on-premises DNS), DNS Firewall (filter outbound queries by domain lists), Traffic Flow (route users by geoproximity, latency, health and more), and Profiles (apply DNS settings across many VPCs and accounts). Private names (hosted zones attached to VPCs) are part of it. Check the current documentation for alias records, the AWS way to point a bare domain at an AWS resource.
- Azure: Azure DNS. Public DNS (hosting for internet domains), Private DNS (names inside your virtual networks, with no custom DNS server needed, and autoregistration of VMs), DNS Private Resolver (so on-premises systems can query Azure private zones and the other way round, without running DNS servers on VMs), Traffic Manager (DNS-based load balancing across regions) and DNS Resolver Policy (filter and log queries, with a threat-intelligence feed of known malicious domains). It does hosting and resolution: check where to register the domain itself.
- Google Cloud: Cloud DNS. A global, authoritative service that publishes your domain, using anycast (the same name servers answer from many places). Zone types: public, private (visible only to the VPC networks you list), forwarding (outbound queries to another name server, for example on-premises) and peering (resolve through another network). It supports DNSSEC, response policies, DNS64, and IAM permissions per project and per zone. Domain registration is separate (Cloud Domains).
- Oracle Cloud: DNS. Public zones (which need delegation at your registrar), private zones (for hostname resolution inside and between VCNs), secondary zones (a copy of a primary elsewhere, for redundancy) and reverse zones. A zone is defined by its SOA record. Documented limits: 1,000 zones, 25,000 records per zone and 1 MB for an uploaded zone file, with public zones available in the commercial realm only. (I could not read about TTL options, steering policies or apex alias records in the page I used.)
Delegation: connecting the registrar to the host
Having a domain at a registrar and its DNS at a cloud is normal. The steps, the same on every cloud:
- Create the zone (example.com) in the cloud's DNS service. It gives you name servers.
- At the registrar, change the domain's NS records to those name servers (this is "delegation"). Oracle's documentation says exactly this about registrars.
- Create the records in the cloud (you can do it first, to prepare).
- Wait for the old NS TTL to expire, then check with dig +short NS yourdomain. Until the delegation is switched, the cloud's zone receives no queries.
Private names inside the cloud
Inside your network, machines need to find each other by name, and you do not want those names on the public internet. Each cloud has a built-in resolver reachable from every machine at a reserved address (the networking page lists it: on AWS the DNS server is at the VPC base plus two, on Azure .2 and .3 of each subnet are reserved for it, and Azure's platform also uses the virtual address 168.63.129.16), plus private zones for your own names:
- A private zone answers only inside the networks it is linked to (Azure virtual networks, Google VPC networks, AWS VPCs, Oracle VCNs). The same name can then mean one address inside, another outside ("split-horizon"): db.example.com resolves to a private address in the VPC and to nothing (or something else) on the internet.
- Hybrid setups (on-premises plus cloud) need forwarding: AWS's VPC Resolver rules and endpoints, Azure's Private Resolver, Cloud DNS's forwarding zones. The usual symptom of a missing one is "I can ping the IP but not the name" from one side.
- If DNS works for the internet but not for your private names, check: is the private zone linked to this network? Is the machine using the cloud's resolver, or a custom one set by DHCP options or a VM image?
A zone-file checker, run for real
Hosting providers let you upload or write zone files, and typos in them cause outages. A DNS zone in the classic text format (a zone file) has rules that are easy to break. This is a 48-line checker I wrote for the commonest mistakes:
#!/usr/bin/env python3
"""A tiny zone-file sanity checker (teaching tool). Usage: zonecheck.py ORIGIN FILE"""
import sys, ipaddress
RFC1918 = [ipaddress.ip_network(n) for n in ('10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16')]
origin = sys.argv[1].rstrip('.') + '.'
records = [] # (name, ttl, type, value)
ttl_default = 3600
for line in open(sys.argv[2]):
line = line.split(';')[0].strip()
if not line: continue
if line.startswith('$TTL'): ttl_default = int(line.split()[1]); continue
if line.startswith('$ORIGIN'): continue
parts = line.split()
name = parts[0]; rest = parts[1:]
ttl = ttl_default
if rest[0].isdigit(): ttl = int(rest[0]); rest = rest[1:]
if rest[0] == 'IN': rest = rest[1:]
records.append((name, ttl, rest[0], ' '.join(rest[1:])))
def fqdn(n): return origin if n == '@' else (n if n.endswith('.') else n + '.' + origin)
findings = []
by_name = {}
for name, ttl, typ, val in records:
by_name.setdefault(fqdn(name), []).append(typ)
for n, types in by_name.items():
if 'CNAME' in types and len(types) > 1:
findings.append(('ERROR', n, 'has a CNAME and also other record types (%s): not allowed' % ', '.join(sorted(set(types) - {'CNAME'}))))
if n == origin and 'CNAME' in types:
findings.append(('ERROR', n, 'CNAME at the zone apex'))
for name, ttl, typ, val in records:
n = fqdn(name)
if typ in ('CNAME', 'NS', 'MX', 'PTR'):
target = val.split()[-1]
if not target.endswith('.') and target.endswith(origin.rstrip('.')):
findings.append(('ERROR', n, '%s target "%s" lacks the final dot: it becomes %s.%s' % (typ, target, target, origin)))
if typ == 'A':
try:
if any(ipaddress.ip_address(val) in net for net in RFC1918): findings.append(('WARN', n, 'public zone points at a private (RFC 1918) address %s' % val))
except ValueError: findings.append(('ERROR', n, 'bad IPv4 address "%s"' % val))
if typ == 'MX' and 'CNAME' in by_name.get(fqdn(val.split()[-1]), []):
findings.append(('ERROR', n, 'MX points at a CNAME'))
if ttl < 60: findings.append(('WARN', n, 'TTL %d is very low (more queries, slower answers)' % ttl))
if ttl > 86400: findings.append(('WARN', n, 'TTL %d is over a day (changes will take that long to spread)' % ttl))
if not any(t == 'SOA' for ts in by_name.values() for t in ts): findings.append(('ERROR', origin, 'no SOA record'))
if not any(t == 'NS' for ts in by_name.values() for t in ts): findings.append(('ERROR', origin, 'no NS records'))
for sev, n, msg in sorted(findings): print('%-5s %-24s %s' % (sev, n, msg))
print('%d records, %d findings' % (len(records), len(findings)))
sys.exit(1 if any(f[0] == 'ERROR' for f in findings) else 0)
And two sample zones (made up). A bad one, with the mistakes that happen in real life:
$TTL 3600 @ IN SOA ns1.example.com. admin.example.com. 2026100601 7200 900 1209600 300 @ IN NS ns1.example.com. @ IN NS ns2.example.com @ IN CNAME web.example.com. @ IN MX 10 mail www IN CNAME web.example.com web 300 IN A 10.0.1.20 api 5 IN A 203.0.113.45 mail IN CNAME web old 200000 IN A 203.0.113.46
and the corrected one:
$TTL 3600 @ IN SOA ns1.example.com. admin.example.com. 2026100601 7200 900 1209600 300 @ IN NS ns1.example.com. @ IN NS ns2.example.com. @ IN A 203.0.113.45 @ IN MX 10 mail.example.com. www IN CNAME example.com. mail 300 IN A 203.0.113.47 api 300 IN A 203.0.113.48
Both checked (as run):
user@lab:~$ python3 zonecheck.py example.com bad.zone ERROR example.com. CNAME at the zone apex ERROR example.com. MX points at a CNAME ERROR example.com. NS target "ns2.example.com" lacks the final dot: it becomes ns2.example.com.example.com. ERROR example.com. has a CNAME and also other record types (MX, NS, SOA): not allowed ERROR www.example.com. CNAME target "web.example.com" lacks the final dot: it becomes web.example.com.example.com. WARN api.example.com. TTL 5 is very low (more queries, slower answers) WARN old.example.com. TTL 200000 is over a day (changes will take that long to spread) WARN web.example.com. public zone points at a private (RFC 1918) address 10.0.1.20 10 records, 8 findings exit status: 1 user@lab:~$ python3 zonecheck.py example.com good.zone 8 records, 0 findings exit status: 0
What the checker found in the bad zone:
- "CNAME at the zone apex" and "has a CNAME and also other record types". A name that has a CNAME must have no other records, and the apex (example.com itself) always has SOA and NS records, so a CNAME there is invalid. (This is why the clouds provide alias-type records to point a bare domain at a service, and why www is the usual place for a CNAME.) The corrected zone gives the apex an A record and puts the CNAME on www.
- "target lacks the final dot": ns2.example.com and web.example.com without a trailing dot are read as relative names, so they become ns2.example.com.example.com., which does not exist. In a zone file, a name that ends in a dot is complete, and one without is relative to the zone. (Some cloud consoles add the dot for you, and some do not: check.)
- "MX points at a CNAME": mail servers must be A or AAAA names.
- "public zone points at a private (RFC 1918) address": 10.0.1.20 is not reachable from the internet. (The sample addresses in the corrected zone are in 203.0.113.0/24, a block reserved for documentation: not private, not routable either.)
- TTL warnings: 5 seconds is extremely low, 200,000 seconds is more than two days.
- The corrected zone has 0 findings and exits with status 0, while the bad one exits with status 1, so it could fail a build (the same way you would check a Terraform-managed zone before applying).
Real tools do far more (the clouds validate on upload, and tools such as named-checkzone check syntax thoroughly), but the rules above are the ones people trip over.
Debugging DNS
A method, in order. The dig page of the Sysadmin tab has the full toolbox:
- Does the name resolve at all? dig +short NAME (and dig +noall +answer NAME to see the TTL and any CNAME chain).
- Ask more than one resolver. dig +short NAME @1.1.1.1 and @8.8.8.8 against your own: different answers mean caches at different ages, or a resolver that is blocking or rewriting the name.
- Ask the authoritative server directly. Find it with dig +short NS DOMAIN, then dig NAME @THAT-SERVER. If the authoritative server is right and the resolver is wrong, the cause is cache, so wait for the TTL (or flush if you control it). If the authoritative server is wrong, fix the zone.
- Check delegation: does dig +short NS DOMAIN return the name servers you expect (the cloud's), or the old ones?
- Check the private side: from a machine inside the network, dig NAME with no server (the cloud resolver) and with the reserved resolver address.
- Look at the SOA serial at each authoritative server: if they differ, a secondary has not caught up.
When the network lies: DNS interception
On this machine I tried to query a root server directly, asking it not to recurse. A real root server answers with a referral (and has no recursion). What came back (as run):
user@lab:~$ dig +norecurse +noall +comments example.com @a.root-servers.net | grep flags ;; flags: qr ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
The flags include ra (recursion available) and the answer section was filled in with the final addresses: a root server would never do that. Something on this network intercepts DNS traffic (a transparent proxy, a security product or a VPN) and answers on behalf of the servers I addressed. So a dig +trace, which is meant to walk the delegation from the root down, could not show the real path from here. This matters in practice: if dig @SERVER always gives the same answer whatever the server, or a root server claims to recurse, a middlebox is answering, and the cloud's DNS is not the only thing involved.
Common mistakes
- A CNAME at the apex, or a CNAME next to other records.
- A missing trailing dot (the example.com.example.com. mistake).
- A TTL that is too high for a change you will need to make, or forgetting that the old value lives on in caches.
- Changing records in the wrong place: the zone at the cloud is not in use until the registrar's NS records point at it.
- Two places hosting the same zone, with different records.
- Hard-coded IP addresses for cloud services.
- A private zone not linked to the network that needs it.
- Dangling records: a name that still points at a deleted server, address or bucket can be taken over by someone else who gets the same address or name. Delete DNS records with the resource.
- Forgetting that mail and proofs live in DNS too (MX, TXT): a careless zone replacement can break e-mail.
What this page did not cover
DNSSEC, DNS over HTTPS and TLS, content delivery networks, the details of e-mail records (SPF, DKIM, DMARC), geographic and weighted routing in depth, and registering domains (prices and transfers). Each cloud's DNS documentation, and the Sysadmin tab's dig and Postfix pages, go further.
The cheat sheet
- Records: A and AAAA (addresses), CNAME (alias; never at the apex or beside other records), MX (mail, to A names), NS (who is in charge), SOA (serial and timers), TXT. TTL = how long others may remember it.
- Roles: the registrar holds the NS delegation, a DNS host (Route 53, Azure DNS, Cloud DNS, Oracle DNS or other) holds the records, a resolver caches answers.
- Services: Route 53 (registration, routing, health checks, VPC Resolver, DNS Firewall) | Azure DNS (public, private with autoregistration, Private Resolver, Traffic Manager) | Cloud DNS (public, private, forwarding, peering, DNSSEC; Cloud Domains for registration) | Oracle DNS (public needs delegation, private, secondary, reverse; 1,000 zones, 25,000 records).
- Changing a record: lower the TTL first, wait that long, change, check, raise it again. Delegating: create the zone, set the registrar's NS to the cloud's name servers, create records, verify with dig +short NS.
- Zone-file rules: a trailing dot means complete, no dot means relative. A CNAME stands alone. MX targets are A names. No private addresses in a public zone.
- Debug: dig +short NAME, dig +noall +answer NAME (TTL and chain), dig NAME @RESOLVER for several resolvers, dig +short NS DOMAIN then dig NAME @AUTHORITATIVE, compare SOA serials. Suspect interception if every server gives the same answer.
- Cloud names: never hard-code addresses. Put your own CNAME or alias in front of cloud-generated names.
← Cloud Security Baseline: A Checklist You Can Audit · ↑ All tutorials · Serverless Functions on Every Cloud: Handlers, Events and Limits →