Skip to main content
  1. Linux Articles/
  2. Ubuntu 26.04 LTS Server/

Changing the NTP Source on Ubuntu 26.04 LTS Server (chrony)

Table of Contents

Changing the NTP server used for time synchronization

Time synchronization on Ubuntu 26.04 LTS Server is handled by chrony. By default it synchronizes with the NTP servers operated by Canonical (ntp.ubuntu.com), but in an environment with an internal NTP server you need to point it there instead.

This article covers changing the source, reading the synchronization status, correcting a clock that has drifted, and turning the server itself into an NTP server. If you only need to confirm that chrony is running, see Time Zone Settings and Time Synchronization.

How the chrony configuration files are organized

The configuration is not a single /etc/chrony/chrony.conf file; it is split across directories.

PathRole
/etc/chrony/chrony.confThe main file. sourcedir and confdir pull in the two below
/etc/chrony/sources.d/*.sourcesSource definitions. The default ubuntu-ntp-pools.sources lives here
/etc/chrony/conf.d/*.confAdditional settings. This is where you add your own file
/run/chrony-dhcp/NTP servers learned over DHCP, pulled in by sourcedir

Do not edit the main chrony.conf. Dropping a single file into conf.d/ is the convention on Ubuntu. Your settings then survive a package upgrade that replaces the main file.

Command to list the configuration files
ls /etc/chrony/conf.d/ /etc/chrony/sources.d/
Listing the configuration files - example
kazulog@sv1:~$ ls /etc/chrony/conf.d/ /etc/chrony/sources.d/
/etc/chrony/conf.d/:
README  kazulog-ntp.conf  ubuntu-nts.conf

/etc/chrony/sources.d/:
README  ubuntu-ntp-pools.sources

Only files ending in .conf or .sources are read. To stop a default definition, rename it rather than deleting it.

Adding an internal NTP server

Create a file under conf.d/ and restart chrony.

Command to add a source
sudo tee /etc/chrony/conf.d/kazulog-ntp.conf <<'EOF'
# Use the internal NTP server (R1)
server 192.168.100.1 iburst
EOF
sudo systemctl restart chrony

server and pool specify sources differently.

DirectiveMeaning
server <address>Use that single address as a source. Use this for an internal server
pool <name>Use the several addresses the name resolves to. maxsources caps how many
iburstSend four closely spaced requests at startup. Synchronization starts in seconds instead of minutes

Here is the state right after adding it.

Commands to check the sources
chronyc sources -v
chronyc tracking
Right after adding the internal server - example
kazulog@sv1:~$ chronyc sources -v | tail -8
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^? 192.168.100.1                 3   6    17    34  +1487ms[+1493ms] +/- 1942ms
^+ ntp-nts-2.ps5.canonical.>     2   6    17    39    +11ms[  +17ms] +/-  118ms
^* ntp-nts-3.ps5.canonical.>     2   6    17    38    +14ms[  +21ms] +/-  119ms
^- ntp-nts-2.ps6.canonical.>     2   6    17    38    -41ms[  -41ms] +/-  172ms
^+ ntp-nts-3.ps6.canonical.>     2   6    17    38    -40ms[  -34ms] +/-  168ms
^- ntp-nts-1.ps5.canonical.>     2   6    17    39    +14ms[  +20ms] +/-  120ms
kazulog@sv1:~$ chronyc tracking
Reference ID    : B97DBE7B (ntp-nts-3.ps5.canonical.com)

Adding a source does not switch the synchronization over. The five defaults are still there, so chrony picks the best of them by majority and accuracy. The leading ^? means “not usable yet”.

The first two characters of each chronyc sources line read as follows.

SymbolMeaning
^Server (= is a peer, # is a local clock)
*The source currently synchronized to
+An acceptable source. Several are combined
-Excluded from the combination
?Unusable. Either unreachable, or not enough information about its accuracy
xFalseticker, disagreeing with the other sources

Using only the internal server

Rename the default definition files so they are no longer read.

Commands to disable the default sources
sudo mv /etc/chrony/sources.d/ubuntu-ntp-pools.sources /etc/chrony/sources.d/ubuntu-ntp-pools.sources.disabled
sudo mv /etc/chrony/conf.d/ubuntu-nts.conf /etc/chrony/conf.d/ubuntu-nts.conf.disabled
sudo systemctl restart chrony

ubuntu-nts.conf points at the certificates used for NTS (Network Time Security). If you are not using Canonical’s servers you can disable it as well.

Only the internal server remains - example
kazulog@sv1:~$ ls /etc/chrony/conf.d/ /etc/chrony/sources.d/
/etc/chrony/conf.d/:
README  kazulog-ntp.conf  ubuntu-nts.conf.disabled

/etc/chrony/sources.d/:
README  ubuntu-ntp-pools.sources.disabled
kazulog@sv1:~$ chronyc sources -v | tail -3
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 192.168.100.1                 3   6    17    39    +75us[ +264us] +/-   68ms
kazulog@sv1:~$ chronyc tracking
Reference ID    : C0A86401 (192.168.100.1)
Stratum         : 4
Ref time (UTC)  : Fri Sep 11 07:27:54 2026
System time     : 0.000000200 seconds slow of NTP time
Last offset     : +0.000188777 seconds
RMS offset      : 0.000188777 seconds
Frequency       : 5.242 ppm fast
Residual freq   : -26.357 ppm
Skew            : 8.059 ppm
Root delay      : 0.004248492 seconds
Root dispersion : 0.067712799 seconds
Update interval : 2.0 seconds
Leap status     : Normal

Reference ID is now the internal server. The main fields of chronyc tracking:

FieldMeaning
Reference IDThe source. The hexadecimal form is the IPv4 address itself (C0A86401 = 192.168.100.1)
StratumYour own level, one more than the source’s
System timeThe difference between the system clock and NTP time. Smaller is better
Last offset / RMS offsetThe most recent correction and its root mean square
FrequencyHow far the clock rate is off, in ppm. chrony keeps compensating for this
Leap statusNormal is healthy. Not synchronised means synchronization is lost

Details about the source are available with chronyc ntpdata. This command requires sudo, unlike tracking, sources and sourcestats.

Details of the source - example
kazulog@sv1:~$ sudo chronyc ntpdata 192.168.100.1 | head -12
Remote address  : 192.168.100.1 (C0A86401)
Remote port     : 123
Local address   : 192.168.100.10 (C0A8640A)
Leap status     : Normal
Version         : 4
Mode            : Server
Stratum         : 3
Poll interval   : 6 (64 seconds)
Precision       : -10 (0.000976562 seconds)
Root delay      : 0.000000 seconds
Root dispersion : 0.002197 seconds
Reference ID    : 7F7F0101 ()

System clock synchronized in timedatectl turns to yes as well.

Checking with timedatectl - example
kazulog@sv1:~$ timedatectl
               Local time: Fri 2026-09-11 16:35:40 JST
           Universal time: Fri 2026-09-11 07:35:40 UTC
                 RTC time: Fri 2026-09-11 07:35:40
                Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Correcting a clock that is badly off

chrony normally corrects the clock by speeding it up or slowing it down (slewing), because jumping the time breaks the ordering of log entries. Stepping the clock all at once is limited by a setting in the main file.

The relevant line in chrony.conf
makestep 1 3

It means step the clock if the error exceeds 1 second, but only for the first 3 updates after startup. After that, chrony will not step on its own.

Let us move the clock forward by five minutes on purpose.

Command to move the clock
sudo date -s '+5 minutes'
Right after moving the clock - example
kazulog@sv1:~$ date
Fri Sep 11 16:29:38 JST 2026
kazulog@sv1:~$ sudo date -s '+5 minutes'
kazulog@sv1:~$ chronyc tracking
Reference ID    : 00000000 ()
Stratum         : 0
Ref time (UTC)  : Thu Jan 01 00:00:00 1970
System time     : 0.000000000 seconds fast of NTP time
Last offset     : -0.000318293 seconds
RMS offset      : 0.000318293 seconds
Frequency       : 5.095 ppm fast
Residual freq   : +0.000 ppm
Skew            : 0.000 ppm
Root delay      : 1.000000000 seconds
Root dispersion : 1.000000000 seconds
Update interval : 65.2 seconds
Leap status     : Not synchronised
kazulog@sv1:~$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^? 192.168.100.1                 3   6    77    14  +300.0s[+300.0s] +/- 6109us

chrony detected the jump, dropped out of synchronization, and Leap status became Not synchronised. The source shows the 300-second error.

After a while the source is selected again, but the error is still there.

After the error is detected - example
kazulog@sv1:~$ date
Fri Sep 11 16:38:40 JST 2026
kazulog@sv1:~$ chronyc tracking
Reference ID    : C0A86401 (192.168.100.1)
Stratum         : 4
Ref time (UTC)  : Fri Sep 11 07:33:24 2026
System time     : 292.156341553 seconds fast of NTP time
Last offset     : +0.000182769 seconds
RMS offset      : 89.999153137 seconds
Frequency       : 4.617 ppm fast
Residual freq   : -5.942 ppm
Skew            : 16.834 ppm
Root delay      : 0.004045914 seconds
Root dispersion : 0.004102858 seconds
Update interval : 65.0 seconds
Leap status     : Normal

System time is still 292 seconds off. The three steps allowed by makestep 1 3 have been used up, so chrony tries to close the gap by slewing. The maximum slew rate defaults to one twelfth of real time (maxslewrate 83333.333), so removing 292 seconds takes close to an hour. The measurement above bears that out: about a minute passed and the error only fell from 297.7 to 292.2 seconds.

This is what chronyc makestep is for. It makes the next update a step instead of a slew.

Command to correct the clock immediately
sudo chronyc makestep
Correction by makestep - example
kazulog@sv1:~$ date
Fri Sep 11 16:39:04 JST 2026
kazulog@sv1:~$ sudo chronyc makestep
200 OK
kazulog@sv1:~$ date
Fri Sep 11 16:34:17 JST 2026
kazulog@sv1:~$ chronyc tracking
Reference ID    : C0A86401 (192.168.100.1)
Stratum         : 4
Ref time (UTC)  : Fri Sep 11 07:33:24 2026
System time     : 0.000000000 seconds fast of NTP time
Last offset     : +0.000182769 seconds
RMS offset      : 89.999153137 seconds

The clock went back from 16:39:04 to 16:34:17 and System time is zero. journalctl records the correction as well.

Command to check the chrony log
sudo journalctl -u chrony --since "-15 min" --no-pager
The chrony log - example
kazulog@sv1:~$ sudo journalctl -u chrony --since "-15 min" --no-pager | tail -8
Sep 11 16:27:52 sv1 chronyd[3650]: System clock was stepped by -1.485292 seconds
Sep 11 16:27:52 sv1 chronyd[3650]: System clock TAI offset set to 37 seconds
Sep 11 16:34:58 sv1 chronyd[3650]: Forward time jump detected!
Sep 11 16:34:58 sv1 chronyd[3650]: Can't synchronise: no selectable sources (0 unreachable sources)
Sep 11 16:35:24 sv1 chronyd[3650]: System clock was stepped by -0.000000 seconds
Sep 11 16:37:13 sv1 chronyd[3650]: Selected source 192.168.100.1
Sep 11 16:37:13 sv1 chronyd[3650]: System clock wrong by -299.997180 seconds
Sep 11 16:34:14 sv1 chronyd[3650]: System clock was stepped by -289.988321 seconds

The last line is the correction made by makestep. Because the clock went backwards, the order of the lines no longer matches their timestamps (16:37:13 is followed by 16:34:14). Check the impact before doing this on a production server.

chronyc makestep applies the correction at the next poll. Running it while no source is selected does nothing, because the error is unknown. That is the stepped by -0.000000 seconds line in the log above.

Acting as an NTP server

chrony is both a client and a server. All you add is allow, which says which clients may query it.

Command to serve NTP
echo 'allow 192.168.100.0/24' | sudo tee -a /etc/chrony/conf.d/kazulog-ntp.conf
sudo systemctl restart chrony
Checking the NTP server - example
kazulog@sv1:~$ cat /etc/chrony/conf.d/kazulog-ntp.conf
# Use the internal NTP server (R1)
server 192.168.100.1 iburst
allow 192.168.100.0/24
kazulog@sv1:~$ ss -ulpn | grep 123
UNCONN 0      0            0.0.0.0:123       0.0.0.0:*
kazulog@sv1:~$ sudo chronyc clients
Hostname                      NTP   Drop Int IntL Last     Cmd   Drop Int  Last
===============================================================================
192.168.100.20                  6      0   1   -    52       0      0   -     -

With allow in place, chrony listens on 123/udp. chronyc clients lists the hosts that have queried it, and the NTP column is the number of packets received.

The client side (sv2) is configured the same way.

Checking the client side - example
kazulog@sv2:~$ cat /etc/chrony/conf.d/kazulog-ntp.conf
# Use the internal NTP server (sv1)
server 192.168.100.10 iburst
kazulog@sv2:~$ chronyc sources -v | tail -3
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 192.168.100.10                4   6    37     0   +639ns[-1262us] +/- 6986us
kazulog@sv2:~$ chronyc tracking
Reference ID    : C0A8640A (192.168.100.10)
Stratum         : 5

The level drops by one at each hop: the router at stratum 3, sv1 at stratum 4, sv2 at stratum 5.

Restoring the defaults

Delete the file you added and rename the disabled files back.

Commands to restore the defaults
sudo rm /etc/chrony/conf.d/kazulog-ntp.conf
sudo mv /etc/chrony/sources.d/ubuntu-ntp-pools.sources.disabled /etc/chrony/sources.d/ubuntu-ntp-pools.sources
sudo mv /etc/chrony/conf.d/ubuntu-nts.conf.disabled /etc/chrony/conf.d/ubuntu-nts.conf
sudo systemctl restart chrony
Back to the defaults - example
kazulog@sv1:~$ chronyc sources -v | tail -7
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^+ ntp-nts-2.ps5.canonical.>     2   6    17    36    +13ms[  +28ms] +/-  119ms
^+ ntp-nts-3.ps5.canonical.>     2   6    17    36    +13ms[  +28ms] +/-  120ms
^- ntp-nts-2.ps6.canonical.>     2   6    17    35    -42ms[  -42ms] +/-  173ms
^* ntp-nts-3.ps6.canonical.>     2   6    17    35    -26ms[  -11ms] +/-  154ms
^- ntp-nts-1.ps5.canonical.>     2   6    17    35    +11ms[  +11ms] +/-  116ms
kazulog@sv1:~$ chronyc tracking | head -3
Reference ID    : 5BBD5B71 (ntp-nts-3.ps6.canonical.com)

Test environment and session logs

The examples were captured on CML with two Ubuntu 26.04 LTS Servers (sv1 and sv2) and a Cisco router (R1), synchronizing for real against R1 as the NTP server.

Test topology
  sv1 (Ubuntu 26.04)      sv2 (Ubuntu 26.04)      R1 (IOS XE 17.03.08a)
   ens3 192.168.100.10     ens3 192.168.100.20      Gi2 192.168.100.1/24
   stratum 4               stratum 5                ntp master 3 (stratum 3)
   allow 192.168.100.0/24
      |                       |                       |
      +-----------------------+------- LAB-SW --------+

On R1, ntp master 3 alone makes it an NTP server based on its local clock.

StepSession logConfigurationsyslog
Initial state (default sources)showconflog
Adding the internal NTP servershowconflog
Disabling the default sourcesshowconflog
Moving the clock by five minutesshowconflog
After the error is detectedshowconflog
Correcting with makestepshowconflog
Confirming synchronization is backshowconflog
Making sv1 an NTP servershowconflog
Synchronizing sv2 against sv1showconflog
Restoring the defaults (final state)showconflog

Records from R1, which served as the NTP server. The running-config is the configuration under test.

R1show outputsyslogrunning-config
Acting as the NTP servershowlogrun

Reference

chrony documentation

Related articles

Ubuntu official pages