Engineering Atlas › System Design Fundamentals

Why clocks drift and how to cope

13 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Two clocks in every machine

A time-of-day clock returns the current date and time. NTP may step it, so it can jump forward or backward. A monotonic clock only moves forward and measures elapsed time; its absolute value is meaningless across machines. Timeouts, leases and latency measurements must use the monotonic clock. Timestamps that leave the machine use time-of-day.

Drift and skew

A crystal at 50 ppm drift gains or loses 50 microseconds per second, which is 4.3 seconds per day. Temperature changes drift further. Skew is the difference between two clocks at one instant. Synchronisation keeps skew bounded by measuring and correcting drift continuously.

How NTP estimates offset

The client records t1 and sends a request. The server records receive time t2 and send time t3. The client records arrival t4. Then round-trip delay is (t4 - t1) - (t3 - t2) and estimated offset is ((t2 - t1) + (t3 - t4)) / 2. The formula assumes the outbound and return paths take equal time. If one direction takes 30 ms and the other 10 ms, the estimate is off by 10 ms, and the client cannot detect it. The error is at most half the round-trip delay, so clients sample several servers repeatedly and trust the samples with the lowest delay (RFC 5905).

NTP servers form strata. Stratum 0 is an atomic or GPS reference, stratum 1 servers are attached to it, and each hop adds a stratum and some error. Daemons then slew the clock, running it slightly fast or slow until the offset closes, rather than stepping it. That only holds for small offsets. Classic ntpd slews offsets under 128 ms and steps anything larger, and because the Unix kernel slews at most 0.5 ms per second, each second of error takes about 2,000 s to remove (ntpd docs). chrony does its own slewing at up to 83,333 ppm by default (chrony.conf). So time is monotonic in practice only while offsets stay small; a badly wrong clock at boot, or after a long VM pause, still gets stepped.

Plain NTP packets are unauthenticated, so anyone on the path can shift a client's clock. Network Time Security adds a TLS key exchange and authenticated time packets (RFC 8915, 2020), and chrony enables it per server with the nts option.

Better precision

PTP (IEEE 1588) timestamps packets in the NIC hardware and lets switches correct for their own queueing delay, reaching sub-microsecond skew on a LAN. Meta moved its fleet from NTP to PTP after finding that commit-wait reads ran about 100 times better on PTP-backed clusters; in one 24-hour sample its NTP clocks stayed within about 8 microseconds and PTP within about 100 nanoseconds (Meta). Cloud providers now expose this too. Since November 2023, the Amazon Time Sync Service gives typical error bounds under 100 microseconds over NTP and under 40 microseconds from the PTP hardware clock on supported Nitro instances (AWS).

Leap seconds and smearing

UTC has occasionally inserted a leap second, 23:59:60, most recently at the end of 2016. Software that assumes every minute has 60 seconds has crashed or spun CPU on these days. Large operators instead smear the extra second, but not identically. Google runs a linear smear over 24 hours, noon to noon UTC, making each second about 11.6 ppm long (Google). Meta uses a quadratic smear over 17 hours starting at midnight UTC (Meta). Mixing smeared and non-smeared sources, or two different smears, gives up to about half a second of disagreement.

Leap seconds are now ending. In 2026 the General Conference on Weights and Measures (CGPM) resolved that UTC becomes continuous from 20 May 2027, with no further leap seconds and the UT1-UTC gap allowed to grow to one hour (BIPM, CGPM 2026 Resolution 5). Old timestamps still contain 23:59:60, so parsers must keep handling it, but new systems no longer need a smear plan.

Using clocks safely

Google's Spanner exposes time as an interval [earliest, latest] with a known error bound, typically a few milliseconds (a sawtooth from about 1 to 7 ms in the published design), and waits out that uncertainty before committing so timestamps respect real-time order (Spanner paper). Without such a bound, treat wall-clock timestamps from different machines as approximate.

Down the stack

Layer 1The contract

Linux exposes several clocks through clock_gettime, each with a different promise. CLOCK_REALTIME is UTC-ish wall time and can be stepped in either direction. CLOCK_MONOTONIC never goes backwards but is still slewed by NTP, so its rate tracks true seconds. CLOCK_MONOTONIC_RAW is the uncorrected hardware rate. CLOCK_BOOTTIME also counts time spent suspended, and CLOCK_TAI counts leap seconds instead of repeating one (clock_gettime(2)). Each call fills a 16-byte struct timespec of seconds and nanoseconds.

A sync daemon promises a bound, not an exact time. chronyc tracking reports the current offset, root delay and root dispersion, and chrony documents the absolute bound as the offset plus root dispersion plus half the root delay (chronyc docs). TrueTime makes the bound the API: TT.now() returns an interval [earliest, latest] guaranteed to contain true time, plus TT.after(t) and TT.before(t). AWS publishes a similar daemon, ClockBound, that hands applications the current error bound.

Layer 2The mechanism

An NTP packet is a 48-byte UDP datagram on port 123. It carries a 2-bit leap indicator, version, mode, stratum, poll exponent, precision, root delay, root dispersion, a reference ID, and four 64-bit timestamps: 32 bits of seconds since 1900 and 32 bits of fraction, a resolution of about 233 picoseconds. The 32-bit seconds field wraps in 2036, which the protocol handles with era numbers.

Clients poll every 2^poll seconds, by default somewhere between 64 s and 1024 s. Each source's recent samples (eight in classic ntpd) go through a clock filter that prefers the minimum-delay sample, because low delay means little room for asymmetric queueing. An intersection algorithm in the spirit of Marzullo's then compares the sources' confidence intervals and discards falsetickers whose intervals do not overlap the majority, which is why you configure at least four servers. The survivors feed a control loop that adjusts the local frequency and phase.

The daemon applies corrections through adjtimex(2): ADJ_FREQUENCY changes the rate in scaled parts per million and ADJ_OFFSET asks the kernel to slew away an offset. The kernel's own discipline caps slewing at 500 ppm, so a 1 s error takes over half an hour to remove. chrony bypasses that loop and can slew much faster and uses makestep (for example makestep 1.0 3) to permit a step only during the first few updates after boot.

PTP replaces software guesses with hardware: Sync and Follow_Up carry the master's send time, Delay_Req and Delay_Resp measure the return path, and transparent-clock switches add their residence time into a correctionField.

Layer 3The machine

On x86 the kernel usually reads time from the invariant TSC, a per-core counter running at a constant rate. Timekeeping converts cycles to nanoseconds as (cycles * mult) >> shift, and NTP corrections work by nudging mult. Through the vDSO, clock_gettime reads that counter without entering the kernel and costs on the order of 20 ns. If the clocksource falls back to HPET or ACPI PM, common in some VMs and visible in /sys/devices/system/clocksource/clocksource0/current_clocksource, every call becomes a real syscall plus slow I/O and can cost a microsecond or more.

Software timestamps are taken after the NIC raises an interrupt, and interrupt coalescing alone can add tens of microseconds of jitter. That is the floor for software NTP inside a datacentre. Hardware timestamping (SO_TIMESTAMPING) stamps packets in the NIC's PTP hardware clock, exposed as /dev/ptp0, and phc2sys then disciplines the system clock from it, which is how sub-microsecond skew is reached.

Spanner's published design polls GPS and atomic time masters every 30 s and assumes a worst-case local drift of 200 us per second between polls, so uncertainty grows in a sawtooth from about 1 ms to about 7 ms and resets after each poll. The two reference types are deliberate: GPS fails through antennas, interference and spoofing, atomic clocks through slow drift, and the failures rarely coincide.

Why it's built this way

  1. Quartz frequency depends on cut and temperature, so every clock drifts and must be measured against a reference continuously.
  2. Measurement crosses a network whose two directions are not guaranteed symmetric, so offset can only be estimated within half the round trip, and low-delay samples are worth the most.
  3. Time going backwards breaks software, so corrections are slewed rather than stepped, which makes convergence slow.
  4. Since error never reaches zero, the honest interface is an interval, and systems that need ordering either wait out the interval or stop depending on physical time.

Diagram

sequenceDiagram
    participant C as Client
    participant S as "NTP server"
    Note over C: t1 = send time (client clock)
    C->>S: request
    Note over S: t2 = receive time (server clock)
    Note over S: t3 = reply time (server clock)
    S-->>C: reply with t2, t3
    Note over C: t4 = arrival (client clock)
    Note over C: delay = (t4-t1) - (t3-t2)
    Note over C: offset = ((t2-t1)+(t3-t4))/2

Worked example

Client sends at t1 = 100.000. Server receives at t2 = 100.120 and replies at t3 = 100.121. Client receives at t4 = 100.041, all in seconds.

Delay is (100.041 - 100.000) - (100.121 - 100.120) = 0.041 - 0.001 = 0.040, so 40 ms on the wire. Offset is ((0.120) + (0.080)) / 2 = 0.100. The client is 100 ms behind the server, with uncertainty up to plus or minus 20 ms if the paths were asymmetric.

Now the consequence. A system uses last-write-wins with wall-clock timestamps. Node A is 100 ms behind. A user writes status=away on node B at true time 12:00:00.050 (stamped .050), then status=online on node A at true time 12:00:00.120 (stamped .020). LWW keeps away, the older write, and silently drops the newer one. With 3 replicas and 2,000 writes per second to hot keys, any skew larger than the gap between conflicting writes loses data.

In the wild

Trade-offs

ApproachStrengthsWeaknessesChoose when
NTP over internetFree, universalMilliseconds of error; asymmetric pathsGeneral servers, logging
NTP or chrony to local stratum 1Sub-millisecond in datacentreNeeds internal time infrastructureMost production fleets
PTP with hardware timestampsSub-microsecondSpecial NICs and switchesTrading, telecoms, tight ordering
Bounded-uncertainty time (TrueTime-style)Safe real-time orderingCommit wait; custom infrastructureExternally consistent databases
Logical clocksNo physical sync neededNo relation to real timeCausal ordering of events

Pitfalls in production

Leadership lens

Check yourself

  1. Why can NTP not measure a one-way network delay?

    Show answer

    It only sees four timestamps from two different clocks. Offset and path asymmetry are indistinguishable, so it assumes symmetric paths and bounds the error by half the round trip.

  2. Why should a lease timer use the monotonic clock?

    Show answer

    The wall clock can be stepped forward or backward by synchronisation, which would make a lease appear to expire early or last too long.

  3. What does slewing do that stepping does not?

    Show answer

    It corrects the offset by running the clock slightly fast or slow, so time never jumps and never goes backwards.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →