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:
timedatectlLook for System clock synchronized: yes. If it says no, NTP isn’t working. Check the service:
systemctl status systemd-timesyncd# orsystemctl status ntp# orsystemctl status chronydIf the service is running but you’re still drifting, check the offset:
chronyc tracking# orntpq -pFor 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:
cat /sys/devices/system/clocksource/clocksource0/current_clocksourceYou 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:
args: -rtc base=utcOr in the web UI, go to Hardware → BIOS and set it to OVMF (UEFI), which handles time better than SeaBIOS.
Also, make sure you’re using kvm-clock:
grep kvm_clock /proc/devicesIf it’s there, your kernel already supports it.
On VMware vSphere:
In the VM settings, go to VM Options → General Options → Advanced. Look for tools.syncTime. Set it to FALSE and let NTP handle time instead:
tools.syncTime = FALSEVMware’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:
VBoxManage setextradata "VM Name" "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 1Configure 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:
apt install chronysystemctl enable chronydConfigure it:
server 0.pool.ntp.org iburstserver 1.pool.ntp.org iburstserver 2.pool.ntp.org iburstserver 3.pool.ntp.org iburst
allow 127.0.0.1allow ::1The iburst flag means “send 8 packets quickly instead of 1” on startup. Speeds up initial sync.
Restart:
systemctl restart chronydCheck status:
chronyc sourceschronyc trackingThe 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:
# Disable NTP temporarilysystemctl stop chronyd
# Set the time manuallytimedatectl set-time "2025-11-04 14:30:00"
# Re-enable NTPsystemctl start chronydThis 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:
echo "$(date): $(timedatectl | grep 'System clock')" >> /var/log/time-check.logOr put it in a cron job:
0 * * * * root timedatectl | grep -q "synchronized: yes" || echo "ALARM: time not synced" | mail -s "Time Sync Alert" rootBottom line: NTP is doing its job. If it’s not working, your hypervisor is lying about time. Fix that and NTP becomes magic again.