Decode the 8-bit reach register on chrony and ntpd
Misread frequently as a score from 0 to 8, the reach column in ntpq -p is actually the decimal-rendered value of an 8-bit circular shift register, printed in octal. RFC 5905 calls it the reachability register.
At each poll:
Because it is printed in octal, the maximum value is 377 octal = 11111111 binary = 255 decimal = all 8 polls succeeded.
| Reach (oct) | Binary (MSB→LSB) | Meaning |
|---|---|---|
| 0 | 00000000 | 8/8 misses — peer unreachable |
| 1 | 00000001 | Most recent poll succeeded, the 7 before failed — first-contact |
| 3 | 00000011 | Last 2 polls OK, 6 before failed |
| 7 | 00000111 | Last 3 polls OK |
| 17 | 00001111 | Last 4 polls OK — recovering |
| 37 | 00011111 | Last 5 polls OK |
| 77 | 00111111 | Last 6 polls OK |
| 177 | 01111111 | Last 7 polls OK |
| 200 | 10000000 | Only the oldest poll succeeded — flagging peer |
| 376 | 11111110 | 7 OK, last poll missed — recent glitch |
| 377 | 11111111 | All 8 OK — healthy |
A peer comes online as reach 0. After the first successful poll it becomes 1, then 3, 7, 17, 37, 77, 177, 377 — if every subsequent poll succeeds. At default minpoll 6 (64 s interval), the path from first contact to full reach 377 takes 8 polls × 64 s = 512 seconds (8.5 min).
The iburst directive changes this: on the first poll, 8 packets are sent back-to-back at ~2 s intervals. Reach 377 is achieved in about 16 seconds instead of 8.5 minutes.
# /etc/chrony/chrony.conf
pool 2.pool.ntp.org iburst
server ntp.rdem-systems.com iburst nts
# /etc/ntp.conf
server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
iburst on every peer. The only downside is 8 packets at start-up — negligible against the benefit of avoiding stratum 16 for 8 minutes after boot.
A peer that was steady at 377 and suddenly drops to 0 over 8 polls (~8.5 min) has lost reachability. Four typical causes:
Stateful firewalls track UDP "connections" (conntrack in Linux). Default UDP timeout is 30 s on many distributions — shorter than the default NTP poll interval of 64 s. Result: every other packet gets dropped.
# Check current timeout (Linux)
$ cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout
30
# Raise to 300 s to survive NTP poll cadence
$ sudo sysctl -w net.netfilter.nf_conntrack_udp_timeout=300
Check from an outside vantage point: dig +short ntp.rdem-systems.com, then ntpdate -q <ip> from a different ASN. If both fail, the peer is offline and you need an alternative source.
A new prefix origination, RPKI invalid, or ISP route flap can make a specific upstream unreachable while the rest of the Internet works fine. traceroute and mtr will reveal where the path breaks.
Check interface counters for dropped packets: ip -s link show and ss -s. On VMs, a noisy-neighbour vSwitch can drop bursts of UDP — usually correlates with spikes in other UDP services on the same host.
When the GUI-level diagnostic hits the end, drop to the wire:
# Capture NTP traffic for 3 minutes
$ sudo tcpdump -nn -i any udp port 123 -w /tmp/ntp.pcap &
$ sleep 180 && sudo kill %1
$ tcpdump -nn -r /tmp/ntp.pcap | head -40
Expected: a request every 64 s (or as configured) from your client's ephemeral port to peer's 123, plus a response. If you see only outbound packets, the return path is broken. If you see nothing at all, the daemon is not actually sending (check it is running).
chrony does not print the octal register, but chronyc sources -v shows the "Reachability register (octal)" field when run with -v:
$ chronyc sources -v
MS Name/IP address Stratum Poll Reach LastRx Last sample
=======================================================================
^+ ntp.rdem-systems.com 1 6 377 18 -0.234ms ...
^* time.cloudflare.com 3 6 377 42 +0.512ms ...
^? ntp1.ptb.de 1 6 037 205 +1.120ms ...
Here, ntp1.ptb.de is at 037 octal = 31 decimal = last 5 of 8 polls OK — it is recovering, not yet trusted.
After the fix — related sites: