./ahmedhashim

Clock Drift

My Arch desktop had drifted about a minute ahead of the two clocks I don’t usually think to question: my radio-controlled Casio GW-B5600 and my phone. The watch corrects itself from longwave radio and the phone has network time, so when both of them agree against the desktop, it’s a safe bet the desktop is the one in the wrong.

timedatectl was the first stop:

$ timedatectl status
               Local time: Sat 2025-01-04 08:11:01 PST
           Universal time: Sat 2025-01-04 16:11:01 UTC
                 RTC time: Sat 2025-01-04 16:11:01
                Time zone: America/Los_Angeles (PST, -0800)
System clock synchronized: no
              NTP service: inactive
          RTC in local TZ: no

Two lines matter here:

System clock synchronized: no
NTP service: inactive

Nothing was disciplining the clock. At boot, Linux reads the RTC, then advances time from hardware timers. Without NTP, nothing compares that against an external reference and nudges it back.

So the desktop was just another quartz clock. Manufacturing tolerance sets the baseline, and temperature and power state keep shifting the rate around.

The good news was RTC in local TZ: no. The hardware clock was UTC, which is what I want on Linux. Only the system clock was off.

The fix was to enable time synchronization:

sudo timedatectl set-ntp true
sudo systemctl enable --now systemd-timesyncd.service

After a few packets, timedatectl looked much healthier:

$ timedatectl status
               Local time: Sat 2025-01-04 08:18:42 PST
           Universal time: Sat 2025-01-04 16:18:42 UTC
                 RTC time: Sat 2025-01-04 16:18:42
                Time zone: America/Los_Angeles (PST, -0800)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

The more interesting output comes from timedatectl timesync-status:

$ timedatectl timesync-status
       Server: 141.11.228.173 (0.arch.pool.ntp.org)
Poll interval: 34min 8s (min: 32s; max 34min 8s)
         Leap: normal
      Version: 4
      Stratum: 2
    Reference: 11FD0225
    Precision: 1us (-26)
Root distance: 1.052ms (max: 5s)
       Offset: +4.917ms
        Delay: 81.811ms
       Jitter: 3.721ms
 Packet count: 8
    Frequency: +4.873ppm
  • Server is the active NTP peer drawn from the pool, so the exact IP doesn’t matter much.
  • Poll interval is how often the client asks for time, stretching out as the local clock looks more stable.
  • Leap is leap-second state, and normal means none is pending.
  • Version is the NTP protocol version, and 4 is what you should see.
  • Stratum is the hop distance from a primary reference like GPS or a radio clock, with stratum 2 (synced from a stratum 1) being normal for a desktop on the public internet.
  • Reference is the server’s upstream identifier, mainly useful when debugging the server side.
  • Precision is what the server advertises for its own clock, which feeds into the error estimate but says nothing about my desktop on its own.
  • Root distance is the estimated maximum gap from true reference time after delay and accumulated error, around a millisecond here and well under the five second max.
  • Offset is the current difference between my local clock and the server’s, and a few milliseconds over the public internet is fine.
  • Delay is the round-trip to the server, around 82ms here, which is unsurprising for public NTP.
  • Jitter is the variation between samples, where lower is better and a few milliseconds is fine.
  • Packet count is how many samples have gone into the current estimate, and the kernel needs a handful before it trusts it.

That leaves Frequency, the field that explains the original symptom. It’s the rate correction the kernel has learned for the local oscillator, in parts per million. At +4.873ppm, the desktop was running roughly 4.873 microseconds per second too fast:

4.873 ppm * 86,400 seconds/day = 0.421 seconds/day

Tiny per second, but it accumulates. Half a second per day is a few seconds per week. Add RTC drift while the machine is powered off, and the gap against a radio-controlled watch becomes hard to miss.

Once the system clock is correct, write it back to the RTC:

sudo hwclock --systohc --utc

That way the next boot starts close. NTP still corrects after the network comes up, but starting closer beats starting far off.

No mystery. The desktop had been keeping time on its own while the watch and phone were checking in with the outside world. Once NTP was back on, the desktop joined them, and the drift fell into the usual millisecond-scale noise of internet time sync.