Identify the rogue source, replace it, keep the majority truthful
In NTP parlance, a truechimer is a source whose reported time agrees with the majority of other sources within the protocol's correctness tolerance. A false ticker is a source that disagrees — either because it is wrong, or because the path to it is so asymmetric that the measurement lies about it.
What a false ticker is NOT:
~ or marginal).For each peer, NTP constructs a correctness interval: [offset − root_distance, offset + root_distance] where root_distance = root_delay/2 + root_dispersion. This interval expresses "the true time lies somewhere in here, and I am at least this wide about it".
The Marzullo-Mills algorithm (RFC 5905 §11.2.1) finds the largest subset of peers whose intervals share a common point. Peers inside that intersection are truechimers; peers outside are false tickers.
ntpq prefixes each peer with a 1-character state flag. The relevant ones for false-ticker diagnosis:
| ntpq prefix | Meaning |
|---|---|
* | System peer — the selected source |
+ | Candidate — in the combine set |
- | Outlyer — rejected by clustering algorithm |
x | False ticker — rejected by intersection |
. | Too-many-survivors reject |
# | Good but not used (too many candidates) |
(space) | Reject (unreachable, bad sanity, high stratum) |
chronyc uses two columns: M (mode: ^ server, = peer) and S (state):
MS Name/IP address
^* ntp.rdem-systems.com synced, current sync source
^+ time.cloudflare.com combined
^- time.google.com rejected by cluster
^x rogue-server.example.com FALSE TICKER
^? down-server.example.com connectivity lost
^~ jittery.example.com too variable
The most common cause. A Stratum 2 whose own upstream went into stratum 16, a server running systemd-timesyncd without backup, a pool member with a broken GPS — all can produce seconds of real offset. Check the source's own chronyc tracking / ntpq -c rv from the operator side, or query it from an independent vantage point with ntpdate -q.
If the path to one peer is via a tunnel (IPsec, VPN, GRE) while the other peers are direct, the one-way latencies are wildly different. NTP assumes symmetry; asymmetry shows up as offset, and can make an otherwise-correct source look like a liar.
A peer that reports a large root_delay or root_dispersion has a wide correctness interval. If its offset is close to correct but its interval is wider than the overlap of the others, Marzullo-Mills may still exclude it as a safety measure (it cannot narrow the overlap, so it is rejected as non-contributing).
With only 3 peers, any single real disagreement creates an even split. Two agreeing peers are not enough for Marzullo-Mills to feel confident discarding the third — so all three can end up flagged or selection fails entirely. Fix: always configure 4+ peers in production.
Before editing ntp.conf, confirm who is telling the truth.
# Get offsets for every configured source
$ chronyc sourcestats -v
# Query each source independently
$ for s in ntp.rdem-systems.com time.cloudflare.com ntp1.ptb.de suspect.example.com; do
echo -n "$s: "
ntpdate -q "$s" 2>&1 | grep offset
done
# Cross-check against this live validator
$ curl -s https://ntp.rdem-systems.com/time-api.php
Compare the offsets. The one that diverges is your false ticker.
Once identified:
# Sample chrony.conf with 4 diversified sources (3 NTS, 1 pool)
server ntp.rdem-systems.com iburst nts # FR, AS206014
server time.cloudflare.com iburst nts # anycast
server nts.netnod.se iburst nts # SE, Netnod
pool 2.pool.ntp.org iburst
makestep 1.0 3
rtcsync
Without NTS, UDP 123 is unauthenticated. An on-path attacker can:
pool.ntp.org DNS result and substitute a rogue server.Signs that a false ticker is an attack rather than a bug:
After the fix — related sites: