How to Install and Configure Chrony for Precise Time Sync on Linux Systems
This guide explains Chrony, a high‑precision time synchronization service, covering its key features, file structure, and step‑by‑step installation and configuration across major Linux distributions, including network setup, firewall and SELinux adjustments, timezone settings, and scripts for both server and client deployment.
Chrony Introduction and Installation
1. Chrony (Time Synchronization Service)
1.1 Introduction to Chrony
Chrony is a time‑synchronization software designed to provide high‑precision system clock synchronization. It includes an NTP (Network Time Protocol) server and client to keep the system clock accurate.
Key features and functions of Chrony:
High‑precision clock synchronization using advanced algorithms and drift compensation.
Support for the NTP protocol, allowing communication with external NTP servers.
Flexible configuration options for customizing synchronization behavior.
Fault tolerance and robustness to handle network interruptions and unavailable time servers.
System clock management, monitoring and adjusting the clock for stability.
Chrony is a powerful tool suitable for servers and devices that require accurate time.
Official website: https://chrony-project.org
Documentation: https://chrony-project.org/documentation.html
1.2 Chrony File Components
Package: chrony
Two main programs:
chronyd – a background daemon that adjusts the kernel clock and synchronizes with NTP servers.
chronyc – a command‑line utility for monitoring performance and configuring chronyd.
Service unit file: /usr/lib/systemd/system/chronyd.service
Listening ports: server 123/udp, client 323/udp
Configuration file: /etc/chrony.conf
1.3 chrony.conf Configuration File
server - 可用于时钟服务器,iburst 选项当服务器可达时,发送一个八个数据包而不是通常的一个数据包。 包间隔通常为2秒,可加快初始同步速度
driftfile - 根据实际时间计算出计算机增减时间的比率,将它记录到一个文件中,会在重启后为系统时钟作出补偿
rtcsync - 启用内核模式,系统时间每11分钟会拷贝到实时时钟(RTC)
allow / deny - 指定一台主机、子网,或者网络以允许或拒绝访问本服务器
cmdallow / cmddeny - 可以指定哪台主机可以通过chronyd使用控制命令
bindcmdaddress - 允许chronyd监听哪个接口来接收由chronyc执行的命令
makestep - 强制chronyd在调整期大于某个阀值时调整系统时钟
local stratum 10 - 即使server指令中时间服务器不可用,也允许将本地时间作为标准时间授时给其它客户端2. Chrony Installation
2.1 Host Initialization
2.1.1 Set Network Interface Name
For Rocky Linux 9/10, AlmaLinux 9/10, CentOS Stream 9/10, AnolisOS 23, OpenCloudOS 9:
# Create systemd link file
mkdir -p /etc/systemd/network/
touch /etc/systemd/network/70-eth0.link
# Example of checking interfaces
[root@rocky10 ~]# ip addr
... (output omitted) ...
# Write link file
cat > /etc/systemd/network/70-eth0.link <<EOF
[Match]
MACAddress=00:0c:29:f8:60:8f
[Link]
Name=eth0
EOFModify NetworkManager connection files, rename and edit the connection name to eth0.
# mv /etc/NetworkManager/system-connections/ens160.nmconnection /etc/NetworkManager/system-connections/eth0.nmconnection
sed -i.bak 's/ens160/eth0/' /etc/NetworkManager/system-connections/eth0.nmconnectionFor Rocky Linux 8, AlmaLinux 8, CentOS 7, CentOS Stream 8, openEuler 22.03/24.03 LTS, AnolisOS 8, OpenCloudOS 8, Kylin Server v10, UOS Server v20:
# Edit GRUB configuration to disable predictable network interface names
sed -ri.bak 's/^GRUB_CMDLINE_LINUX/& net.ifnames=0 biosdevname=0/' /etc/default/grub
# Regenerate GRUB configuration
grub2-mkconfig -o /boot/grub2/grub.cfg # or appropriate EFI path2.1.2 Create Udev Rule (AnolisOS 8 only)
# /etc/udev/rules.d/10-network.rules
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="<MAC_ADDRESS>", NAME="eth0"2.1.3 Configure Network Interface
Set static IP, gateway, DNS for each distribution. Example for Rocky/Linux:
# /etc/sysconfig/network-scripts/ifcfg-eth0
NAME=eth0
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=none
TYPE=Ethernet
IPADDR=172.31.0.10
PREFIX=21
GATEWAY=172.31.0.2
DNS1=223.5.5.5
DNS2=180.76.76.76Ubuntu Netplan example:
# /etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: no
addresses: [172.31.0.10/21]
gateway4: 172.31.0.2
nameservers:
addresses: [223.5.5.5,180.76.76.76]2.1.4 Disable Firewall
# Disable firewalld (Rocky, AlmaLinux, CentOS, openEuler, AnolisOS, OpenCloudOS, openSUSE, Kylin Server, UOS Server)
systemctl disable --now firewalld
# For CentOS 7 also disable NetworkManager
systemctl disable --now NetworkManager
# Ubuntu
systemctl disable --now ufw2.1.5 Disable SELinux
# For distributions with SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config2.1.6 Disable AppArmor (openSUSE)
# Disable AppArmor
systemctl disable --now apparmor2.1.7 Set Timezone
timedatectl set-timezone Asia/Shanghai
echo 'Asia/Shanghai' > /etc/timezone
# Ubuntu locale adjustment
cat >> /etc/default/locale <<EOF
LC_TIME=en_DK.UTF-8
EOF2.2 Implement a Private Time Server
2.2.1 Server Configuration
Install Chrony and edit /etc/chrony.conf to use public NTP servers and allow all clients.
# Install Chrony (example for Rocky)
yum install -y chrony
# Edit /etc/chrony.conf
# Remove existing pool lines and add custom servers
server ntp.aliyun.com iburst
server ntp.tencent.com iburst
server ntp.tuna.tsinghua.edu.cn iburst
allow 0.0.0.0/0
local stratum 10Start and enable the service:
systemctl restart chronyd
systemctl enable --now chronyd
ss -ntul # verify ports 123/udp and 323/udp are open
chronyc sources -nv # verify synchronization2.2.2 Client Configuration
Install Chrony on the client and point it to the private server (e.g., 172.31.0.9).
# Install Chrony
yum install -y chrony # or apt install -y chrony
# Edit /etc/chrony.conf (or /etc/chrony/chrony.conf for Ubuntu/Debian)
# Remove pool lines and add the private server
server 172.31.0.9 iburst
allow 0.0.0.0/0
local stratum 10
systemctl restart chronyd
systemctl enable --now chronyd
chronyc sources -nv # should show * for the private server2.3 One‑Click Chrony Installation Scripts
2.3.1 Server Installation Script
#!/bin/bash
# install_chrony_server_v3.sh – Chrony server installer supporting Rocky, AlmaLinux, CentOS, openEuler, Anolis, OpenCloudOS, openSUSE, Kylin, UOS
COLOR="echo -e \033[01;31m"
END='\033[0m'
os(){
. /etc/os-release
MAIN_NAME=$(sed -rn 's/^NAME="?([A-Za-z]+).*"?$/\1/p' /etc/os-release)
if [ "$MAIN_NAME" = "Kylin" ]; then
MAIN_VERSION_ID=$(sed -rn 's/^VERSION_ID="?([A-Za-z]+)(.*)"?$/\2/p' /etc/os-release)
else
MAIN_VERSION_ID=$(sed -rn 's/^VERSION_ID="?([0-9]+).*"?$/\1/p' /etc/os-release)
fi
if [[ "$MAIN_NAME" == "Ubuntu" || "$MAIN_NAME" == "Debian" ]]; then
FULL_NAME="$PRETTY_NAME"
elif [[ "$MAIN_NAME" == "UOS" ]]; then
FULL_NAME="$NAME"
else
FULL_NAME="$NAME$VERSION_ID"
fi
}
os
NTP_SERVER1=ntp.aliyun.com
NTP_SERVER2=ntp.tencent.com
NTP_SERVER3=ntp.tuna.tsinghua.edu.cn
install_chrony(){
if [[ "$MAIN_NAME" =~ ^(Rocky|AlmaLinux|CentOS|openEuler|Anolis|OpenCloudOS|openSUSE|Kylin|UOS)$ ]]; then
if [[ "$MAIN_NAME" == "openSUSE" ]]; then
INSTALL_TOOL=zypper
else
INSTALL_TOOL=yum
fi
rpm -q chrony &>/dev/null || { $COLOR "Installing chrony, please wait..." $END; $INSTALL_TOOL install -y chrony &>/dev/null; }
if [[ "$MAIN_NAME" == "OpenCloudOS" ]]; then
sed -i -e '/^pool.*/d' -e '/^server.*/d' -e "/^# Use public.*/a\server $NTP_SERVER1 iburst
server $NTP_SERVER2 iburst
server $NTP_SERVER3 iburst" -e 's@^#allow.*@allow 0.0.0.0/0@' -e 's@^#local.*@local stratum 10@' /etc/chrony.conf
else
sed -i -e '/^pool.*/d' -e '/^server.*/d' -e "/^# Please consider .*/a\server $NTP_SERVER1 iburst
server $NTP_SERVER2 iburst
server $NTP_SERVER3 iburst" -e 's@^#allow.*@allow 0.0.0.0/0@' -e 's@^#local.*@local stratum 10@' /etc/chrony.conf
fi
else
dpkg -s chrony &>/dev/null || { $COLOR "Installing chrony, please wait..." $END; apt install -y chrony &>/dev/null; }
if [[ "$MAIN_NAME" == "Ubuntu" ]]; then
sed -i -e '/^pool.*/d' -e "/^# See http:.*/a\server $NTP_SERVER1 iburst
server $NTP_SERVER2 iburst
server $NTP_SERVER3 iburst" /etc/chrony/chrony.conf
else
sed -i -e '/^pool.*/d' -e "/^# Use Debian.*/a\server $NTP_SERVER1 iburst
server $NTP_SERVER2 iburst
server $NTP_SERVER3 iburst" /etc/chrony/chrony.conf
fi
echo "allow 0.0.0.0/0" >> /etc/chrony/chrony.conf
echo "local stratum 10" >> /etc/chrony/chrony.conf
fi
systemctl restart chronyd && systemctl enable --now chronyd &>/dev/null
systemctl is-active chronyd &>/dev/null || { $COLOR "Chrony failed to start, exiting!" $END; exit 1; }
$COLOR "$FULL_NAME" $END "Chrony server installation completed!"
}
install_chrony2.3.2 Client Installation Script
#!/bin/bash
# install_chrony_client_v3.sh – Chrony client installer supporting the same distributions as the server script
COLOR="echo -e \033[01;31m"
END='\033[0m'
os(){
. /etc/os-release
MAIN_NAME=$(sed -rn 's/^NAME="?([A-Za-z]+).*"?$/\1/p' /etc/os-release)
if [ "$MAIN_NAME" = "Kylin" ]; then
MAIN_VERSION_ID=$(sed -rn 's/^VERSION_ID="?([A-Za-z]+)(.*)"?$/\2/p' /etc/os-release)
else
MAIN_VERSION_ID=$(sed -rn 's/^VERSION_ID="?([0-9]+).*"?$/\1/p' /etc/os-release)
fi
if [[ "$MAIN_NAME" == "Ubuntu" || "$MAIN_NAME" == "Debian" ]]; then
FULL_NAME="$PRETTY_NAME"
elif [[ "$MAIN_NAME" == "UOS" ]]; then
FULL_NAME="$NAME"
else
FULL_NAME="$NAME$VERSION_ID"
fi
}
os
SERVER=172.31.0.9
install_chrony(){
if [[ "$MAIN_NAME" =~ ^(Rocky|AlmaLinux|CentOS|openEuler|Anolis|OpenCloudOS|openSUSE|Kylin|UOS)$ ]]; then
if [[ "$MAIN_NAME" == "openSUSE" ]]; then
INSTALL_TOOL=zypper
else
INSTALL_TOOL=yum
fi
rpm -q chrony &>/dev/null || { $COLOR "Installing chrony, please wait..." $END; $INSTALL_TOOL install -y chrony &>/dev/null; }
if [[ "$MAIN_NAME" == "OpenCloudOS" ]]; then
sed -i -e '/^pool.*/d' -e '/^server.*/d' -e "/^# Use public.*/a\server $SERVER iburst" /etc/chrony.conf
else
sed -i -e '/^pool.*/d' -e '/^server.*/d' -e "/^# Please consider .*/a\server $SERVER iburst" /etc/chrony.conf
fi
else
dpkg -s chrony &>/dev/null || { $COLOR "Installing chrony, please wait..." $END; apt install -y chrony &>/dev/null; }
if [[ "$MAIN_NAME" == "Ubuntu" ]]; then
sed -i -e '/^pool.*/d' -e "/^# See http:.*/a\server $SERVER iburst" /etc/chrony/chrony.conf
else
sed -i -e '/^pool.*/d' -e "/^# Use Debian.*/a\server $SERVER iburst" /etc/chrony/chrony.conf
fi
fi
systemctl restart chronyd && systemctl enable --now chronyd &>/dev/null
systemctl is-active chronyd &>/dev/null || { $COLOR "Chrony failed to start, exiting!" $END; exit 1; }
$COLOR "$FULL_NAME" $END "Chrony client installation completed!"
}
install_chronyThe above sections provide a complete walkthrough for installing Chrony, configuring a private NTP server, setting up clients, and using one‑click scripts to automate the process on a wide range of Linux distributions.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
