NTP Setup Between Two Linux Servers Using Chrony — Production-Ready Guide
In any system, time is not just a background detail — it’s a core dependency. Every log entry, authentication request, scheduled job, and…
NTP Setup Between Two Linux Servers Using Chrony — Production-Ready Guide
In any system, time is not just a background detail — it’s a core dependency. Every log entry, authentication request, scheduled job, and distributed operation relies on the assumption that all machines share a consistent clock. The moment that assumption breaks, systems don’t fail loudly — they fail silently and unpredictably.
Imagine debugging an issue where events appear out of order, authentication tokens randomly fail, or scheduled tasks behave inconsistently. In many cases, the root cause isn’t complex architecture or bad code — it’s simply that the servers involved don’t agree on what time it is.
This is where Network Time Protocol (NTP) becomes essential. NTP ensures that all systems in a network stay synchronized to a common time source, maintaining consistency across infrastructure. Instead of each server drifting independently, they operate on a unified timeline, making systems reliable, traceable, and predictable.
In this guide, we’ll set up a simple and effective NTP architecture using Rocky Linux, where one server acts as the time source and another synchronizes against it.

Architecture
- NTP Master (Server):
192.168.0.110→ntp-master - NTP Client:
192.168.0.111→ntp-client
One machine serves time. The other consumes it. No ambiguity.
Why Chrony (Not ntpd)
Rocky Linux uses chrony by default for a reason:
- Faster sync on boot
- Better handling of unstable networks
- Lower drift over time
- Works cleanly in VMs and cloud
If you’re still thinking about ntpd, you’re behind.
Step 1 — Configure NTP Master (192.168.0.110)
Set hostname
hostnamectl set-hostname ntp-master
Install chrony
dnf install -y chrony
Edit config
vi /etc/chrony.conf
Minimal, Correct Config
# Upstream time sources (optional but recommended)
pool 2.rocky.pool.ntp.org iburst
# Allow clients from your subnet
allow 192.168.0.0/24
# Act as time source even if upstream is unavailable
local stratum 10
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony
Start service
systemctl enable chronyd
systemctl restart chronyd
Open firewall (if enabled)
firewall-cmd --add-service=ntp --permanent
firewall-cmd --reload
Verify
chronyc sources -v
chronyc tracking
If this server isn’t synced, your entire setup is garbage. Fix this first.
Step 2 — Configure NTP Client (192.168.0.111)
Set hostname
hostnamectl set-hostname ntp-client
Install chrony
dnf install -y chrony
Edit config
vi /etc/chrony.conf
Critical Fix (Most People Fail Here)
REMOVE:
pool 2.rocky.pool.ntp.org iburst
ADD:
server 192.168.0.110 iburst
Final Clean Config
server 192.168.0.110 iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony
Start service
systemctl enable chronyd
systemctl restart chronyd
Step 3 — Verification (Don’t Skip This)
On Client
[root@ntp-client ~]# chronyc sources -v
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
| / 'x' = may be in error, '~' = too variable, '?' = unusable.
|| .- 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
===============================================================================
^* ntp-master 10 6 377 12 +8419ns[ +12us] +/- 269us
Expected:
- You should see
192.168.0.110 - Marked with
^*(selected source)
[root@ntp-client ~]# chronyc tracking
Reference ID : C0A8006E (ntp-master)
Stratum : 11
Ref time (UTC) : Thu Apr 16 16:35:30 2026
System time : 0.000002946 seconds fast of NTP time
Last offset : +0.000003721 seconds
RMS offset : 0.000014631 seconds
Frequency : 4.336 ppm slow
Residual freq : +0.001 ppm
Skew : 0.238 ppm
Root delay : 0.000537779 seconds
Root dispersion : 0.000058532 seconds
Update interval : 64.0 seconds
Leap status : Normal
Check:
Reference ID→ should point to masterStratum→ higher than master (e.g., 11 if master is 10)
Common Failures (You Will Hit These If You’re Careless)
1. Still using public NTP on client
If you see external servers → you didn’t remove the pool line. Fix it. No excuses.
2. ^? or unreachable source
Means:
- Firewall blocking UDP 123
- Master not running
- Network issue
Check:
ping 192.168.0.110
ss -ulpn | grep 123
3. Master not allowing clients
If you skipped:
allow 192.168.0.0/24
Client will never sync. Period.
4. Time not syncing immediately
Chrony doesn’t blindly jump time unless needed.
Force sync:
chronyc makestep
What “Good” Looks Like
On client:
^* 192.168.0.110
On master:
- Synced to upstream OR acting as local stratum
Hard Truth
If you don’t verify:
- source selection (
^*) - stratum consistency
- tracking output
Then you didn’t configure NTP — you guessed and hoped.
This setup is simple. If it’s not working, you missed something basic. Go back and check instead of layering more confusion on top.
Conclusion
You now have:
- A controlled internal time source
- No dependency on external NTP for clients
- Predictable, stable synchronization
That’s how you run this in real environments — clean, minimal, and verified.
메타데이터
- post_id
- d6ee758d6d33
- slug
- ntp-setup-between-two-rocky-linux-servers-chrony-production-ready-guide-d6ee758d6d33
- url
- https://medium.com/@tradingcontentdrive/ntp-setup-between-two-rocky-linux-servers-chrony-production-ready-guide-d6ee758d6d33
- canonical_url
- https://medium.com/@tradingcontentdrive/ntp-setup-between-two-rocky-linux-servers-chrony-production-ready-guide-d6ee758d6d33
- author_url
- https://medium.com/@tradingcontentdrive
- status
- ok
- fetched_at
- 2026-06-12 22:02:08