Why Active Directory needs DNS

Active Directory (AD) is a set of services running on domain controllers (DCs): LDAP (Lightweight Directory Access Protocol) for directory queries, Kerberos for authentication, SMB (Server Message Block) shares for Group Policy files, and a global catalog for forest-wide searches. The structure behind them is explained in Active Directory Fundamentals. A client that wants to sign in does not have a list of DC addresses configured anywhere. It only knows the domain name, for example ad.example.com, and asks the DNS (Domain Name System) where the services for that domain are.

That design makes AD flexible: you can add, move or retire DCs without touching a single client, because each DC publishes itself in DNS. The price is that DNS becomes the first dependency of everything in the domain. When DNS answers wrongly or not at all, the symptoms look like anything but DNS:

  • "The trust relationship between this workstation and the primary domain failed", or a domain join that says the domain "could not be contacted";
  • slow sign-in, followed by a desktop without mapped drives or policies;
  • Group Policy errors in the event log, and gpupdate that cannot reach a DC;
  • Kerberos failures that silently fall back to the weaker NTLM protocol (see Kerberos and NTLM).

General DNS (the hierarchy, record types, caching and the tools) is covered on the DNS page; this page is about what Active Directory adds on top.

SRV records: how services are published

An SRV record (service record, RFC 2782) is a DNS record that tells a client which host offers a given service over a given protocol for a domain, on which port, and in which order of preference. Its name has a fixed shape, _service._protocol.name, and its data holds four fields: priority, weight, port and target host. An A record answers "what is the address of this host?"; an SRV record answers "which hosts run this service?".

Every DC registers a set of SRV records for itself. The ones a technician meets most often:

Record name Port Finds
_ldap._tcp.dc._msdcs.ad.example.com 389 Every DC of the domain (the record the DC locator starts with)
_ldap._tcp.Utrecht._sites.dc._msdcs.ad.example.com 389 The DCs that serve the AD site Utrecht
_kerberos._tcp.dc._msdcs.ad.example.com 88 DCs running the Kerberos key distribution center
_ldap._tcp.pdc._msdcs.ad.example.com 389 The DC holding the PDC emulator role (one per domain)
_gc._tcp.ad.example.com 3268 Global catalog servers of the forest
_kerberos._tcp.ad.example.com, _kpasswd._tcp.ad.example.com 88, 464 Kerberos and Kerberos password change, in the generic form that non-Windows clients (Linux, macOS) also use

The _msdcs part is commonly expanded as "Microsoft Domain Controller Services": records under it are specific to Active Directory, while the shorter names follow the generic SRV conventions any Kerberos or LDAP client understands. Querying the records yourself is the fastest way to see what a client sees:

Resolve-DnsName -Type SRV _ldap._tcp.dc._msdcs.ad.example.com
Name                                  Type TTL  Section NameTarget          Priority Weight Port
----                                  ---- ---  ------- ----------          -------- ------ ----
_ldap._tcp.dc._msdcs.ad.example.com   SRV  600  Answer  dc01.ad.example.com 0        100    389
_ldap._tcp.dc._msdcs.ad.example.com   SRV  600  Answer  dc02.ad.example.com 0        100    389
_ldap._tcp.dc._msdcs.ad.example.com   SRV  600  Answer  dc03.ad.example.com 0        100    389

Name       : dc01.ad.example.com
QueryType  : A
TTL        : 1200
Section    : Additional
IP4Address : 192.0.2.10

The same query works with nslookup -type=SRV on any Windows machine, or dig SRV on Linux; see the DNS tools. An empty answer (or "Non-existent domain") means the client asks the wrong DNS server or the DC failed to register; the troubleshooting section shows how to tell which.

Who registers the records

The Netlogon service on each DC registers its SRV records, and re-registers them periodically and at every service start. The full list a DC wants to register is written to the text file %SystemRoot%\System32\config\netlogon.dns, which is handy for comparing "what should exist" with "what DNS returns". By default each DC registers priority 0 and weight 100, so all DCs are equal; administrators can lower a DC's weight in the Netlogon settings, for example to send fewer clients to a busy PDC emulator. Restarting Netlogon on a DC (Restart-Service Netlogon) is the usual way to force a re-registration after fixing a DNS problem.

Priority and weight in Python

RFC 2782 defines how a client uses priority and weight: always try the lowest priority value first; among records with the same priority, pick randomly in proportion to the weight. The Windows DC locator adds site awareness on top (next section), but the rule is the same one used for SIP, XMPP and other SRV-based services. This sketch orders four DCs for three clients and then counts which DC 10,000 clients would try first. The random generator is seeded, so the output is identical on every run.

# Choose a server from DNS SRV records the way RFC 2782 describes.
import random

# (priority, weight, port, target) for _ldap._tcp.dc._msdcs.ad.example.com
RECORDS = [
    (0, 60, 389, "dc01.ad.example.com"),
    (0, 30, 389, "dc02.ad.example.com"),
    (0, 10, 389, "dc03.ad.example.com"),
    (10, 100, 389, "dc-dr.ad.example.com"),  # disaster-recovery site
]


def srv_order(records, rng):
    """Return the targets in the order a client should try them."""
    ordered = []
    for priority in sorted({r[0] for r in records}):  # lowest priority first
        group = [r for r in records if r[0] == priority]
        group.sort(key=lambda r: r[1] != 0)  # weight-0 records go first
        while group:
            total = sum(r[1] for r in group)
            pick = rng.randint(0, total)  # 0..total, both ends included
            running = 0
            for record in group:
                running += record[1]
                if running >= pick:
                    ordered.append(record[3])
                    group.remove(record)
                    break
    return ordered


rng = random.Random(2782)  # seeded, so the output is the same every run
for attempt in range(1, 4):
    print(f"Client {attempt}:", " -> ".join(srv_order(RECORDS, rng)))

first_choice = {}
for _ in range(10_000):
    first = srv_order(RECORDS, rng)[0]
    first_choice[first] = first_choice.get(first, 0) + 1
print("First choice over 10,000 clients:")
for target, count in sorted(first_choice.items()):
    print(f"  {target:<22}{count:>6}")
Client 1: dc01.ad.example.com -> dc03.ad.example.com -> dc02.ad.example.com -> dc-dr.ad.example.com
Client 2: dc02.ad.example.com -> dc01.ad.example.com -> dc03.ad.example.com -> dc-dr.ad.example.com
Client 3: dc01.ad.example.com -> dc02.ad.example.com -> dc03.ad.example.com -> dc-dr.ad.example.com
First choice over 10,000 clients:
  dc01.ad.example.com     6017
  dc02.ad.example.com     2973
  dc03.ad.example.com     1010

Two things to notice. The disaster-recovery DC with priority 10 is never a first choice: it is only used when every priority-0 DC fails to answer. And weights 60, 30 and 10 give roughly 60%, 30% and 10% of the clients, which spreads load without any central balancer.

The DC locator: finding the right DC

The DC locator is the Windows process (implemented by the Netlogon service) that finds a suitable domain controller for a client by querying DNS SRV records and then checking which DC answers and which AD site the client belongs to. It runs at startup, at sign-in, when a cached DC stops responding, and whenever an application asks for a DC.

DC locator sequence. 1: the client sends an SRV query for _ldap._tcp.dc._msdcs to the AD DNS server. 2: DNS returns a list of domain controllers. 3: the client sends an LDAP ping over UDP port 389 to the DCs. 4: the first DC to reply tells the client which AD site it belongs to. 5: if that DC is in another site, the client sends a site-specific SRV query. 6: the client uses a DC in its own site and caches it. Client AD DNS server Domain controllers 1 SRV query: _ldap._tcp.dc._msdcs 2 List: dc01, dc02, dc03 3 LDAP ping (UDP 389) to the DCs 4 First reply: "your site is Utrecht" 5 Site query (if needed) 6 Use a DC in site Utrecht, cache it
The DC locator asks DNS for candidates, then lets the DCs themselves tell the client which site it is in, so the client ends up at a nearby DC instead of one across a slow WAN link.

Step by step:

  1. The client asks its DNS server for the SRV records of the domain. If it already knows its site (it remembers the last one), it asks the site-specific name _ldap._tcp.Utrecht._sites.dc._msdcs.ad.example.com first; otherwise the generic one.
  2. DNS returns the DCs, ordered by priority and weight.
  3. The client sends each candidate a small "LDAP ping": a connectionless LDAP query over UDP port 389.
  4. The first DC to answer replies with its own name and capabilities (global catalog, writable, time server, and so on) and with the site that matches the client's IP address. That mapping comes from the subnets defined in AD Sites and Services.
  5. If the answering DC is not in the client's site, the client repeats the query with the site-specific name.
  6. The client uses the DC that answers and caches it until it stops responding, the client restarts, or the periodic rediscovery (by default every 12 hours) runs.

Step 4 is why missing subnets in AD Sites and Services are a classic cause of slow sign-ins at branch offices: a client whose subnet is not mapped to any site may pick any DC in the domain, including one on the other side of the world.

AD-integrated zones and secure dynamic updates

A DNS zone can be stored in a text file on one primary server, as the zone file example on the DNS page shows. On a DC running the DNS Server role there is a better option. An AD-integrated zone is a DNS zone whose records are stored as objects in the Active Directory database instead of in a zone file, so they replicate with normal AD replication and every DC that runs DNS can accept changes to the zone.

That gives three advantages over a classic primary/secondary setup:

  • Multi-master. There is no single primary server whose failure stops all updates; any DNS server on a DC can accept changes.
  • One replication system. Zone data travels with AD replication, using its schedule and compression, instead of separate zone transfers. The scope is configurable: all DNS servers in the domain (the DomainDnsZones partition) or in the whole forest (ForestDnsZones).
  • Secure dynamic updates. The zone can accept updates from authenticated domain members only.

Secure dynamic updates

Domain members register their own A and AAAA records when they start or get a new address; DCs register their SRV records the same way. With the zone set to Secure only, a client must authenticate with Kerberos before it may create a record, and the record gets an access control list that makes the creating computer its owner. Another machine cannot overwrite fs01.ad.example.com to point users at itself. With "Nonsecure and secure", anyone who can reach the DNS server can register or change names, which is an open invitation for traffic redirection. Keep AD-integrated zones on Secure only.

Dynamic registration also leaves old records behind when laptops change networks. Aging and scavenging removes records that have not been refreshed for a set time. It is off by default; switch it on deliberately, with intervals longer than your DHCP lease time (see DHCP), so records of machines that are merely on holiday are not removed.

The _msdcs zone

In a new forest, the AD-specific records of the forest root live in a separate zone, _msdcs.ad.example.com, replicated to every DNS server in the forest. It also holds a record per DC named after the DC's GUID, which other DCs use to find their replication partners. A broken _msdcs zone therefore does not only break client sign-in; it can break replication between DCs too.

Choosing the domain name

The DNS name of an AD domain is chosen once, when the first DC is promoted, and renaming a domain later is a complex, risky project that many products do not support. So the name deserves a moment of thought. The current advice: use a subdomain of a registered domain you own, such as ad.example.com when the organization owns example.com.

Choice Verdict Why
ad.example.com Recommended You own it, nobody else can register it, it does not overlap with public records, and public certificate authorities can issue certificates for names under it.
example.com (the public domain itself) Works, with extra work Internal DNS must also serve copies of public records such as www, which is split-brain DNS (below). Every public change has to be made twice.
kestrel.local Avoid .local is reserved for multicast DNS (RFC 6762), which macOS, iOS and many Linux systems use for discovery, so those clients may not send the query to your DNS server at all. Public certificate authorities also do not issue certificates for names that are not publicly registered.
kestrel (single label) Avoid A single-label domain name has no dot, so DNS clients treat it differently (they add suffixes, or refuse to register records in it), and many applications and Microsoft cloud services do not support it.
A made-up top-level domain (.lan, .intra) Avoid You do not control it; a name that is not public today may become a real top-level domain, after which internal names collide with the internet.
.internal Possible, not recommended Reserved by ICANN (2024) for private use, so it will never collide with a public top-level domain, but public certificate authorities still cannot issue certificates for it; a subdomain you own remains the better choice.

Besides the DNS name, every domain also has a short NetBIOS name, the part users see in KESTREL\amara-style sign-in names. The promotion wizard proposes the leftmost DNS label (AD for ad.example.com); you can pick a more meaningful one. User principal names, the user@domain sign-in format, can use a different suffix such as @example.com so that sign-in names match email addresses; see Users, Groups and Computers.

DNS settings on clients and DCs

Every domain member, server or workstation, must use DNS servers that can answer for the AD zones, which in practice means the DCs running DNS (or internal resolvers that forward the AD zones to them). DHCP hands those addresses out to clients; servers with static addresses get them configured by hand. Internet names are then resolved by the AD DNS servers on the client's behalf.

DCs that run DNS also need care. A common Microsoft recommendation for a DC with several DCs in the domain: another DC as the preferred DNS server, and the DC's own loopback address as the alternate. Pointing a DC only at itself can leave it unable to find its partners after a restart, while its own DNS service is still loading the zones.

Forwarders and conditional forwarders

For names outside its own zones, an AD DNS server either walks the DNS hierarchy itself (using root hints) or passes the query to a forwarder: a resolver of the internet provider, a filtering DNS service or the organization's own recursive resolver (see how a name is resolved). A conditional forwarder applies only to one domain: "send everything for partner.example.net to 198.51.100.53". Conditional forwarders are the usual way to make two forests resolve each other's names before a trust is created, and they can be stored in AD so every DC gets them.

Split-brain DNS

When the AD domain has the same name as the public domain, internal DNS answers for example.com and the public DNS answers for it too, with different content: split-brain (or split-horizon) DNS, explained on the DNS page. It works, but every public record that staff need (the website, the mail server, the VPN gateway) must be copied into the internal zone by hand, and forgetting one gives "the website works at home but not in the office" tickets. A subdomain such as ad.example.com avoids the problem entirely.

Troubleshooting DNS for AD

Work from the client toward the DCs. First: which DNS servers does the client use, and do they return the SRV records?

ipconfig /all                       # which DNS servers, which DNS suffix?
nltest /dsgetsite                   # which AD site does this machine think it is in?
nltest /dsgetdc:ad.example.com      # run the DC locator and show the result
           DC: \\DC01.ad.example.com
      Address: \\192.0.2.10
     Dom Guid: 3f2c9a1e-7b4d-4c1a-9e55-2d8b6a1f0c42
     Dom Name: ad.example.com
  Forest Name: ad.example.com
 Dc Site Name: Utrecht
Our Site Name: Utrecht
        Flags: PDC GC DS LDAP KDC TIMESERV WRITABLE DNS_DC DNS_DOMAIN DNS_FOREST CLOSE_SITE
The command completed successfully

"Dc Site Name" equal to "Our Site Name" and the flag CLOSE_SITE mean the locator found a DC in the client's own site. An error such as ERROR_NO_SUCH_DOMAIN sends you back to DNS: query the SRV record with Resolve-DnsName against the DNS server from ipconfig /all. Adding /force to the nltest command makes it skip its cache and run the locator again.

On a DC, dcdiag tests DNS for the whole domain: delegations, forwarders, dynamic update and whether every DC's records are registered.

dcdiag /test:dns /v
   Summary of DNS test results:

                                Auth Basc Forw Del  Dyn  RReg Ext
      _________________________________________________________________
      Domain: ad.example.com
         DC01                   PASS PASS PASS PASS PASS PASS n/a
         DC02                   PASS PASS FAIL PASS PASS PASS n/a

   ......................... ad.example.com failed test DNS

Here DC02 fails the forwarders test (Forw): it cannot reach its configured forwarder, so clients that use DC02 cannot resolve internet names, while sign-in still works. The verbose output above the summary names the forwarder that failed.

Symptom Likely cause Check or fix
Domain join: "domain could not be contacted" Client uses a public or router DNS server ipconfig /all; set the AD DNS servers
Sign-in works, but slowly at one branch Branch subnet missing in AD Sites and Services nltest /dsgetsite; add the subnet to the right site
New DC is never used Its SRV records are not registered Compare netlogon.dns with DNS; restart Netlogon
Intermittent failures that "fix themselves" A public resolver among the client's DNS servers Remove it; ipconfig /flushdns
A server name resolves to an old address Stale record, or a cached old answer ipconfig /registerdns on the server; check scavenging

Broader sign-in troubleshooting, including the event logs to read, is on Monitoring and Troubleshooting Identity; general DNS problems are on the DNS page.