Where to start First steps track · 15 stages
Linux Labs

chrony: time synchronization over NTP

13 min Ubuntu 24.04 · chrony 4.5

An NTP client and server: keeps the system clock in step with time servers and can serve time to your machines.

Cheat sheet

8 commands Printable version
  • chronyc tracking is the clock in sync, and how close
  • chronyc sources time servers and their state
  • chronyc sourcestats offset and spread per server
  • sudo chronyc reload sources reread /etc/chrony/sources.d
  • sudo chronyc burst 4/4 poll the servers now
  • sudo chronyc makestep correct the clock in one step
  • chronyc waitsync 10 0.5 wait for sync (for scripts)
  • sudo systemctl restart chrony reread chrony.conf and conf.d

Overview

chrony is two programs. The chronyd service keeps the system clock: it polls time servers over NTP (UDP port 123) and brings the clock to their time. chronyc queries the service and gives it commands over a local socket. By default the Ubuntu clock is kept by systemd-timesyncd — a client only, whose state timedatectl shows. chrony keeps the clock more precisely, shows in detail where the time comes from, and can serve it to other machines.

chrony is not installed on the lab machine; the package is in /opt/debs:

bash
sudo apt-get install -y -q --no-download \
  -o Dir::Cache::archives=/opt/debs chrony

The install takes about 10 seconds and removes systemd-timesyncd: two NTP clients on one machine would get in each other's way. The chrony service is enabled and running right away.

Is the clock in sync — tracking

node1 gets its time from node2:

text
student@node1:~$ chronyc tracking
Reference ID    : 0A2FFC0C (node2)
Stratum         : 11
Ref time (UTC)  : Wed Oct 21 11:45:02 2026
System time     : 0.000004130 seconds slow of NTP time
Last offset     : -0.000005138 seconds
RMS offset      : 0.000005138 seconds
Frequency       : 0.000 ppm slow
Residual freq   : -11.864 ppm
Skew            : 1000000.000 ppm
Root delay      : 0.000475221 seconds
Root dispersion : 4.684397697 seconds
Update interval : 2.0 seconds
Leap status     : Normal
Field What it means
Reference ID where the time comes from now: the address in hex and the name
Stratum the level in the NTP hierarchy: the source is one level lower
System time how far the system clock is now behind (slow) or ahead (fast)
Last offset the offset at the last measurement
Leap status Normal — the clock is in sync, Not synchronised — it is not

The gap between the clock and the source is the System time line: 4 microseconds here. Without sources Reference ID is 00000000 (), Stratum is 0, Ref time is in 1970, and Leap status is Not synchronised.

Servers — sources

text
student@node1:~$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
^* node2                        10   6    17     4
    +51us[  +46us] +/-  238us

The last line is a single line in the output; it is wrapped here.

Column What it means
M ^ — server, = — peer, # — the machine's own clock
S * — selected for adjusting, + — combined with it, - — not combined, ? — unusable, x — may be wrong, ~ — too variable
Stratum the server's level
Poll the polling interval as a power of two: 6 is 64 seconds
Reach the last eight polls in octal: 377 — all answered, 17 — four polls so far, each answered, 0 — no answers at all
LastRx how many seconds ago the last answer came
Last sample the offset after adjustment, the measured one in brackets, the error after +/-

Reach reads from right to left: the lowest bit is the latest poll. After UDP 123 was closed on node2, 17 turned into 170: the last three polls got no answer. chronyc sources -v prints the same legend above the table.

Stepping and slewing — makestep

Usually chronyd adjusts the clock smoothly: it makes the clock run a little faster or slower, and the time does not jump: no faster than 83 milliseconds per second (maxslewrate defaults to 83333 ppm), so 2 hours would take at least a day. A large gap it fixes with a step. The line makestep 1 3 in the Ubuntu config allows a step when the gap is over 1 second — but only during the first three updates after start.

The node1 clock is 2 hours fast, the node2 source is added with reload sources — 4 seconds later the journal shows:

text
chronyd[5096]: Selected source 10.47.252.12 (node2)
chronyd[5096]: System clock wrong by -7199.962332 seconds
chronyd[5096]: System clock was stepped by -7199.962332 seconds

If you set the clock by hand (date -s) on a running machine, chronyd notices the jump and drops the sync until a new measurement, then steps the time back:

text
chronyd[1939]: Forward time jump detected!
chronyd[1939]: Can't synchronise: no selectable sources
chronyd[1939]: Selected source 10.71.162.12 (node2)
chronyd[1939]: System clock wrong by -7199.999899 seconds
chronyd[1939]: System clock was stepped by -7199.999899 seconds

A new measurement comes on the polling schedule (64 seconds and more) or right after sudo chronyc burst 4/4.

Configuration

11

Files and directories

The main file is /etc/chrony/chrony.conf. In Ubuntu 24.04, without comments, it is:

text
confdir /etc/chrony/conf.d
pool ntp.ubuntu.com        iburst maxsources 4
pool 0.ubuntu.pool.ntp.org iburst maxsources 1
pool 1.ubuntu.pool.ntp.org iburst maxsources 1
pool 2.ubuntu.pool.ntp.org iburst maxsources 2
sourcedir /run/chrony-dhcp
sourcedir /etc/chrony/sources.d
keyfile /etc/chrony/chrony.keys
driftfile /var/lib/chrony/chrony.drift
ntsdumpdir /var/lib/chrony
logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
makestep 1 3
leapsectz right/UTC

Your own settings go into two directories:

Directory Files How to apply
/etc/chrony/conf.d/ *.conf — any directives sudo systemctl restart chrony
/etc/chrony/sources.d/ *.sources — only server, pool, peer sudo chronyc reload sources

reload sources rereads only sources.d: a server line in conf.d does not appear after it, a restart is needed. The lab machine has no network to the outside, the pool names do not resolve, and chronyc activity prints 16 sources with unknown address — these lines do no harm.

Directives are in the table below; the full list is man chrony.conf.

Directive What it does Example
server sources.d, conf.d A time server by name or address; iburst makes the first four polls back to back. server node2 iburst
pool chrony.conf, sources.d A name behind which there are many servers; maxsources sets how many to use. pool ntp.ubuntu.com iburst maxsources 4
allow conf.d Which addresses to answer as an NTP server; without allow chronyd is only a client. allow 10.0.0.0/8
local conf.d Serve the time of the machine's own clock even when the server has no source. local stratum 10
makestep chrony.conf Step the clock if the gap is over N seconds, but only during the first M updates after start. makestep 1 3
confdir chrony.conf A directory of *.conf files read when the service starts. confdir /etc/chrony/conf.d
sourcedir chrony.conf A directory of *.sources files; chronyc reload sources rereads them without a restart. sourcedir /etc/chrony/sources.d
driftfile chrony.conf The file where chronyd remembers how fast or slow the machine's clock runs, across restarts. driftfile /var/lib/chrony/chrony.drift
rtcsync chrony.conf Copy the system time to the hardware clock (RTC) every 11 minutes. rtcsync
maxupdateskew chrony.conf Ignore measurements while the estimated frequency error is above this many ppm. maxupdateskew 100.0
logdir chrony.conf The chrony log directory; the log directive sets what to write. logdir /var/log/chrony

Nothing found. Try a shorter query.

Recipes

4

A client of your own server

bash
echo 'server node2 iburst' | sudo tee /etc/chrony/sources.d/lab.sources
sudo chronyc reload sources
chronyc sources                  # a few seconds later: ^* node2

iburst makes the first four polls back to back, so the sync takes seconds instead of minutes. Only node2 stays as a source if you comment out the pool lines in chrony.conf and restart the service.

A server for your machines

On node2:

bash
printf 'allow 10.0.0.0/8\nlocal stratum 10\n' \
  | sudo tee /etc/chrony/conf.d/lab-server.conf
sudo systemctl restart chrony
sudo ss -ulnp | grep chronyd     # 0.0.0.0:123 — waits for clients
sudo chronyc clients             # who asked for the time

allow sets which addresses to answer: without it chronyd works only as a client. The lab machines get their own network in each session — ip -br a shows it. local stratum 10 allows serving the time of its own clock even when the server itself has no source: without this line such a server is no use to clients. The clients of node2 get level 11.

Check the sync from a script

text
student@node1:~$ chronyc waitsync 5 0.5; echo $?
try: 1, refid: 0AF8030C, correction: 0.388168603, skew: 1000000.000
0

waitsync <tries> <offset> waits until the clock is in sync and the correction is below the given one (0.5 s here), polling every 10 seconds. Code 0 means it got there, 1 means the tries ran out. The offset as a number is the fourth field:

bash
chronyc tracking | awk '/System time/ {print $4}'

Correct the clock now

bash
sudo chronyc burst 4/4           # a new measurement now
sleep 5
sudo chronyc makestep            # step by the whole offset

makestep without a fresh measurement changes nothing: right after date -s it answers 200 OK, and the clock stays as it was. sudo systemctl restart chrony also corrects the clock: after start the first three updates of makestep 1 3 apply again.

Troubleshooting

7

506 Cannot talk to daemon

chronyd is not running: systemctl status chrony. If chrony is not installed, there is no chronyc command at all.

501 Not authorised

Commands that change the service state — makestep, burst, reload sources — are for root: sudo chronyc …. tracking, sources and sourcestats work as a regular user.

sources is empty

The server name does not resolve: chronyd added a source without an address and waits. The journal has Added source ID#0000000017 (ntp.lab.invalid) with no IP, and chronyc activity counts it among sources with unknown address. Check the name: getent hosts <name>.

Source ^?

text
^? 10.255.255.1                  0   6     0     -
    +0ns[   +0ns] +/-    0ns

The server has never answered (Reach 0): a wrong address, no chrony on it, no allow for your network, or UDP 123 closed by a firewall — firewall. If Reach was non-zero and dropped, the answers stopped coming recently.

Leap status: Not synchronised

There is no usable source: see chronyc sources. Right after a manual date -s it is the same — chronyd waits for a new measurement.

Set the clock by hand, and it went back

chronyd brings the time back to the source with a step, as in chrony. To shift the time for an experiment, turn the sync off: sudo timedatectl set-ntp false stops chrony, set-ntp true starts it again.

timedatectl timesync-status: Failed to query server

text
Failed to query server: The name org.freedesktop.timesync1 was not
  provided by any .service files

timesync-status asks systemd-timesyncd, and the chrony install removed it. With chrony use chronyc tracking. Right after the install timedatectl may print NTP service: n/a; the reliable check is Leap status in chronyc tracking.

Danger zone

1

Stepping the clock on a live server

A step back repeats time that has already passed: journal lines go out of order, cron jobs and timers may fire twice or be skipped, TLS certificates look "not yet valid". On a live server let chrony adjust the clock smoothly, and keep makestep and date -s for a machine you have just brought up.

Terms

1
NTP
The protocol for checking a clock against a time server over the network, UDP port 123. The client is systemd-timesyncd or chrony; a server for your machines is chrony with allow. Also appears in: timedatectl, date, Locale and encoding

See also

  • timedatectl — time zone, UTC and systemd-timesyncd.
  • date — show and set the time, Unix time.
  • systemd-timers — timers depend on the right time.
  • cron — schedules in local time.
  • firewall — open UDP 123 for clients.
  • journalctl — the service journal: journalctl -u chrony.
Loading…