Skip to content
Go back

Time Sync on VMs: Why NTP Keeps Drifting

By SumGuy 4 min read
Time Sync on VMs: Why NTP Keeps Drifting

The Silent Time Bomb

You spin up a VM. Everything works fine for a week. Then your logs start getting weird timestamps. Cron jobs run at wrong times. Your TLS certificates say they’re valid but your system thinks they expired. You check NTP — it’s running, synced, green lights everywhere.

But your system time is still garbage.

Welcome to VM time sync hell. It’s not actually NTP’s fault (usually). It’s that your hypervisor is lying to you.

How VMs Handle Time

On bare metal, the hardware has a Real Time Clock (RTC) that keeps time even when the system’s off. The kernel reads it at boot, then uses a high-precision timer (TSC on Intel, HPET on others) to keep track of elapsed time.

On a VM, there’s no real hardware timer. The hypervisor emulates one. It maps the host’s clock to the guest VM. But that mapping is approximate. If the host runs hot, if the VM is competing for CPU with other guests, if the hypervisor makes scheduling decisions, the guest’s clock drifts.

NTP helps, but it assumes your clock only drifts slowly. It won’t fix a clock that’s hours off — it’ll either reject the adjustment or slip the time gradually. If your VM’s clock jumped 5 minutes due to hypervisor jitter, NTP might not catch it fast enough.

Check If You’re Actually Drifting

Run this:

Terminal window
timedatectl

Look for System clock synchronized: yes. If it says no, NTP isn’t working. Check the service:

Terminal window
systemctl status systemd-timesyncd
# or
systemctl status ntp
# or
systemctl status chronyd

If the service is running but you’re still drifting, check the offset:

Terminal window
chronyc tracking
# or
ntpq -p

For chronyc tracking, look at the Frequency line. If it’s something like +500 ppm, that means your clock is running 500 parts-per-million fast. Over a week, that’s several seconds of drift.

The Hypervisor Problem

Here’s the thing: NTP can only slew (gradually adjust) time at a rate of about 500 ppm. If your VM’s clock drifts faster than that, NTP will never catch up. And hypervisor scheduling can absolutely cause faster drift than that.

Check your hypervisor settings. If you’re using Proxmox:

Terminal window
cat /sys/devices/system/clocksource/clocksource0/current_clocksource

You want something stable like tsc or kvm-clock. If you see jiffies or pit, you’re in trouble — those are fallbacks and drift like crazy.

The Fix: Hypervisor Time Sources

On Proxmox/KVM:

Edit your VM config and add a clock source:

/etc/pve/qemu-server/vmid.conf
args: -rtc base=utc

Or in the web UI, go to HardwareBIOS and set it to OVMF (UEFI), which handles time better than SeaBIOS.

Also, make sure you’re using kvm-clock:

Terminal window
grep kvm_clock /proc/devices

If it’s there, your kernel already supports it.

On VMware vSphere:

In the VM settings, go to VM OptionsGeneral OptionsAdvanced. Look for tools.syncTime. Set it to FALSE and let NTP handle time instead:

Terminal window
tools.syncTime = FALSE

VMware’s time sync tool conflicts with NTP and causes thrashing.

On VirtualBox:

Guest Additions includes time sync. It’s aggressive and can fight with NTP. Disable it:

Terminal window
VBoxManage setextradata "VM Name" "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 1

Configure NTP Properly

If your hypervisor is providing a stable clock source, NTP should work fine. Use chrony on modern systems — it’s more robust than ntpd:

Terminal window
apt install chrony
systemctl enable chronyd

Configure it:

/etc/chrony/chrony.conf
server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
server 2.pool.ntp.org iburst
server 3.pool.ntp.org iburst
allow 127.0.0.1
allow ::1

The iburst flag means “send 8 packets quickly instead of 1” on startup. Speeds up initial sync.

Restart:

Terminal window
systemctl restart chronyd

Check status:

Terminal window
chronyc sources
chronyc tracking

The Nuclear Option: Jump the Clock

If you’ve got a VM that’s hours off and NTP isn’t fixing it (maybe it just came online from a backup), you might need to manually jump it:

Terminal window
# Disable NTP temporarily
systemctl stop chronyd
# Set the time manually
timedatectl set-time "2025-11-04 14:30:00"
# Re-enable NTP
systemctl start chronyd

This is scary and can break things. Don’t do it on production unless you know what you’re doing.

Prevention: Don’t Let It Get Bad

Check your VM’s time health weekly:

Terminal window
echo "$(date): $(timedatectl | grep 'System clock')" >> /var/log/time-check.log

Or put it in a cron job:

/etc/cron.d/check-time
0 * * * * root timedatectl | grep -q "synchronized: yes" || echo "ALARM: time not synced" | mail -s "Time Sync Alert" root

Bottom line: NTP is doing its job. If it’s not working, your hypervisor is lying about time. Fix that and NTP becomes magic again.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Auditd & Audit Logging: Know Exactly Who Touched What on Your Server
Next Post
HashiCorp Vault: Stop Hardcoding Secrets Like It's 2012

Related Posts