Skip to main content
RunBook Academy

LinuxLXXVI · Virtualisation and LinuxGuest visibility

Timekeeping in a guest - clock sources, steps and the resumed VM

Advanced⏱ ~17 minchronyctimedatectldmesg

What you'll learn

  • Choose and verify the clock source a guest should be using
  • Explain why chrony slews rather than steps by default, and what that costs a resumed guest
  • Avoid the two-masters problem between hypervisor time sync and NTP
  • Recognise the downstream failures of a guest whose clock is wrong in each direction

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-11

Not yet marked complete on this device.

The overview lesson gave the two rules that avoid most VM time incidents. This one is about why they are the rules, because when a time problem does happen the diagnosis depends on knowing which layer produced it.

The root cause of all of it is one sentence: a vCPU is not always running. The host deschedules it whenever it wants the physical core, and any timekeeping that assumes a continuously executing CPU loses time across that gap without knowing it did.

Clock sources

The kernel reads time from a clock source, and the guest has several to choose from:

Read-only / Safea KVM guest should be on kvm-clock
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock
kvm-clock tsc acpi_pm

Illustrative output

SourceHow it reads timeCostSurvives migration
kvm-clockParavirtual: a shared page the host updatesVery lowYes - the host corrects it
tscReads the CPU timestamp counter directlyLowestOnly with hardware TSC scaling
acpi_pmTraps to the emulated ACPI power-management timerHigh: a VM exit per readYes, slowly
hpetTraps to the emulated HPETHighYes, slowly

kvm-clock is the right answer on KVM and it is the default on any modern guest. The equivalent on VMware and Hyper-V is their own paravirtual source. Set nothing and check occasionally; the situation worth catching is a guest that fell back to acpi_pm, where every clock_gettime() becomes a VM exit and a busy application can spend a measurable fraction of its CPU asking what time it is.

Why not just use the TSC

The TSC is the fastest thing available: a single instruction, no trap. On modern hardware it is invariant, meaning it ticks at a constant rate regardless of CPU frequency scaling and C-states, so a guest pinned to one host can use it safely.

The problem is movement. TSC values and frequencies differ between physical hosts. Live-migrating a guest that reads the TSC directly means the counter it is reading changes rate and offset underneath it. Modern CPUs have TSC scaling support in hardware - the tsc_scale flag on AMD, an equivalent VMX control on Intel - which lets the hypervisor present a consistent TSC across hosts, and where it is missing the hypervisor has to intervene.

kvm-clock sidesteps all of it by asking the host, which is the entity that knows the answer.

Watch for the kernel deciding the TSC is not trustworthy:

sudo dmesg -T | grep -iE 'clocksource|tsc'

Marking TSC unstable due to clocksource watchdog or ... due to check_tsc_sync_source failed is the kernel demoting itself to a slower source. It is a symptom, not a fault to fix in the guest: it usually means host CPU oversubscription or a hypervisor configuration issue, and the guest is now paying a much higher cost per time read.

What chrony does with a large offset

This is the behaviour that surprises people, and it is a deliberate design decision rather than a bug.

By default chrony slews: it speeds the clock up or slows it down until the offset is gone, so time never goes backwards and never jumps. Applications that assume wall time advances monotonically are not broken by it.

It steps - jumps the clock directly - only under the makestep directive, and the usual configuration limits stepping to the first few updates after start:

# /etc/chrony/chrony.conf
makestep 1.0 3

That reads: step if the offset exceeds 1 second, but only if there have been no more than 3 clock updates since chronyd started. In other words, step at boot and slew from then on.

The consequence for a VM is direct. A guest paused for two hours and resumed comes back two hours behind. chronyd has been running for weeks, so its step allowance is long exhausted, and it begins to slew. Slewing is bounded by maxslewrate, and the Linux kernel’s own ceiling on the frequency offset a driver can set is 100000 ppm - ten per cent. Correcting a two-hour offset at ten per cent takes twenty hours, and the default rate is lower still.

The guest is now hours out of sync, converging slowly, and chronyc tracking reports it working correctly the entire time:

chronyc tracking
chronyc sources -v
timedatectl

Two ways to handle it, and the choice is a real trade:

# Allow stepping at any time. Correct for guests that are
# paused, snapshotted or migrated. Time can go backwards.
makestep 1.0 -1

A negative limit disables the “only the first N updates” restriction. This is the right setting for guests that get suspended or restored, and it means an application can observe wall-clock time moving backwards - which breaks anything using CLOCK_REALTIME to measure an interval, and is genuinely dangerous for a database that derives ordering from timestamps.

The alternative is to force a correction explicitly, at a moment you choose, as part of the resume procedure:

sudo chronyc makestep

That steps once, on demand. For a database guest, stepping during a controlled window after resume - before the application is allowed back in - is safer than leaving makestep permanently unrestricted.

The two-masters problem

Both the hypervisor and NTP can set the guest clock. When both are authoritative they fight, and the resulting jumps belong to neither.

  • On VMware, open-vm-tools can synchronise guest time to the host. If you also run chrony, disable the tools synchronisation and let chrony be the single authority.
  • On KVM, there is no equivalent continuous sync - the paravirtual clock gives the guest a good starting point and chrony disciplines from there, which is the desired arrangement by default.
  • On Hyper-V, the time synchronisation integration service does the same job as the VMware one and needs the same decision.

The rule is one authority. The paravirtual clock source is not an authority in this sense - it is how the guest reads time - so kvm-clock plus chrony is one authority, not two.

ptp_kvm: host time without the network

A KVM guest can read the host clock directly through a PTP device, avoiding the network round trip entirely. The ptp_kvm module creates a PTP hardware clock node, which chrony can then use as a reference clock:

sudo modprobe ptp_kvm
ls -l /dev/ptp0
# /etc/chrony/chrony.conf
refclock PHC /dev/ptp0 poll 2

The accuracy is far better than network NTP because there is no network. The trade-off is that the guest is now synchronised to its host, so the fleet is only consistent if the hosts are - which they should be, and which now becomes a hard requirement rather than a nice-to-have.

If /dev/ptp0 does not appear, the module is not loaded or the platform does not support it; the module is KVM-specific.

Validation

cat /sys/devices/system/clocksource/clocksource0/current_clocksource
timedatectl | grep -E 'synchronized|NTP service|RTC in local TZ'
chronyc tracking | grep -E 'Stratum|System time|Leap status'
chronyc sources -v | head -5
grep -E '^(makestep|maxslewrate|refclock)' /etc/chrony/chrony.conf 2>/dev/null

What each line should say: the clock source is the paravirtual one; System clock synchronized: yes and RTC in local TZ: no; the stratum is sensible and Leap status is Normal; there is at least one selected source; and the makestep policy is one you chose deliberately rather than inherited.

The RTC line matters more than it looks. A guest with its hardware clock in local time will be an hour wrong twice a year, on a schedule, in a way that only shows up in the first boot after the transition.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A guest is resumed after a two-hour pause. chronyd has been running for weeks with the usual `makestep 1.0 3`. What happens?

  2. Q2. A VM is restored from a three-week-old snapshot. Which symptoms follow from the clock alone? Select all that apply.

  3. Q3. Running chrony alongside a hypervisor time synchronisation service that also sets the guest clock is a misconfiguration.

  4. Q4. What does `Marking TSC unstable` in the guest kernel log indicate?

Passing score: 75%. Answers are checked in this browser.