1. Overview

Linux applications require accurate and synchronized time to run properly. We can think of services such as high availability clusters, databases, etc. Therefore we need to synchronize the clocks of our systems using a reliable time source. To achieve that, we rely on the Network Time Protocol (NTP).

In this tutorial, we will learn how to configure and synchronize our system time using NTP on RHEL-based systems.

2. Server Time and Time Zones

On the Linux system, we have the hardware and the system clocks. The hardware clock also is known as the real-time clock (RTC).  It runs independently from the kernel even while the server is shut down. Whereas the system clock is maintained by the OS.

During Linux boot time, the hardware clock updates the system clock. But the hardware clock is not reliable enough. For that reason, we use NTP to synchronize our system time from an external and more accurate time source.

Another key point to mention is that NTP is based on the UTC time zone. Hence we need to configure our system time zone appropriately.

First of all, let’s display with timedatectl our time information:

$ timedatectl
      Local time: Fri 2022-04-29 14:24:42 EDT
  Universal time: Fri 2022-04-29 18:24:42 UTC
        RTC time: Fri 2022-04-29 18:24:42
       Time zone: America/Toronto (EDT, -0400)
    NTP enabled: no
NTP synchronized: no
 RTC in local TZ: no
      DST active: yes
 Next DST change: DST ends (the clock jumps one hour backwards) at
                  Sun 2022-11-06 01:59:59 EDT
                  Sun 2022-11-06 01:00:00 EST

Then we can list all the available time zones using the command timedatectl list-timezones:

$ timedatectl list-timezones | grep "^Europe/"

We use the grep command to match all the lines starting with Europe/.

Finally, as a superuser, let’s set our local time zone and print the timedatectl output:

# timedatectl set-timezone Europe/Athens; timedatectl 
      Local time: Fri 2022-04-29 21:43:21 EEST
  Universal time: Fri 2022-04-29 18:43:21 UTC
        RTC time: Fri 2022-04-29 18:43:21
       Time zone: Europe/Athens (EEST, +0300)

We notice that our Local time is now in the correct Time zone, while the default RTC time is in UTC.

3. Chrony Installation

The chrony suite provides NTP time synchronization. This package does not require regular polling of the NTP server. Therefore it can be suitable for environments with intermittent network or internet connections.

If the chrony package is not already installed, as the root user, we can run:

# dnf install chrony

Next, we will see how to configure chrony to synchronize our system time.

4. Chrony as NTP Client

4.1. Configuration

The chrony configuration file is /etc/chrony.conf. Let’s display the predefined NTP servers’ addresses:

$ grep ^server /etc/chrony.conf
server 0.gr.pool.ntp.org
server 1.gr.pool.ntp.org
server 2.gr.pool.ntp.org
server 3.gr.pool.ntp.org

Since our system “host1” has access to the internet, we can choose from the available public NTP pools from www.pool.ntp.org.

To illustrate, let’s replace the NTP servers with the ones of our choice based on our preferred time zone:

$ grep "server " /etc/chrony.conf
#server 0.centos.pool.ntp.org iburst
#server 1.centos.pool.ntp.org iburst
#server 2.centos.pool.ntp.org iburst
#server 3.centos.pool.ntp.org iburst
server 0.gr.pool.ntp.org iburst
server 1.gr.pool.ntp.org iburst
server 2.gr.pool.ntp.org iburst
server 3.gr.pool.ntp.org iburst

Using our text editor, we comment out the default NTP servers’ lines by inserting the # sign at the beginning. Then we add the new NTP addresses.

Note that we keep the iburst default option for quicker initial synchronization.

Next, let’s start and enable the service:

# systemctl start chronyd
# systemctl enable chronyd

In the above, systemctl with the enable option configures chronyd to start automatically after the server reboot.

Finally, let’s check the status of the chronyd service:

$ systemctl status chronyd
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2022-04-29 21:44:52 EEST; 26s ago

Now that chronyd is running, let’s explore how we can monitor our time synchronization.

4.2. Manage Time Synchronization

We can use the chronyc command to check the status of our NTP. Let’s run the command with the sources option and the -v flag for more verbose output:

$ chronyc sources -v
210 Number of sources = 4
  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current synced, '+' = combined , '-' = not combined,
| /   '?' = unreachable, 'x' = time may be in error, '~' = time too variable.
||                                                 .- 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               
^- ipa225.1.tellas.gr            1   9   377   128    +65ms[  +65ms] +/-  560ms
^* nero.grnet.gr                 1   9   377   397   -448us[ -298us] +/-   56ms
^+ ipa95.24.tellas.gr            3   8   377   131  +3252us[+3252us] +/-   90ms
^+ ns4.asda.gr                   2   9   377     8   -642us[ -642us] +/-   87ms

Let’s understand the meaning of two important columns:

The S column gives us details about the state of the NTP source. The asterisk * means that our system is currently synchronized with nero.grnet.gr. Whereas the plus sign + indicates that two other servers can also be considered for synchronization.

The Stratum column takes a value in the range of 1 to 15. Time sources with lower values have higher accuracy.

We have seen that “host1” is syncing time with an NTP server on the internet. Let’s see next how we can enable NTP for another node which we will call “host2”.

5. Configuring the NTP Server

Unlike “host1”, “host2” can’t connect to the internet. To overcome the firewall restriction, we can provide time to “host2” on the local network. To demonstrate we set up chrony as an NTP server on “host1”.

From “host1”, let’s run the command firewall-cmd with the option –list-services:

# firewall-cmd --list-services
dhcpv6-client ftp ssh tftp

From the above, we see that the firewall does not allow the incoming NTP packets on the UDP port 123.

Therefore let’s run the firewall-cmd command with the options —permanent and —add-service:

# firewall-cmd --permanent --add-service ntp

We see that we have successfully added the rule and it is now persistent upon reboot.

Afterward, we load the new firewall rule with the reload option to make it active:

# firewall-cmd --reload

Following, we allow access to the NTP server from our local subnet:

$ grep ^allow /etc/chrony.conf

Finally, let’s restart the chronyd service:

# systemctl restart chronyd

From “host2”, we update the server directive in the /etc/chrony.conf file:

$ grep ^server /etc/chrony.conf
server host1

Note that we use host1 as our timeserver. We map host1 to its IP address using the /etc/hosts file.

Lastly, to test our configuration, let’s pass the -d  and -Q flags to the chronyd command. We use the -Q option to query the NTP server without adjusting the system clock:

$ chronyd -d -d -Q "server host1 iburst" 
2022-04-29T22:21:13Z main.c:515:(main) chronyd version 3.4 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASYNCDNS +SECHASH +IPV6 +DEBUG)
2022-04-29T22:21:13Z ntp_sources.c:479:(resolve_sources) resolving host1
2022-04-29T22:21:13Z ntp_sources.c:407:(name_resolve_handler) host1 resolved to 1 addrs
2022-04-29T22:21:13Z ntp_sources.c:380:(process_resolved_name) (1)
2022-04-29T22:21:13Z ntp_core.c:1176:(transmit_timeout) Transmit timeout for []
2022-04-29T22:21:13Z ntp_io.c:896:(NIO_SendPacket) Sent 48 bytes to from [UNSPEC] fd 4
2022-04-29T22:21:13Z ntp_io.c:698:(process_message) Received 48 bytes from to fd=4 if=2 tss=1 delay=0.000089723
2022-04-29T22:21:15Z ntp_core.c:1176:(transmit_timeout) Transmit timeout for []
2022-04-29T22:21:15Z ntp_io.c:896:(NIO_SendPacket) Sent 48 bytes to from [UNSPEC] fd 4
2022-04-29T22:21:15Z ntp_io.c:698:(process_message) Received 48 bytes from to fd=4 if=2 tss=1 delay=0.000085051
2022-04-29T22:21:17Z reference.c:905:(special_mode_sync) System clock wrong by 194.827846 seconds (ignored)

In the above command, we use the -d option twice to print the debugging messages to the terminal. Also, we specify the NTP server address as host1.

From the generated messages, we notice that the NTP server is replying to the client’s requests. However, the system time on the client deviates from the NTP server time. The offset is around 194 seconds.

Let’s see next how we can manually synchronize the time on “host2” with the NTP server on “host1”.

6. Updating Time Manually

We can set the system clock from the command line with chronyd. Let’s run it with -q option to update the clock:

$ chronyd -q "server host1 iburst"
2022-04-29T22:26:46Z chronyd version 3.4 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASYNCDNS +SECHASH +IPV6 +DEBUG)
2022-04-29T22:26:50Z System clock wrong by 194.831537 seconds (step)
2022-04-29T22:26:50Z chronyd exiting

After the manual time update, we can print the measured offset again with chronyd -Q:

$ chronyd -Q "server host1 iburst"
2022-04-29T22:27:17Z System clock wrong by 0.001776 seconds (ignored)

From the above, we managed to reduce the offset between the system clock and the time of the NTP server.

7. Conclusion

In this article, we discussed the NTP service. We learned how to set our time zone appropriately and configure the NTP client and server for time synchronization.

Comments are closed on this article!