Skip to content

NTP Server and chronyd Configuration

Timezone and Locale covered changing the timezone with timedatectl and reading its System clock synchronized/NTP service fields — but that article didn't go into which service is actually synchronizing the clock, or how to configure it. On modern Ubuntu/Debian and RHEL servers, this job is normally handled by chrony (the chronyd service). This article covers how chronyd works internally, how to configure chrony.conf, and how to turn a server into an NTP source for other hosts.

Why Network Time Synchronization Matters

Every computer has its own hardware clock (RTC, real-time clock), but it gradually drifts away from true time (clock drift). Across multiple servers, this drift causes real problems:

  • TLS certificates — a certificate's validity window is checked against the server's own clock; a wrong clock can make a still-valid certificate get rejected as "expired" or "not yet valid";
  • Kerberos authentication — the protocol itself relies on timestamps; if the difference exceeds the allowed tolerance (usually a few minutes), authentication is rejected;
  • Correlating distributed logs — comparing the order of events across logs collected from multiple servers becomes practically impossible if their clocks aren't in sync.

That's why almost every production Linux server keeps its clock synchronized continuously against an external network source.

chronyd versus the Older ntpd

On the RHEL 8+ family, chrony is the default time service out of the box. On Ubuntu/Debian the default is actually systemd-timesyncd — a lightweight SNTP client that keeps the clock synced but cannot serve time to other hosts — and you install chrony when you need a full NTP implementation: better accuracy, richer diagnostics (chronyc), or the ability to act as an NTP server for other machines (the internal NTP server role below). The older ntpd (the classic NTP project) still shows up on some systems but isn't recommended for new installs. Where chrony's capabilities go beyond timesyncd:

  • chronyd settles faster and more reliably after an interrupted or slow network connection — a laptop, or a virtual machine coming back from pause/resume, for instance;
  • chronyd handles time better on a server that never has a constant internet connection — an isolated internal network with only occasional connectivity, for example;
  • both use the same underlying NTP protocol, so they interoperate — chronyd can get its time from an ntpd running on another server, and vice versa.

Checking which service is running:

systemctl status chronyd
timedatectl | grep 'NTP service'

The service name is sometimes chrony on Ubuntu and chronyd on the RHEL family — check the exact name with systemctl list-units '*chron*'.

Note

On a stock Ubuntu Server that no one has changed, the running time service is usually systemd-timesyncd, not chrony — so systemctl status chronyd returns "Unit chronyd.service could not be found" until chrony is installed with sudo apt install chrony. Installing chrony automatically disables systemd-timesyncd; the two are mutually exclusive, and running both at once would have them fight over the same clock.

chronyc: Checking Current Synchronization Status

chronyc is the command-line tool for talking to the chronyd service.

chronyc tracking

chronyc tracking
Reference ID    : C0248F62 (ntp1.example.com)
Stratum         : 3
Ref time (UTC)  : Fri Jul 25 09:58:10 2026
System time     : 0.000012340 seconds fast of NTP time
Last offset     : +0.000015600 seconds
RMS offset      : 0.000045000 seconds
Frequency       : 3.256 ppm fast
Residual freq   : +0.002 ppm
Skew            : 0.045 ppm
Root delay      : 0.023456789 seconds
Root dispersion : 0.001234567 seconds
Update interval : 64.2 seconds
Leap status     : Normal

The fields that matter:

  • Stratum — how many "hops" this server is from the reference source: stratum 1 is a server directly connected to a hardware time source (an atomic clock or GPS), stratum 2 is a server getting time from a stratum 1 source, and so on; a higher number means a more distant, and potentially less accurate, source;
  • System time — how far the current system clock differs from the "true" time computed via NTP;
  • Leap status: Normal — no leap second or other special condition; synchronization is stable. Not synchronised means the system hasn't yet locked onto any source.

chronyc sources

chronyc sources -v
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp1.example.com              2   6   377    23   +120us[ +135us] +/-  15ms
^+ ntp2.example.com              2   6   377    25   +310us[ +298us] +/-  18ms
^- 192.168.1.1                   3   6   177    41   -2.1ms[ -2.3ms] +/- 120ms

What the columns mean:

  • ^* — the server currently selected as the primary source (there is only ever one);
  • ^+ — a reliable secondary source, factored into the combined calculation;
  • ^- — a source not currently contributing to the calculation, typically because its reported time differs significantly from the others;
  • Reach — the response history of recent polls, shown as an octal bitmask; 377 means all of the last eight polls got a response;
  • Last sample — the most recent measurement from that source, along with its estimated error margin.

chronyc sourcestats shows stability statistics for each source over time — frequency drift, for example — useful for assessing long-term quality.

chrony.conf: Client Configuration

The main configuration file lives at /etc/chrony/chrony.conf on Ubuntu/Debian, and typically at /etc/chrony.conf on the RHEL family.

pool ntp.ubuntu.com iburst maxsources 4
driftfile /var/lib/chrony/chrony.drift
makestep 1.0 3
rtcsync

The key directives:

  • pool/server — the time source; pool points at a DNS pool made up of multiple servers (pool.ntp.org, for instance), while server points at exactly one specific server;
  • iburst — sends several requests in quick succession to speed up initial synchronization;
  • driftfile — the file where the hardware clock's estimated drift rate is stored; on restart, chronyd starts from this value instead of from zero;
  • makestep 1.0 3 — if the time difference exceeds 1.0 second, the first three measurements are allowed to correct the clock by "stepping" it immediately rather than adjusting it slowly; this is normally only needed at startup, since slowly correcting a large difference takes a long time;
  • rtcsync — enables periodically writing the system clock back to the hardware clock (RTC).

Reloading the service after a configuration change:

sudo systemctl restart chronyd
chronyc tracking

Configuring the Server as an Internal NTP Source

On a network with many servers, a common practice is to have one server pull time from the internet and redistribute it to the other internal hosts as an internal NTP server, instead of exposing every host to the internet directly — this reduces external traffic and ensures all internal hosts rely on the same source.

The allow directive is added to chrony.conf:

pool ntp.ubuntu.com iburst

allow 10.20.0.0/24

allow determines which network's NTP requests get served. By default, chronyd acts only as a client and doesn't serve other hosts; without allow, external requests are rejected.

Warning

Writing an overly broad allow directive — allow 0.0.0.0/0, for instance — turns the server into an open NTP server that serves time to any external host. This wastes resources, and in some cases can be abused as an amplification vector in NTP-based network attacks. Always scope allow to exactly the internal network that needs it.

Reloading the service and checking the result:

sudo systemctl restart chronyd
chronyc clients

chronyc clients shows the list of clients currently requesting time from this server — the most direct way to confirm the internal NTP server is actually working.

Firewall

NTP runs on UDP 123 by default. Before exposing a server as an internal NTP source, confirm that port is only open to the intended internal network — this is covered in more depth in Firewall and ufw.

sudo ufw status
sudo ufw allow from 10.20.0.0/24 to any port 123 proto udp

Danger

Changing a firewall rule changes how network services appear to other hosts. Save the current ufw status numbered output before adding a rule, and write a rule scoped exactly to the required address and port — avoid broad allow rules.

Practical Scenario: Fixing "System clock synchronized: no"

Problem: timedatectl reports System clock synchronized: no, and TLS connections have started failing with certificate errors.

Step 1 — check:

timedatectl
systemctl status chronyd
chronyc tracking

Step 2 — build a hypothesis: if chronyd is inactive/failed, the service isn't running at all; if the service is active but chronyc sources shows no ^* entry, it can't reach any source — a network or firewall issue, for instance.

Step 3 — check the service (if it's not running):

sudo systemctl restart chronyd
journalctl -u chronyd --since '10 min ago'

Step 4 — if sources exist but aren't responding, check connectivity:

chronyc -a 'burst 4/4'
dig +short ntp.ubuntu.com
sudo ss -unlp | grep 123

ss -unlp confirms chronyd is actually listening on UDP port 123 — a practical application of the ss tool covered in more depth in Network Diagnostics.

Step 5 — result: once chronyc tracking shows Leap status: Normal and chronyc sources shows at least one ^* entry, synchronization has been restored. Recheck timedatectl:

timedatectl | grep -E 'synchronized|NTP service'

Step 6 — documentation: record why the service stopped (it may not have started automatically after a previous reboot, for instance) and what was done to fix it permanently — enabling it with systemctl enable chronyd, for example.

Common Mistakes

Not Noticing Only ^- Entries Appear in chronyc sources

If no source shows ^*, the system hasn't fully committed to trusting any server yet — often a sign of a network or firewall block, a dead source server, or too large a time discrepancy.

Assuming a Server Is an "Internal NTP Server" Without Checking allow

Without allow written in chrony.conf, the service remains client-only and serves no other hosts. Confirm with chronyc clients that requests are actually arriving.

Expecting a Large Time Difference to Correct Slowly Without makestep

If makestep is absent or too narrow, a large initial discrepancy is corrected slowly, step by step — this can make time appear to move incorrectly for some applications. In normal use, makestep with a limited count is meant only for the one-time correction at startup.

Confusing Timezone Configuration with NTP Synchronization

An incorrect timezone doesn't mean the clock isn't synchronized — these are two independent settings. Check Leap status and System time in chronyc tracking first, then check timezone configuration separately.

Interview angle

A question worth being ready for: "the server's Stratum value just jumped from 2 to 10 — what does that mean, and is it a problem?" A stratum in the high single digits or above usually means chronyd has lost contact with its usual upstream sources and fallen back to a much lower-quality reference, or is running on its own local clock without external correction — Leap status: Not synchronised often accompanies this. It's a strong signal to check connectivity to the configured pool/server entries before anything downstream (TLS, Kerberos, log ordering) starts failing.

Exercises

  1. Compare the output of chronyc tracking and chronyc sources -v, and write in your own words what Stratum, Leap status, and the ^* marker mean.
  2. Explain why each of the pool, iburst, makestep, and driftfile directives in chrony.conf is needed.
  3. On an isolated lab network, configure one VM as an internal NTP server (restricted with allow), connect to it from a second VM, and confirm the connection with chronyc sources.
  4. Repeat the practical scenario above: deliberately stop the chronyd service, observe the change in timedatectl, then restore the service and wait until synchronization is confirmed again.

Verification criterion: for exercise 4, your final check must show Leap status: Normal in chronyc tracking and at least one ^* entry in chronyc sources — a service simply being active in systemctl status is not sufficient proof that synchronization actually recovered.

References

  • Chrony — official documentation: https://chrony-project.org/documentation.html
  • man7.org: chronyd(8): https://man7.org/linux/man-pages/man8/chronyd.8.html
  • man7.org: chronyc(1): https://man7.org/linux/man-pages/man1/chronyc.1.html
  • man7.org: chrony.conf(5): https://man7.org/linux/man-pages/man5/chrony.conf.5.html
  • freedesktop.org: timedatectl(1): https://man7.org/linux/man-pages/man1/timedatectl.1.html
  • Ubuntu Server — Time synchronisation: https://ubuntu.com/server/docs/network-ntp
  • Red Hat Enterprise Linux 9 — Using chrony to configure NTP: https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/configuring_basic_system_settings/using-chrony-to-configure-ntp_configuring-basic-system-settings