Chrony is the NTP client and server of current Linux distributions: it aligns the clock on time servers, with millisecond-range accuracy over the Internet, and can encrypt the exchange with NTS. A correct clock underpins log correlation, the validity of TLS certificates and every distributed system. This guide installs chrony, shows where its configuration lives depending on the distribution, teaches you to read chronyc tracking and chronyc sources, enables NTS, then opens the service to a local network. Audience: Linux administrators. Every output was recorded on 30 September 2026, with chrony 4.6.1 (Debian 13), 4.5 (Ubuntu 24.04) and 4.8 (AlmaLinux 10).
What you will learn
- Install chrony and check that it really replaces the time service already present.
- Find the configuration file, which does not sit in the same place in every family.
- Read the synchronisation state with
chronyc trackingandchronyc sources. - Encrypt the time exchange with NTS and check that it works.
- Serve time to a local network without opening the service to everyone.
Prerequisites
- A Debian 13, Ubuntu 24.04 or RHEL 10 server (or AlmaLinux, Rocky), with
sudorights. - Outbound Internet access on port UDP 123 (NTP), and TCP 4460 for NTS.
- Knowing how to start a service and read its state with
systemctl.
Why a server's clock must be right
A drifting clock does not bring a server down: it skews everything that compares dates. Two logs thirty seconds apart make an incident impossible to reconstruct, a TLS certificate looks "not yet valid" or "expired", a time-limited authentication token is rejected. Every hardware clock drifts, and the clock of a virtual machine also depends on the host that runs it.
The NTP protocol (Network Time Protocol), designed by David L. Mills and deployed as early as 1985, corrects this drift by querying time servers. Chrony is its modern implementation: started in 1997 by Richard Curnow and maintained by Miroslav Lichvar, it locks on faster than the old ntpd at boot, copes better with network outages, and it is the one the reference distributions offer today.
Key concepts
Five notions are enough to read everything chrony prints. They come back in every output of chronyc, and mixing them up leads to wrong diagnoses.
Stratum
The stratum measures the distance to the reference clock. Stratum 0 is the physical clock itself (atomic, GPS), which does not speak NTP. A server directly connected to it is stratum 1, a server locked on a stratum 1 is stratum 2, and so on. Your server therefore shows the stratum of its source plus one.
Sources, servers and pools
A source is a server being queried. The server directive declares one, the pool directive declares a DNS name hiding several servers: chrony keeps up to four of them by default. Distributions ship a pool (2.debian.pool.ntp.org, 2.almalinux.pool.ntp.org), which gives several sources without writing anything.
Drift and driftfile
The drift is the rate difference between the local clock and real time. Chrony measures it continuously and records it in the driftfile (/var/lib/chrony/chrony.drift on Debian, /var/lib/chrony/drift on AlmaLinux): after a restart, it starts again with an already known correction instead of learning it again.
Slew or step: makestep
By default, chrony corrects smoothly (slew): it speeds up or slows down the clock until the gap is closed, never going backwards, which preserves the order of logs and transactions. The makestep 1 3 directive, present in all three default configurations, allows an immediate step if the gap exceeds 1 second, and only during the first 3 updates: that is the case of a machine left switched off for a long time.
iburst
The iburst option, after server or pool, sends a burst of requests at startup instead of waiting for the normal interval. The first lock thus takes a few seconds instead of several minutes. It is set on every source of the default configurations.
Install chrony
On Debian and Ubuntu, installing chrony uninstalls systemd-timesyncd, the minimal time client shipped by default on Ubuntu. Both packages provide the same virtual service, time-daemon, and exclude each other: APT says so plainly on Ubuntu 24.04. On RHEL and its derivatives, chrony is the standard time service.
sudo apt updatesudo apt install chronyThe following packages will be REMOVED: systemd-timesyncdThe service starts as soon as it is installed. It is called chrony.service, with an alias chronyd.service: both names work with systemctl.
sudo dnf install chronysudo systemctl enable --now chronydThe service is called chronyd.service. enable --now starts it right away and at every boot of the machine.
Then check that the service is running and that the clock is synchronised, two different things:
systemctl status chronydchronyc trackingConfiguring chrony
The configuration file does not have the same path in every family, and it is the first source of mistakes. Recorded on the three reference distributions:
| Family | Configuration | Keys | Drop-in directories |
|---|---|---|---|
| Debian 13, Ubuntu 24.04 | /etc/chrony/chrony.conf | /etc/chrony/chrony.keys | /etc/chrony/sources.d/, /etc/chrony/conf.d/ |
| RHEL 10, AlmaLinux 10 | /etc/chrony.conf | none by default | none by default |
Editing /etc/chrony.conf on a Debian means writing a file that chronyd will never read: the configuration looks in place, and nothing changes. When in doubt, ask systemd which daemon runs and with which options:
systemctl cat chronydThe default configuration
Here is the configuration shipped by Debian 13, comments removed. AlmaLinux 10's holds the same essential directives, with the pool 2.almalinux.pool.ntp.org.
pool 2.debian.pool.ntp.org iburstsourcedir /run/chrony-dhcpsourcedir /etc/chrony/sources.dkeyfile /etc/chrony/chrony.keysdriftfile /var/lib/chrony/chrony.driftntsdumpdir /var/lib/chronylogdir /var/log/chronymaxupdateskew 100.0rtcsyncmakestep 1 3leapseclist /usr/share/zoneinfo/leap-seconds.listconfdir /etc/chrony/conf.dThe directives to know: pool (the sources), sourcedir (sources added as files, or provided by DHCP in /run/chrony-dhcp), driftfile (the stored drift), ntsdumpdir (the NTS key cache, which avoids renegotiating at every restart), rtcsync (the kernel regularly copies the system time into the hardware clock) and makestep.
Declare your own sources
On Debian and Ubuntu, the clean method is to add a file in /etc/chrony/sources.d/ rather than edit the main file: it survives package updates. The file must end in .sources and only contain server, pool or peer lines, and a simple reload is enough, without restarting the service:
echo 'server ntp.internal.lan iburst' | sudo tee /etc/chrony/sources.d/internal.sourcessudo chronyc reload sourcesOn RHEL and AlmaLinux, which do not have this directory, add the server line to /etc/chrony.conf, then restart:
sudo systemctl restart chronydDeclare at least three sources. With two sources that disagree, chrony cannot tell which one lies; from three onwards, it discards the one that drifts away from the others.
Check the synchronisation
An active chronyd service does not prove the time is right. It can run without any source answering, for instance if port UDP 123 is filtered, and the clock then drifts without a single error message. Two commands provide the proof.
chronyc tracking: the state of the clock
chronyc tracking tells you which source the system is locked on and what gap it measures. Output recorded on AlmaLinux 10, a few seconds after startup:
Reference ID : A29FC801 (time.cloudflare.com)Stratum : 4Ref time (UTC) : Wed Sep 30 14:44:15 2026System time : 0.000812048 seconds slow of NTP timeLast offset : -0.000666413 secondsRMS offset : 0.000666413 secondsFrequency : 0.000 ppm slowResidual freq : -196.961 ppmSkew : 1000000.000 ppmRoot delay : 0.026948174 secondsRoot dispersion : 31.631998062 secondsUpdate interval : 2.0 secondsLeap status : NormalThe lines that matter for diagnosis:
- Reference ID: the source in use. A value of
00000000means no source. - Stratum: that of the source plus one. A stratum 10 usually means a server that fell back on its local clock (
local stratum 10) because no source is reachable. - System time: the current gap, here 0.8 millisecond slow.
- Skew and Root dispersion: the uncertainty. The huge values of this output are normal right after startup, they melt away within minutes.
- Leap status:
Normalwhen all is well,Not synchronisedwhen the clock is locked on nothing.
chronyc sources: the servers queried
chronyc sources lists the sources and tells which one is in use. With the -v option, chrony prints its own legend above the table:
.-- Source mode '^' = server, '=' = peer, '#' = local clock. / .- Source state '*' = current best, '+' = combined, '-' = not combined,| / 'x' = may be in error, '~' = too variable, '?' = unusable.|| .- xxxx [ yyyy ] +/- zzzz|| Reachability register (octal) -. | xxxx = adjusted offset,|| Log2(Polling interval) --. | | yyyy = measured offset,|| \ | | zzzz = estimated error.|| | | \MS Name/IP address Stratum Poll Reach LastRx Last sample===============================================================================^+ time.cloudflare.com 3 6 17 22 +1316us[+1806us] +/- 16ms^* gbg2-ts.nts.netnod.se 1 6 17 22 -1400us[ -911us] +/- 22ms^? ptbtime1.ptb.de 1 7 21 14 +1912us[+1912us] +/- 18msThe first character gives the mode (^ for a server), the second the state, and that is the one to look at:
| Symbol | Meaning |
|---|---|
* | the best source, the one currently driving the clock |
+ | source combined with the best one to refine the computation |
- | selectable source, not combined, nothing abnormal |
x | source chronyd considers wrong (falseticker) |
~ | source whose time is too variable to be used |
? | unusable source: unreachable, or not yet evaluated enough |
The Reach column is an octal register of the last eight requests: 377 means eight answers out of eight, 17 four answers after a recent start, 0 none. Poll is the base-2 logarithm of the interval: 6 means 64 seconds. A healthy clock shows at least one ^* line and a Reach climbing towards 377.
Encrypt the time exchange with NTS
NTS (Network Time Security, RFC 8915) authenticates NTP answers: an attacker on the network can no longer supply a false time without being detected. Classic NTP has no protection at all, and shifting a server's clock is enough to make it accept an expired certificate. Chrony has supported NTS since version 4.0, and the three versions recorded here ship it.
Enabling it takes one option, nts, on servers that offer it. The chrony.conf manual recommends, as soon as you authenticate, to remove unauthenticated sources: in the default mix mode, chrony only uses them if they agree with the NTS sources. So replace the pool with at least three NTS servers:
server time.cloudflare.com iburst ntsserver nts.netnod.se iburst ntsserver ptbtime1.ptb.de iburst ntsOn Debian and Ubuntu, put these lines in a .sources file under /etc/chrony/sources.d/ and comment out the pool line of the main file; on AlmaLinux, replace the pool line of /etc/chrony.conf. Then restart the service.
chronyc authdata proves NTS works. Recorded with the three servers above:
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen=========================================================================time.cloudflare.com NTS 1 30 128 27 0 0 8 64gbg2-ts.nts.netnod.se NTS 1 15 256 27 0 0 8 100ptbtime1.ptb.de NTS 1 15 256 27 0 0 8 100The Cook column counts the cookies received when the session was set up: 8 means the negotiation succeeded. A 0 with an Atmp (attempts) count that keeps rising signals a failed setup, which the chronyd log explains.
Serve time to the local network
A single internal server locked on the Internet, and every machine on the network locked on it: that is the most common architecture. By default, chrony serves time to nobody: you must allow it explicitly with allow, on the internal server.
allow 192.168.10.0/24If you want the server to keep answering when the Internet is down, add local stratum 10: it then serves its own clock, with a high stratum that warns clients of its low quality. Finally open port UDP 123 in the firewall, for that network only, and declare the server on the client side with a server line.
Securing chrony
Chrony opens two separate channels, and most security mistakes come from confusing them. The NTP service (UDP 123) distributes time; the command channel (UDP 323 and a Unix socket) lets chronyc drive the daemon.
| Directive | Channel | Effect |
|---|---|---|
allow / deny | NTP service | who may receive time |
cmdallow / cmddeny | commands | who may drive chronyd remotely |
bindcmdaddress | commands | the listening address for commands |
By default, the command channel only listens on 127.0.0.1 and ::1, which is the right setting: do not open it to the network with cmdallow, or you expose the daemon's administration while believing you opened time. A bindcmdaddress 127.0.0.1 line only makes this default explicit.
Symmetric keys between server and internal clients
To authenticate an internal server that does not offer NTS, chrony accepts symmetric keys, shared between the server and its clients. Generate them with chronyc keygen, which produces a random SHA256 key, never a key typed by hand:
chronyc keygen 1 SHA256 2561 SHA256 HEX:1866637433DF91090E2D5F7E346120C2B803FB091A12BD7464B528194A66CC22Copy this line into the key file on both sides (/etc/chrony/chrony.keys on Debian, a file declared by keyfile on AlmaLinux), then add key 1 to the client's server line. The key file keeps the permissions shipped by Debian: owner root, service group (_chrony), mode 640. Chronyd drops root privileges after startup: recorded on Debian 13, a file in root:root 600 is indeed loaded at launch, but the log shows Missing read access, and a chronyc rekey then fails with Could not open keyfile. A 644 mode, conversely, exposes the keys to every local account.
Troubleshooting
| Symptom | Likely cause | Solution |
|---|---|---|
chronyc sources only shows ^? and a Reach of 0 | Port UDP 123 blocked outbound, or DNS unreachable | Test with chronyc -n sources, open UDP 123 towards the servers |
Reference ID : 00000000 and Leap status : Not synchronised | No usable source | Check the sources, then the firewall |
Stratum : 10 on an internal server | Fell back on local stratum 10, Internet unreachable | Restore access to the external sources |
| The edited configuration has no effect | Wrong file (/etc/chrony.conf on Debian) | systemctl cat chronyd, then edit the right file |
NTS source with Cook 0 in chronyc authdata | ca-certificates missing, or TCP 4460 blocked | Install ca-certificates, open TCP 4460 outbound |
| Internal clients receive no time | No allow, or deny all written after allow | A single allow <network>, without deny all |
| A directive copied from an old tutorial has no effect | Removed directive, such as commandkey, ignored with a mere warning | Read journalctl -u chronyd, remove the reported line |
The service log, journalctl -u chronyd, gives the exact cause of an ignored directive, an unreadable key file or an NTS failure.
Hands-on practice
Nothing replaces hands-on work: this lab has you configure chrony, check the synchronisation with chronyc tracking and set the time zone, all of it persistently on a VM.
Key points
- An active
chronydproves nothing: onlychronyc trackingand a^*line inchronyc sourcesprove the synchronisation. - The configuration lives in
/etc/chrony/chrony.confon Debian and Ubuntu, in/etc/chrony.confon RHEL: editing the wrong file changes nothing. - Installing chrony on Debian or Ubuntu removes
systemd-timesyncd: one time service per machine. - NTS authenticates time with the
ntsoption; it needsca-certificatesand port TCP 4460, and is checked withchronyc authdata. - Chrony serves time to nobody by default: an
allow <network>is enough, and adeny allwritten after it shuts everything.
Knowledge check
Check that the essentials of this guide are acquired. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: HAProxy: load balancing and high availability
A new full quiz with random questions
Work again only on the questions you missed
More information
The project documentation is the reference for every directive, and the NTP site traces the history of the protocol.