EN FR Home

NTP False Ticker Detection & Fix

Identify the rogue source, replace it, keep the majority truthful

1. What a false ticker is (and is not)

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:

2. How the Marzullo-Mills selection flags it

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.

Implication. With 4 peers you can tolerate 1 false ticker (3 truechimers still outvote). With 3 peers and 1 false ticker, you are in deadlock: 2/1 is not decisive, and depending on how the intervals sit, selection may fail entirely — exactly the scenario that produces stratum 16 with sources reachable.

3. Decode the ntpq "x" prefix and chronyc states

ntpq prefixes each peer with a 1-character state flag. The relevant ones for false-ticker diagnosis:

ntpq prefixMeaning
*System peer — the selected source
+Candidate — in the combine set
-Outlyer — rejected by clustering algorithm
xFalse 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

4. The 4 real-world causes

Cause A — upstream server is actually off

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.

Cause B — asymmetric path

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.

Cause C — inflated root distance

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).

Cause D — insufficient peer count

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.

5. Confirm which source is actually wrong

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.

6. Replace without creating a new deadlock

Once identified:

  1. Do not simply remove the peer — replace it. You want 4+ active peers at all times.
  2. Pick a replacement on a different operator and different ASN than your other peers. See our dual-operator options.
  3. Prefer NTS-authenticated sources when available — they prevent an attacker-in-the-middle from turning a truechimer into a false ticker.
  4. After reloading chrony/ntpd, wait 4–8 poll intervals (~5–15 min) for the new peer to reach 377 and for selection to stabilise.
# 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

7. Security: when a false ticker is an attack

Without NTS, UDP 123 is unauthenticated. An on-path attacker can:

Signs that a false ticker is an attack rather than a bug:

Defence. Deploy NTS (Network Time Security) on every authenticable source. An attacker cannot forge an NTS-AEAD packet without the cookie — false-ticker attacks become impossible at the client.
After the fix. Run this validator's live diagnostic to confirm sync is restored. Measure jitter on the new source set to ensure the replacement is not itself jittery. For regulated environments, produce audit evidence of the NTS deployment and the 4-source diversification.

After the fix — related sites: