Why accurate time matters
A clock that is off by seconds breaks several things:
- TLS handshake. Certificates are validated against the wall clock. Drift by ±5 minutes
from reality and
curlreturnscertificate has expired. - Kerberos. The tolerance is ±5 minutes, beyond that you get
KRB_AP_ERR_SKEW. - Logs and tracing. ELK/Loki/Jaeger correlate events by timestamp.
- Databases and replication. Postgres physical replication, MongoDB causal consistency.
- The filesystem.
makedecides what to rebuild from mtime, cron waits for a specific time, and systemd-timers drift.
NTP, the protocol
NTP (Network Time Protocol) runs over UDP/123. Servers form a hierarchy of strata:
Stratum 0 atomic clocks / GPS / radio signal (they do not answer NTP themselves)
↓
Stratum 1 servers attached directly to stratum 0 (a handful worldwide)
↓
Stratum 2 public servers synchronized with stratum 1
↓
Stratum 3 usually your own local NTP server, if you run one
↓
Stratum 4+ clients on your network
The higher the number, the further from the original source. Stratum 16 means "not synchronized".
Sources for a home or office host:
- pool.ntp.org. A global pool of thousands of servers; DNS returns a random set.
- Distribution pools:
0.fedora.pool.ntp.org,0.debian.pool.ntp.org,0.ubuntu.pool.ntp.org. - Your own internal server for a large network. It lowers load on the public pool and works in an air-gapped environment.
Three NTP implementations on Linux
| Implementation | Default on | Notes |
|---|---|---|
chronyd (chrony) | Fedora, RHEL, Arch | Full-featured, handles an intermittent network, can act as a server |
systemd-timesyncd | Ubuntu desktop, minimal images | Client only (SNTP), one server, lightweight |
ntpd (reference) | rare on new systems | Older implementation, slower to react, deprecated |
Run only one. If chrony is installed, run systemctl disable --now systemd-timesyncd.
Check which daemon synchronizes the clock:
timedatectl
# System clock synchronized: yes
# NTP service: active
When NTP service: active, some daemon is running. To find out which one,
use systemctl is-active chronyd systemd-timesyncd.
The chrony config: /etc/chrony.conf (or /etc/chrony/chrony.conf)
A minimal client config:
pool 2.fedora.pool.ntp.org iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
What the key directives mean:
| Directive | Purpose |
|---|---|
pool <host> iburst | The DNS name returns several servers; iburst gives a fast initial sync |
server <host> iburst | One specific server |
server X.X.X.X prefer | Prefer this source while it works |
driftfile <path> | Persist the crystal's drift estimate across reboots |
makestep 1.0 3 | If the offset is > 1 sec, apply the first 3 updates as a step, then slew |
rtcsync | Sync the RTC (hardware clock) to the system clock every 11 minutes |
allow 192.168.0.0/24 | Serve NTP to clients in this subnet (chrony as a server) |
local stratum 10 | Announce yourself at stratum 10 when the internet is unreachable (for air-gap) |
The iburst option is essential. At startup it sends 4 packets back to back at a 2-second
interval, so synchronization takes seconds instead of minutes.
The systemd-timesyncd config
/etc/systemd/timesyncd.conf is simple:
[Time]
NTP=time.cloudflare.com 0.pool.ntp.org
FallbackNTP=ntp.ubuntu.com
RootDistanceMaxSec=5
After an edit, run systemctl restart systemd-timesyncd. To check:
timedatectl timesync-status
Debugging with chronyc
chronyc tracking
# Reference ID : C0A80034 (yorktown.both.org)
# Stratum : 3
# System time : 0.000004870 seconds slow of NTP time ← key line
# Last offset : -0.000007259 seconds
# Frequency : 18715.217 ppm fast ← crystal drift
# Skew : 0.087 ppm
# Leap status : Normal
The main indicators:
- System time. How far the clock is behind or ahead. < 1 ms is fine. Seconds is bad.
- Stratum. Your own stratum. A 16 means not synchronized.
- Leap status.
Normal, orInsert/Deletein the days before a leap second.
The list of servers:
chronyc sources -v
# MS Name/IP address Stratum Poll Reach LastRx Last sample
# ^* time.cloudflare.com 3 8 377 55 +418us[+430us] +/- 18ms
Reading the MS column (Mode/State):
^is a regular server,=is a peer.*was chosen as best,+is combined into the estimate,-was rejected,xmay be wrong,?has not answered yet.
Force a sync right now:
sudo chronyc makestep
# 200 OK
Use this after editing the config or when a host wakes up from a long sleep.
The RTC, the hardware clock
The hardware clock (RTC) runs off a battery on the board and keeps ticking while the system is off. At boot the system clock (kernel) reads the RTC, and NTP then trims it.
hwclock --show # time in the RTC
sudo hwclock --systohc # write system → RTC
sudo hwclock --hctosys # read RTC → system
In chrony, rtcsync runs --systohc automatically every 11 minutes.
Without it, the clock jumps back to the RTC value after a reboot.
A gotcha with Windows/Linux dual boot: Windows stores local time in the RTC, Linux stores UTC. If you dual boot and the clock jumps by the timezone offset:
sudo timedatectl set-local-rtc 1 # tell Linux the RTC holds local time
Running your own NTP server
With dozens of hosts in an office or at home, one local server beats pointing them all
at pool.ntp.org. In /etc/chrony.conf on the server:
pool 2.fedora.pool.ntp.org iburst
allow 192.168.0.0/24 # who to serve
rtcsync
driftfile /var/lib/chrony/drift
Then open UDP/123 in the firewall. On the clients:
server 192.168.0.10 iburst prefer
The clients' stratum will be one above the server's.
Debugging: why the clock will not sync
systemctl status chronyd # is it even running
journalctl -u chronyd --since "10 min ago" # recent messages
chronyc activity # how many sources are online/offline
chronyc sources -v # do we see anything
# network check
sudo nmap -sU -p 123 0.pool.ntp.org # is the port open?
sudo ss -ulnp 'sport = :123' # are we listening ourselves?
Common problems:
- Every source shows
?(no answer). UDP/123 is blocked by the ISP's firewall. Reference ID: 00000000 (). Not synchronized, the initial sync is in progress, wait a minute withiburst.chronyddoes not start. It conflicts withsystemd-timesyncd. Disable timesyncd.