LinuxXXIV · Time Synchronisationchrony
chrony architecture - the modern NTP client and server
What you'll learn
- Describe chrony sources and selection
- Configure chrony as an NTP client
- Configure chrony as an NTP server for a private network
- Read chronyc tracking and sources output
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
chrony is the recommended NTP client on modern RHEL, Rocky, Alma, Fedora, and is increasingly used on Debian and Ubuntu. It is more accurate than the older ntpd, handles intermittent connectivity better, and can act as both client and server. This lesson is about configuring it correctly.
chrony vs the alternatives
| Tool | When to use |
|---|---|
| chronyd | Modern default on most distributions; client + server |
| systemd-timesyncd | Lightweight NTP client; not a server |
| ntpd | Legacy; still used on older systems; client + server |
For new deployments, chrony is the default choice.
Configuration
/etc/chrony.conf (RHEL family) or /etc/chrony/chrony.conf
(Debian):
# Use public NTP servers
pool 2.pool.ntp.org iburst
pool time.cloudflare.com iburst
# Use the company NTP server
server ntp1.example.com iburst prefer
server ntp2.example.com iburst
# Allow this host to serve NTP to the local network
allow 10.0.0.0/24
# Drift file (records clock frequency offset)
driftfile /var/lib/chrony/drift
# Make stepping corrections instead of slewing for large
# offsets (default is to slew up to 1 second)
makestep 1.0 3
# Send NTP requests even when the network is offline
# (useful for laptops)
rtcsync
# Log file
logdir /var/log/chrony
# Stratum for this server (if it is one)
local stratum 10
Apply:
sudo systemctl enable --now chronyd
sudo systemctl restart chronyd
Read the state
chronyc tracking
Output:
Reference ID : C0A80A01 (10.0.0.1)
System time : 0.000001234 seconds fast of NTP time
Last offset : +0.000000456 seconds (estimated)
RMS offset : 0.000012345 seconds
Frequency : 23.456 ppm (fast)
Residual freq : +0.001 ppm
Skew : 0.123 ppm
Root delay : 0.012345 seconds
Root dispersion : 0.001234 seconds
Update interval : 64.2 seconds
Leap status : Normal
Reading this:
- Reference ID: which source is currently selected.
- System time: difference between the system clock and the selected source. <1ms is good.
- Last offset: most recent offset measurement.
- Frequency: how far the system clock is running from true time, in ppm. A small value is good.
- Skew: estimated error in the frequency.
- Root delay: round-trip time to the source.
- Root dispersion: accumulated error from the source.
Sources
chronyc sources
Output:
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp1.example.com 2 6 377 35 +12us[ -23us] +/- 85us
^- ntp2.example.com 2 6 377 34 -45us[ -67us] +/- 92us
^- time.cloudflare.com 3 6 377 33 +120us[+98us] +/- 210us
^- 2.pool.ntp.org 2 6 377 32 +156us[+142us] +/- 180us
Reading the flags:
*= current sync source (the one being used).+= acceptable; combined with the selected source.-= acceptable but not selected.?= unreachable or rejected.x= rejected (by the algorithm).~= too variable.
^* is the source your clock is synced to. The other
acceptable sources are in case the primary fails.
Source selection
chrony uses a complex algorithm to select the best source. Key factors:
- Stratum: lower is better. Stratum 0 is a hardware reference clock; stratum 1 is a server directly connected to one; stratum 2 is synced to stratum 1; etc.
- Reach: a bit field showing recent reachability.
377(octal) means all 8 recent attempts succeeded. - Last sample: offset and error estimate.
If ^* switches between sources, you have a configuration
issue (unstable network, sources disagree, etc.).
chrony as a server
To serve NTP to a private network:
# In /etc/chrony.conf
allow 10.0.0.0/24
local stratum 10
The local stratum 10 directive makes this host a stratum
10 server that does not sync to the internet - useful for
isolated networks.
For a server that syncs to public NTP and serves to the private network:
pool time.cloudflare.com iburst
allow 10.0.0.0/24
Clients on the private network configure their chrony to use this server.
So open the port as well as the ACL:
# firewalld (RHEL, Fedora, SUSE)
sudo firewall-cmd --permanent --add-service=ntp
sudo firewall-cmd --reload
sudo firewall-cmd --list-services # confirm 'ntp' is listed
# nftables (Debian, Ubuntu, or any default-deny baseline)
sudo nft add rule inet filter input ip saddr 10.0.0.0/24 udp dport 123 accept
sudo nft list chain inet filter input # confirm the rule is above any drop
# ufw
sudo ufw allow from 10.0.0.0/24 to any port 123 proto udp
Confirm chronyd is bound and listening:
sudo ss -ulnp | grep ':123'
# UNCONN 0 0 0.0.0.0:123 0.0.0.0:* users:(("chronyd",pid=...,fd=...))
Then verify from a client, never from the server. A
server-side test proves nothing, because the loopback path never
traverses the filter. On a client, chronyc sources -v must
show Reach 377 against this server before you call the setup
complete.
Authentication
chrony supports NTP authentication via symmetric keys or public keys (NTS, RFC 8915):
# Symmetric key: the key file holds ID/algorithm/secret
keyfile /etc/chrony/chrony.keys
# key, trust and require are OPTIONS on the source line
server ntp1.example.com iburst key 1 trust require
On chrony 4.0 and later, prefer NTS for anything crossing an untrusted path. There is no shared secret to distribute:
server ntp1.example.com iburst nts
For a private network, authentication prevents rogue NTP servers from poisoning your clocks.
Common recipes
Accurate time from public servers:
pool time.cloudflare.com iburst
pool ntp1.example.com iburst
makestep 1.0 3
rtcsync
Internal NTP server:
server ntp1.example.com iburst
server ntp2.example.com iburst
allow 10.0.0.0/24
local stratum 10
Stratum 1 server with GPS:
The refclock syntax is refclock driver parameter [option]....
Pick one driver per line. gpsd can feed chrony either by
shared memory (SHM) or by Unix socket (SOCK); the two are
different drivers and must never be mixed on one line.
Shared memory, which is what gpsd writes by default:
# gpsd writes SHM segment 0 (NMEA time) and segment 1 (PPS)
refclock SHM 0 refid GPS precision 1e-1 offset 0.128 delay 0.2 noselect
refclock SHM 1 refid PPS precision 1e-7 prefer
allow 10.0.0.0/24
The socket protocol gpsd also speaks:
refclock SOCK /run/chrony.ttyS0.sock refid GPS
allow 10.0.0.0/24
The noselect on the NMEA segment is deliberate: NMEA
sentences are accurate to tens of milliseconds, so they
label the second while PPS supplies the precision. The
offset 0.128 value is the serial-line delay for your
receiver — measure it, do not copy it.
Initial sync after a long outage:
sudo systemctl stop chronyd
sudo chronyd -q 'server time.cloudflare.com iburst'
sudo systemctl start chronyd
The -q flag tells chrony to set the time and exit - useful
after a long downtime when the clock is far off.
Knowledge check
Knowledge check · 4 questions
Q1. Which chronyc command shows the current NTP source and timing offset?
Q2. chrony can act as both an NTP client and an NTP server.
Q3. Which of the following are valid chrony configuration directives? Select all that apply.
Q4. You add authentication to /etc/chrony.conf with a `server ntp1 key 1` line followed by a separate `trust 1` line, restart chronyd, and walk away. An hour later Kerberos logins start failing across the fleet. What is the most likely cause?
Passing score: 75%. Answers are checked in this browser.