Fixing FreeBSD Time Sync Drift When ntpd Won't Converge
How to diagnose FreeBSD ntpd source selection, reachability, offset thresholds, drift, virtual clocks, and safe one-time clock steps.
FreeBSD’s ntpd(8) disciplines a clock only after it finds credible, reachable time sources. A large offset, no selected peer, a bad frequency estimate, a competing hypervisor time agent, or blocked UDP/123 can all look like “ntpd is running but does nothing.” Diagnose peer selection and daemon logs before changing step thresholds or forcing the wall clock.
Establish the real clock and service state
Capture the operating-system time, UTC representation, daemon configuration, and process:
date
date -u
service ntpd status
sysrc ntpd_enable ntpd_sync_on_start ntpd_flags ntpd_config
grep -v '^[[:space:]]*#' /etc/ntp.conf
A wrong timezone changes how the same epoch is displayed; NTP synchronizes UTC time and does not repair /etc/localtime. If date -u is accurate but local output is wrong, fix timezone configuration instead of stepping the clock.
Review /var/log/messages for the daemon’s exact startup and panic-threshold messages. The default panic threshold is 1,000 seconds: without the startup exception, ntpd exits when the source consensus is farther away because such a gap may indicate a bad real-time clock or configuration. Offsets below that threshold do not simply get ignored.
Read peer selection, reach, and offset together
Use numeric output to remove DNS delays from the diagnostic:
ntpq -pn
ntpq -c rv
In the peers table, * marks the selected system peer, + a candidate included in the combining set, and rejection symbols identify falsetickers or excess sources. reach is an octal shift register of recent replies; it should build toward 377 after successful polls. when and poll show sampling cadence. offset is displayed in milliseconds by the peers command, while delay and jitter provide network-quality context.
No * is not automatically a failure during the first few polls. Wait long enough for several samples, then investigate if reach remains zero, all sources are rejected, or offsets disagree widely. Compare multiple independent sources; trusting one incorrect server can move every client consistently wrong.
NTP uses UDP port 123. Test DNS and routing separately and capture traffic when necessary:
drill pool.ntp.org
route -n get PEER_IP
tcpdump -ni em0 udp port 123
Replace the peer and interface. Requests leaving with no replies implicate firewall, NAT, routing, or the upstream server. Replies arriving while reach remains zero suggest configuration, authentication, source-address, or daemon issues. ntpdate -q can query without setting time, but the long-running daemon’s own ntpq and logs remain the authoritative view of selection.
Understand slew, step, and panic thresholds precisely
The default step threshold is approximately 128 milliseconds. Smaller corrections are normally slewed by slightly changing clock frequency. Larger corrections may be stepped after ntpd’s selection and stepout logic. Offsets exceeding the 1,000-second panic threshold cause exit unless the first large correction has been explicitly allowed.
The -g option permits one initial correction of any size; after that, exceeding the panic threshold remains fatal. FreeBSD exposes this safely through rc configuration:
sysrc ntpd_sync_on_start="YES"
service ntpd restart
The Handbook recommends ntpd_sync_on_start=YES for systems without a battery-backed clock. It causes the rc script to start ntpd with the large-initial-correction allowance. It is preferable to an undocumented local edit of /etc/rc.d/ntpd.
For a controlled one-shot synchronization, stop the resident daemon and use its configured sources:
service ntpd stop
ntpd -qg
service ntpd start
-q sets the time once and exits; -g permits the first correction past the panic limit. If an immediate step rather than normal threshold behavior is specifically required, current ntpd also documents -G, but backward steps can disrupt databases, logs, build systems, TLS validation, Kerberos, leases, and clustered applications. Schedule and communicate the step instead of treating it as harmless maintenance.
Avoid tinker panic 0 as a reflex. It removes a safety check on every startup and can authorize a compromised or badly configured source to make an extreme correction. A one-time -g policy plus monitoring retains protection after synchronization.
Find why a corrected clock drifts again
After initial sync, observe over multiple poll intervals:
ntpq -pn
ntpq -c rv
cat /etc/ntp.drift
sysctl kern.timecounter.hardware kern.timecounter.choice
The drift file stores ntpd’s learned frequency correction and normally needs hours to settle. Deleting it repeatedly forces relearning and can worsen convergence. Check ownership and write access if the daemon logs drift-file errors, especially when running as the unprivileged ntpd user.
A steadily growing offset with healthy reach can indicate a poor hardware timecounter, VM scheduling pauses, CPU power-management interactions, or another agent changing the clock. Do not run competing guest tools and ntpd with both authorized to step time. On a virtual machine, inspect host synchronization, pause/resume events, live migration, and the hypervisor’s documented guest time policy.
A dead motherboard battery explains a wrong time after every cold boot but not rapid drift during continuous uptime. Compare reboot history and RTC behavior. On appliances without RTC, make the initial -g path reliable and ensure DNS/TLS dependencies do not require accurate time before NTP can contact peers.
Large asymmetric network delay can bias offset estimates. Use nearby, diverse servers and investigate jitter or path changes instead of lowering poll intervals aggressively. The ntpd manual warns that poll control is an adaptive system; manual minimums increase network load and do not repair a defective oscillator.
Diagnose disagreement instead of choosing the closest-looking peer
If several sources are reachable but none is selected, capture association and system variables:
ntpq -c associations
ntpq -c rv
ntpq -c 'rv &1'
Association identifiers differ per run; replace &1 with the association selected from actual output when supported by the installed ntpq. Look at status words, stratum, root delay, root dispersion, jitter, and reach. A low stratum is not proof of accuracy, and the peer showing the smallest instantaneous offset is not automatically correct. NTP selection compares histories and agreement among sources to reject falsetickers.
Avoid configuring several DNS names that all resolve to the same physical server or provider and calling that diversity. Use sources with independent failure domains appropriate to the organization, or an authenticated internal hierarchy. If an internal NTP appliance disagrees with public sources, investigate its upstream reference and leap/smear policy before forcing clients toward either side.
Clock smearing around leap events can make two otherwise healthy source groups disagree temporarily if one group smears and another follows UTC steps. Mixing policies in one client population is the design defect; lowering selection thresholds hides it. Record source policy and make it consistent across the fleet.
Verify convergence and protect against recurrence
Success means a selected peer, healthy reach, offsets and jitter appropriate to the environment, no repeated panic exits, and stable behavior after reboot. Monitor ntpd service state, synchronization status, offset, and source count—not just whether a PID exists.
Record the authorized sources and make firewall rules explicit. Alert on a clock step, loss of all peers, and offsets large enough to threaten authentication or logs. Test a cold boot and VM resume. FreeBSD’s daemon is conservative because incorrect time can corrupt evidence and distributed protocols; the correct fix is to restore credible inputs and a safe initial correction, not to disable every threshold.
Related:
- Fixing FreeBSD Jail Networking When VNET Jails Can’t Reach the Network
- How to Configure Static Networking on FreeBSD from Scratch
Sources: