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.
| Path | Role |
|---|---|
/etc/chrony/chrony.conf | The main file. sourcedir and confdir pull in the two below |
/etc/chrony/sources.d/*.sources | Source definitions. The default ubuntu-ntp-pools.sources lives here |
/etc/chrony/conf.d/*.conf | Additional 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.
ls /etc/chrony/conf.d/ /etc/chrony/sources.d/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.sourcesOnly 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.
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 chronyserver and pool specify sources differently.
| Directive | Meaning |
|---|---|
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 |
iburst | Send four closely spaced requests at startup. Synchronization starts in seconds instead of minutes |
Here is the state right after adding it.
chronyc sources -v
chronyc trackingkazulog@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.
| Symbol | Meaning |
|---|---|
^ | 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 |
x | Falseticker, disagreeing with the other sources |
Using only the internal server
Rename the default definition files so they are no longer read.
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 chronyubuntu-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.
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 : NormalReference ID is now the internal server. The main fields of chronyc tracking:
| Field | Meaning |
|---|---|
Reference ID | The source. The hexadecimal form is the IPv4 address itself (C0A86401 = 192.168.100.1) |
Stratum | Your own level, one more than the source’s |
System time | The difference between the system clock and NTP time. Smaller is better |
Last offset / RMS offset | The most recent correction and its root mean square |
Frequency | How far the clock rate is off, in ppm. chrony keeps compensating for this |
Leap status | Normal 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.
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.
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: noCorrecting 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.
makestep 1 3It 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.
sudo date -s '+5 minutes'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] +/- 6109uschrony 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.
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 : NormalSystem 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.
sudo chronyc makestepkazulog@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 secondsThe clock went back from 16:39:04 to 16:34:17 and System time is zero. journalctl records the correction as well.
sudo journalctl -u chrony --since "-15 min" --no-pagerkazulog@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 secondsThe 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.
echo 'allow 192.168.100.0/24' | sudo tee -a /etc/chrony/conf.d/kazulog-ntp.conf
sudo systemctl restart chronykazulog@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.
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 : 5The 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.
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 chronykazulog@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.
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.
| Step | Session log | Configuration | syslog |
|---|---|---|---|
| Initial state (default sources) | show | conf | log |
| Adding the internal NTP server | show | conf | log |
| Disabling the default sources | show | conf | log |
| Moving the clock by five minutes | show | conf | log |
| After the error is detected | show | conf | log |
| Correcting with makestep | show | conf | log |
| Confirming synchronization is back | show | conf | log |
| Making sv1 an NTP server | show | conf | log |
| Synchronizing sv2 against sv1 | show | conf | log |
| Restoring the defaults (final state) | show | conf | log |
Records from R1, which served as the NTP server. The running-config is the configuration under test.
| R1 | show output | syslog | running-config |
|---|---|---|---|
| Acting as the NTP server | show | log | run |
Reference
Related articles
- Changing the Hostname on Ubuntu 26.04 LTS Server (hostnamectl)
- Updating Packages on Ubuntu 26.04 LTS Server (apt update / upgrade)
- Setting the Timezone and Time Synchronisation on Ubuntu 26.04 LTS Server
- Creating Users and Granting sudo Privileges on Ubuntu 26.04 LTS Server
- Configuring the SSH Server on Ubuntu 26.04 LTS Server
- Managing Services with systemctl and Reading Logs with journalctl on Ubuntu 26.04 LTS Server
- Configuring Automatic Updates on Ubuntu 26.04 LTS Server (unattended-upgrades)
- Automating the Initial Setup of Ubuntu 26.04 LTS Server with cloud-init
- Configuring Kernel Parameters on Ubuntu 26.04 LTS Server (sysctl)
- Configuring the Network on Ubuntu 26.04 LTS Server (Netplan)
- Configuring Name Resolution on Ubuntu 26.04 LTS Server (systemd-resolved)
- Changing the NTP Source on Ubuntu 26.04 LTS Server (chrony)
- Static Routes on Ubuntu 26.04 LTS Server (Netplan)
- How to Install the Latest neovim from the Official Site on Ubuntu 26.04 LTS Server