Windows Server for beginners. Part 12: How does time work in the domain?

All parts of the series “Windows Server for beginners”

In part 6 the Kerberos authenticator carried a timestamp, and the KDC rejected it if the clocks differed by more than five minutes. In part 10 a replication conflict with equal versions was decided by timestamp. The certificates that will appear in part 25 have a start of validity; the logs we read in every part are useless if two servers' clocks show different times. Time in a domain is not a convenience but a condition for it to work, and Windows has a separate service for it, with its own hierarchy, protocol and rules worth understanding once.


The hierarchy

The Windows time service is W32Time, the w32time service. It speaks NTP version 3 (RFC 1305) — by Microsoft's own account not an exact implementation of the standard — with the MS-SNTP extension for signing replies. Inside a domain, time sources are arranged in a tree:

                   External source: pool.ntp.org, GPS clock, provider's NTP
                            │
             PDC emulator of the forest root domain  (stratum 2–3)
                     ┌──────┴──────┐
     PDC emulators of child domains   other DCs of the root domain
             │                              │
     DCs of the child domain          domain members: servers and workstations
             │
     members of the child domain

The rule is simple: everyone takes time from the one above. A workstation or member server takes it from a controller of its domain; a controller from its domain's PDC emulator (or from a controller of the parent domain); a child domain's PDC from a reliable source in its own domain or from any controller of the parent domain; and the root domain's PDC from nobody: it's the top, and the administrator assigns its source. This mode is called NT5DS (domain hierarchy), and it's turned on automatically when a machine joins the domain:

PS C:\> w32tm /query /configuration | Select-String 'Type|NtpServer|AnnounceFlags'
AnnounceFlags: 10 (Local)
Type: NT5DS (Local)
NtpServer: time.windows.com,0x9 (Local)
NtpServer (Local)

The lines come in the order of the output's sections: AnnounceFlags from the general [Configuration] section, Type and NtpServer from the settings of the NtpClient provider in the [TimeProviders] section, and NtpServer (Local) is the header of the second provider, the server side of the service. The service shows its configuration, like w32tm /query /source below, only in an elevated window; without elevation both commands answer Access is denied. (0x80070005).

Type: NT5DS means take time through the domain hierarchy; the NtpServer value isn't used in this mode. AnnounceFlags only matters on controllers: 0x1 is "always a time server", 0x2 "a time server automatically", 0x4 "always a reliable time server", 0x8 "a reliable time server automatically". The default of 10 is 0x2 + 0x8: the service decides whether to advertise itself, and a controller whose clock isn't synchronized stops advertising. A PDC with a manual source gets 5 = 0x1 + 0x4: "always a time server" and "always reliable".

How does a domain member know whom to ask? Through the same DC Locator from part 3: a controller's reply carries the TIMESERV and GTIMESERV flags, and W32Time picks a controller with the first, preferring the second. A controller raises TIMESERV when it's synchronized itself, and GTIMESERV when it's declared reliable. That's why a controller with the default AnnounceFlags that has lost synchronization stops advertising itself as a time server (event 142, and as a reliable one, event 144), and clients move to another.


Why the reply is signed

Plain NTP is not protected at all: whoever answers first sets the client's time, and time is five minutes away from a Kerberos failure. In a domain W32Time uses MS-SNTP: the client sends a request with a key identifier whose lower 31 bits are the RID of its computer account and whose top bit says which password to use, the current or the previous one. The controller computes, through Netlogon, an MD5 over the hash of that account's password and the reply itself, and puts the result into the reply. The client checks the signature with its own password and discards an unsigned or wrongly signed reply. A reply can't be forged without knowing the machine's password, and the machine password is known only to the machine and the controllers.

Two consequences follow. A domain member in NT5DS mode won't take time from an NTP server that isn't a controller, even if it's listed in DHCP option 42: the service synchronizes only through the domain hierarchy. And a machine with a broken secure channel (the one with "trust relationship failed") also stops synchronizing time, without telling anyone.

Devices outside the domain — switches, Linux servers, printers — can take time from a controller over plain unsigned NTP: the controller answers on port 123 to everyone. Option 42 is for them, and it's worth listing the controllers in it so that time across the whole network comes from one source.


The PDC emulator

After the forest is installed, the top of the hierarchy is not configured at all. The root domain's PDC writes event 12 to the log: the machine is configured to take time through the domain hierarchy, but it's the PDC emulator of the forest root domain, and there's no one above it in the hierarchy. Until it's given an external source, the whole forest lives by the motherboard clock of dc1, which drifts by seconds a day. Inside the domain everything will be consistent, but not with the outside world — and the outside world means certificates, cloud services and the provider's logs.

We set it on the PDC — and only there:

PS C:\> w32tm /config /manualpeerlist:"0.pool.ntp.org,0x8 1.pool.ntp.org,0x8 2.pool.ntp.org,0x8" /syncfromflags:manual /reliable:yes /update
The command completed successfully.
PS C:\> Restart-Service w32time
PS C:\> w32tm /resync /rediscover
Sending resync command to local computer
The command completed successfully.
PS C:\> w32tm /query /status
Leap Indicator: 0(no warning)
Stratum: 3 (secondary reference - syncd by (S)NTP)
Precision: -23 (119.209ns per tick)
Root Delay: 0.0234375s
Root Dispersion: 0.0617432s
ReferenceId: 0xC000027B (source IP:  192.0.2.123)
Last Successful Sync Time: 15.10.2026 13:02:41
Source: 0.pool.ntp.org,0x8
Poll Interval: 10 (1024s)

The flags after the server name: 0x8 is client mode (request–reply, no symmetric associations), 0x1 means use the special poll interval SpecialPollInterval instead of the adaptive one, 0x2 marks a fallback source used only when the others are unavailable. /syncfromflags:manual means take time only from the list; /reliable:yes means declare itself a reliable source, that is, set AnnounceFlags to 5 and raise the GTIMESERV flag. In an organization with its own time source — a GPS receiver or the provider's NTP server — that's what goes in the list.

Stratum 3 in the output: pool.ntp.org returned a stratum 2 server, and we're one level below. The other controllers will be at level 4, domain members at 5. The lower, the more accumulated error, but for Kerberos's five minutes that's enough with a huge margin.

The problem with this setting is that it's tied to the machine, while the PDC role is tied to the domain. When the role moves to dc2 (part 17), dc2 becomes a top without a source, while dc1 keeps going to the internet and advertising itself as reliable, and the domain ends up with two sources of truth. The solution Microsoft's support team describes on the AskDS blog is a group policy linked to the Domain Controllers OU with a WMI filter on the PDC role (value 5 of the DomainRole property means Primary Domain Controller):

SELECT * FROM Win32_ComputerSystem WHERE DomainRole = 5

The policy sets an NTP client with a manual server list and AnnounceFlags = 5; it applies only to the machine that currently holds the role, and when the role moves, it follows on the next policy refresh. How to build such a policy is covered in part 24; for now the rule is on paper: moved the PDC — reconfigure time, and return the old PDC to the hierarchy:

PS C:\> w32tm /config /syncfromflags:domhier /reliable:no /update

Correction limits

W32Time doesn't jump the clock on any difference. Two parameters in HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config:

  • MaxPosPhaseCorrection and MaxNegPhaseCorrection are the largest forward and backward corrections the service agrees to apply. On controllers the default is 172,800 seconds (48 hours), on domain members there's no limit (0xFFFFFFFF), on standalone machines it's 54,000 seconds (15 hours). If the source says the clock has drifted further, the service refuses, logs event 34 and keeps living by its own clock. This is a safeguard: one controller with a runaway clock won't drag the whole domain along. The flip side: a controller that lay switched off for half a year with a dead BIOS battery won't synchronize its time after power-on until someone sets it by hand (Set-Date) or temporarily raises the limit.
  • Corrections up to MaxAllowedPhaseOffset (300 seconds by default on domain members, 1 second on standalone machines) are applied gradually: the clock speeds up or slows down until it catches up. Larger ones make the service set the clock immediately, and event 52 with the size of the shift appears in the log.

Accuracy. Windows Server 2016 rewrote the W32Time algorithm: under the right conditions — under 0.1 ms latency to the source, no further than stratum 5, no more than four network hops — it keeps accuracy to one millisecond, and on an ordinary network to 50 ms or a second; there's a separate Accurate Time guide in the documentation for this. By default seconds are enough for domain members, and controllers poll their source every 64–1024 seconds, choosing the interval adaptively. Windows Server 2019 added PTP (Precision Time Protocol, IEEE 1588) support for networks with hardware timestamps, and correct handling of leap seconds.


Virtual controllers

A virtual machine gets time from two places: from W32Time over the network and from the hypervisor through the integration services. The second source is more precise and doesn't depend on the network, but it breaks the hierarchy: a controller taking time from a host whose time is set by NTP from another source, or by nobody at all, is synchronized with the host, not with the PDC.

How it works in Hyper-V. The guest runs the VMICTimeProvider time provider, which hands W32Time samples of the host's time; the host reports its own stratum plus one to the guest. On Windows Server 2016 and later W32Time picks the best of the providers — by stratum first, then by root delay, dispersion and offset — and a domain-joined guest finds the most accurate source instead of taking the host's time by default.

For controllers Microsoft has two recommendations, and they differ. The page on virtual controllers on Hyper-V advises turning off time synchronization with the host for a controller guest entirely — the Time Synchronization integration service, which is done on the host:

PS C:\> Disable-VMIntegrationService -VMName dc1 -Name 'Time Synchronization'

The Windows Server 2016 accurate time guide offers a softer option for guests on 2012 R2 and older — keep the integration service, but disable the provider inside the guest:

PS C:\> Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider' -Name Enabled -Value 0
PS C:\> Restart-Service w32time

The idea of both is the same: a controller must live by the domain hierarchy, not by the host's clock, which may come from a different source or from none. VMware has its own synchronization settings in VMware Tools, and the goal is the same.

What not to do with a controller: revert it to a snapshot. The clock will jump back and W32Time will fix it, but the directory ends up in the past relative to its partners — that's USN rollback, which replication detects and stops, and which has its own section in part 18. Controllers on Windows Server 2012 and later are protected by VM-GenerationID: the hypervisor changes this identifier on a revert, the directory notices, resets its invocationID and RID pool, and then safely replicates again. But that's a rescue after a disaster, not a way of working.


What breaks

A difference of more than five minutes from a controller:

  • Kerberos answers KRB_AP_ERR_SKEW. The Windows client can recover: along with the error the controller reports its own time, the client repeats the request with that time substituted, and authentication succeeds. That saves the logon, but not everything else.
  • Replication between controllers runs over RPC with Kerberos, and it stops. In repadmin /showrepl it's error 5 "Access is denied", which says nothing about time; the hints come from dcdiag /test:CheckSecurityError with the line "Time skew error" and from "Replicate now" in the Sites snap-in with the message "There is a time and/or date difference between the client and server" (error 1398).
  • Certificates with a start date in the future are rejected; autoenrollment (part 25) silently doesn't issue a certificate.
  • DFS-R resolves conflicts as "last writer wins", comparing the timestamps of updates, and a change from a lagging server can lose to an earlier one.

The diagnostic sign: authentication errors that appeared at once for all users or all servers of one site. The first thing to check: w32tm /monitor.


A hands-on look

The state of all the domain's controllers with one command — from any machine in the domain:

PS C:\> w32tm /monitor
Getting AD DC list for default domain...
dc2.corp.example.com[192.168.1.11:123]:
    ICMP: 0ms delay
    NTP: -0.0012418s offset from dc1.corp.example.com
        RefID: dc1.corp.example.com [192.168.1.10]
        Stratum: 4
dc1.corp.example.com *** PDC ***[192.168.1.10:123]:
    ICMP: 0ms delay
    NTP: +0.0000000s offset from dc1.corp.example.com
        RefID: (unknown) [0x7B0200C0]
        Stratum: 3
dc3.corp.example.com[192.168.2.10:123]:
    ICMP: 18ms delay
    NTP: +0.0048102s offset from dc1.corp.example.com
        RefID: dc1.corp.example.com [192.168.1.10]
        Stratum: 4

Warning:
Reverse name resolution is best effort. It may not be
correct since RefID field in time packets differs across
NTP implementations and may not be using IP addresses.

RefID is where the controller takes its time from, offset is the difference from the PDC. The controllers are not listed alphabetically, and the PDC is marked *** PDC ***. The PDC's own source often shows up as (unknown): w32tm tries to turn the RefID into a name with a reverse DNS lookup, and if that fails, prints it in square brackets with the bytes reversed — 0x7B0200C0 here is 192.0.2.123, the same 0xC000027B as in w32tm /query /status. That's what the warning at the end of the output is about. Milliseconds are normal; seconds are a reason to look at the log; minutes are an outage. w32tm /monitor /domain:eu.corp.example.com checks a child domain.

A specific machine's source and where it comes from:

PS C:\> w32tm /query /source
dc1.corp.example.com

PS C:\> w32tm /query /peers
#Peers: 1

Peer: dc1.corp.example.com
State: Active
Time Remaining: 412.3764110s
Mode: 3 (Client)
Stratum: 3 (secondary reference - syncd by (S)NTP)
PeerPoll Interval: 10 (1024s)
HostPoll Interval: 10 (1024s)

Comparing with an arbitrary server without changing any settings — stripchart:

PS C:\> w32tm /stripchart /computer:dc1.corp.example.com /samples:3 /dataonly
Tracking dc1.corp.example.com [192.168.1.10:123].
Collecting 3 samples.
The current time is 15.10.2026 13:20:05.
13:20:05, +00.0011384s
13:20:07, +00.0010917s
13:20:09, +00.0011102s

This is how you check how far a workstation, an external NTP server or a server from another domain is from the controller. A positive value means the target's time is ahead of ours.

The request and reply in packets — pktmon filter add -p 123, then w32tm /resync on a domain member:

Network Time Protocol (NTP Version 3, client)
    Flags: 0x1b (LI: no warning, VN: 3, Mode: client)
    Peer Clock Stratum: unspecified or invalid (0)
    ...
    Transmit Timestamp: Oct 15, 2026 11:20:33.128745600 UTC
    Key ID: 0x00000450
    Message Authentication Code: 7c3f…e21a

Network Time Protocol (NTP Version 3, server)
    Flags: 0x1c (LI: no warning, VN: 3, Mode: server)
    Peer Clock Stratum: secondary reference (4)
    Reference ID: 192.168.1.10
    ...
    Key ID: 0x00000450
    Message Authentication Code: a91d…04c7

Key ID: 0x450 is 1104 in decimal, the RID of the WS1$ account: by it the controller found the machine password for the signature. The packet is 20 bytes longer than plain NTP (68 instead of 48) — that's MS-SNTP.

The time service log is the Microsoft-Windows-Time-Service provider in the System log:

PS C:\> Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-Time-Service'} -MaxEvents 5 | Format-Table TimeCreated, Id, LevelDisplayName, Message -Wrap
Id What
12 the root domain's PDC is set to the hierarchy, but there's no one above it: no external source assigned
29 no source is reachable, next attempt after the given number of minutes
34 the needed correction exceeds MaxPosPhaseCorrection or MaxNegPhaseCorrection, the clock is left alone
35 the service started synchronizing with the given source
36 time hasn't been synchronized for a long while, and the service stopped serving it to clients
37 valid time data is arriving from the source
47 a source from the manual list didn't answer and was dropped
50 a difference above the threshold has lasted longer than the set time; the service considers itself unsynchronized
52 the service set the clock by the given amount
129 no controller found to synchronize through the hierarchy
131, 134 the name of a hierarchy controller or of a manual-list source didn't resolve in DNS
142, 144 the machine stopped advertising itself as a time server (clock not synchronized) or as a reliable one

Event 129 on a domain member and 12 on the PDC mean the hierarchy is broken at that point.

And finally, directory time versus machine time:

PS C:\> [DateTime]::UtcNow.ToString('o'); (Get-ADRootDSE -Server dc2).currentTime.ToUniversalTime().ToString('o')
2026-10-15T11:21:40.4012389Z
2026-10-15T11:21:40.0000000Z

currentTime from RootDSE is the controller's clock from its own point of view, to the second; the difference from local UTC is the skew Kerberos will see.


Sources


This ends the domain block: three controllers, DNS, DHCP for both address families and consistent time. The next four parts are about how to manage all of it without walking up to the servers. We'll start with the tool we've been using since part 4 but have never taken apart.

Next — part 13: PowerShell for Active Directory. The ActiveDirectory module and the ADWS service it works through; -Filter and -LDAPFilter, -Properties and -SearchBase; the -Server parameter and choosing a controller; bulk operations through the pipeline; the AD: drive; Get-ADObject for everything that has no dedicated cmdlet; and how not to bring a controller down with a single query.

Only registered users can leave a comment.

Sign in or register to leave a comment.