After a reboot, your Linux VM can show a time that is hours off even though you fixed it last session. That usually means something in the boot chain overwrote a good clock: the virtual RTC started wrong, chrony stepped to a bad NTP peer, or the hypervisor injected host time while chrony was also adjusting the clock.
This guide walks through how Linux sets time on boot, how to read timedatectl and chronyc, and the fixes that stick. The lab example came from a Rocky Linux host where LDAP client setup and a separate lab script both touched the same machine; the LDAP configuration was unrelated, but a server … prefer line added for the lab client made the two changes appear connected. The same checks apply on Ubuntu, Debian, and other chrony-based distros.
Tested on: Rocky Linux 10.2; chrony 4.8; VirtualBox VM with host-only and NAT interfaces.
For day-to-day NTP tuning, see Chrony force sync in Linux.
Quick answer
Run timedatectl status and chronyc tracking right after a bad reboot. Then check journalctl -u chronyd -b on the RHEL family or journalctl -u chrony -b on Ubuntu and Debian for Selected source and System clock was stepped. If chrony selected a lab host that was given prefer, or that host was serving wrong time through local stratum 10 orphan with no valid upstream, remove or redesign those directives, keep rtcsync in /etc/chrony.conf, set a trusted approximate time once if needed, and reboot to confirm the virtual RTC holds the fix.
How Linux sets time on boot
The sequence matters when you are debugging a jump:
- The hypervisor exposes a virtual RTC value to the guest.
- The kernel sets the initial system clock from that RTC (UTC when
RTC in local TZ: no). - Programs interpret and display the system clock using the configured timezone, normally through
/etc/localtime. Changing the timezone does not change the underlying UTC system time. chronyd(orsystemd-timesyncdon some images) polls NTP servers and may step or slew the clock.
systemd-timedated is the D-Bus service behind timedatectl; it is not a separate boot stage that applies the timezone. If step 4 selects a peer that is itself wrong, the VM can move to that peer's time even when the RTC was closer to reality. That is what happened in the lab trace below.
Diagnose right after a bad reboot
Start with the clock state. You want timezone, whether NTP is active, and whether the RTC matches UTC.
timedatectl statusSample output:
Local time: Sun 2026-07-19 11:05:19 IST
Universal time: Sun 2026-07-19 05:35:19 UTC
RTC time: Sun 2026-07-19 05:35:20
Time zone: Asia/Kolkata (IST, +0530)
System clock synchronized: no
NTP service: active
RTC in local TZ: noRTC in local TZ: no is what you want on Linux: the virtual RTC stores UTC, and the OS applies the timezone for display.
Confirm only the intended time service is running. Both chronyd and systemd-timesyncd should not normally be active together.
# RHEL, Rocky Linux, AlmaLinux, Oracle Linux
systemctl is-active chronyd systemd-timesyncd
# Ubuntu and Debian
systemctl is-active chrony systemd-timesyncdCommands below use chronyd on the RHEL family. Replace the systemd unit name with chrony on Ubuntu and Debian.
Sample output on a chrony-based RHEL or Rocky system:
active
inactiveIf both show active, stop and disable the one you do not want before continuing.
Next, see which NTP source chrony considers best and whether it is actually synchronized.
chronyc trackingSample output when no upstream NTP is reachable (common in locked-down lab networks):
Reference ID : 00000000 ()
Stratum : 0
Leap status : Not synchronisedA healthy client instead shows a real reference (for example time.cloudflare.com) and Leap status: Normal.
List every configured source. In chronyc sources -v, ^* marks the selected NTP server, # can mark a reference clock such as a local source, and ^? means unreachable or not yet usable.
chronyc sources -vSample output:
^? time.cloudflare.com 0 6 0 - +0ns[ +0ns] +/- 0ns
^? time3.google.com 0 6 0 - +0ns[ +0ns] +/- 0nsAll public pools marked ^? while a LAN IP is selected is a strong hint the VM is syncing only to a local peer.
For more detail beyond the source list:
chronyc activity
chronyc sourcestatschronyc activity shows how many sources are online, offline, or unresolved. chronyc sourcestats shows whether chronyd has accumulated usable measurements and the estimated offset and stability of each source.
The boot log usually states the offset chrony applied and which source it chose.
# RHEL family
journalctl -u chronyd -b --no-pager | grep -iE 'Selected source|step|wrong'
# Ubuntu and Debian
journalctl -u chrony -b --no-pager | grep -iE 'Selected source|step|wrong'Sample output from the lab incident (trimmed):
Jul 19 10:57:18 ldap1.example.com chronyd[2258]: Selected source 192.168.56.109
Jul 19 10:57:18 ldap1.example.com chronyd[2258]: System clock wrong by -31673.864517 seconds
Jul 19 02:09:24 ldap1.example.com chronyd[2258]: System clock was stepped by -31673.864517 secondsRoughly 31 673 seconds is about 8.8 hours. The RTC had been near 10:57 IST after boot; chrony stepped the clock back to match 192.168.56.109 (a lab LDAP client), which was still on 02:09 IST.
Compare the system clock to the RTC. The Universal time and RTC time lines from timedatectl status above already give an unambiguous UTC-side comparison when RTC in local TZ: no.
You can also compare local-time displays directly:
date '+%Y-%m-%d %H:%M:%S %z'
sudo hwclock --show --utcSample output:
2026-07-19 11:05:19 +0530
2026-07-19 11:05:20.321044+05:30Both commands display local time here. The --utc option tells hwclock that the underlying RTC value uses the UTC timescale; it does not change the displayed timezone. The two timestamps should be close when rtcsync is working and you shut down cleanly.
Common causes
Use the Fix column to jump to the section that addresses each cause.
| Cause | Why it happens | Fix |
|---|---|---|
prefer on a lab NTP server |
prefer gives that source priority over other selectable sources. If the preferred lab server is reachable and chrony considers it valid, it may be selected instead of public sources. It does not make an unreachable source usable. |
Review chrony directives |
local stratum 10 orphan on a lab VM |
Legitimate for an isolated or redundant local NTP design. In the lab incident, this configuration allowed chrony to provide local time when no valid upstream was available. Because that VM's clock was wrong, clients that accepted it as their usable or preferred source inherited the incorrect time. | Review chrony directives |
| Stale or incorrectly interpreted RTC | RTC was not updated, the VM restored an old clock value, or RTC local/UTC interpretation is inconsistent | Confirm rtcsync and timezone, set the clock once |
| Outbound NTP blocked | Firewall or network blocks UDP 123 to the internet | Diagnose sources, Troubleshooting |
| VirtualBox or VMware time sync vs chrony | Hypervisor and chrony both adjust the clock | VirtualBox and VMware guests |
timedatectl set-local-rtc 1 |
Windows dual-boot setups; Linux then treats RTC as local time | Confirm rtcsync and timezone |
Fix permanently
1. Review chrony directives
On the affected VM, open /etc/chrony.conf (Ubuntu and Debian: /etc/chrony/chrony.conf) and review lab-only server lines, prefer, and local / orphan settings.
The prefer option gives a source priority over other selectable non-preferred sources. It does not make an unreachable or rejected source usable, and it does not bypass chrony's source-validity checks. If a preferred lab server is reachable and considered valid, chrony may keep selecting it even when public pools are also configured.
Bad pattern that caused the lab jump:
# BEGIN lab local NTP
server 192.168.56.109 iburst prefer
# END lab local NTPOn the peer VM, this configuration allowed chrony to provide local time when no valid upstream source was available:
allow 192.168.56.0/24
local stratum 10 orphanlocal stratum 10 orphan is not inherently wrong. It is used for intentionally isolated networks and for coordinating multiple local time servers. The orphan option helps redundant local servers agree when no external source is selectable; chrony 4.8 ignores orphan sources while another source remains selectable, and orphan mode has a default unsynchronized activation delay. Remove local or orphan configurations unless the machine is intentionally acting as a time server for an isolated or redundant NTP network.
Keep a standard client configuration when public or internal NTP is available. Retain your distribution's default pool, or point at verified internal servers:
# Retain your distribution's default pool, or use a verified internal source
pool 2.rocky.pool.ntp.org iburst
rtcsync
makestep 1.0 3On Ubuntu and Debian, use the pool already defined in /etc/chrony/chrony.conf or under /etc/chrony/sources.d/ rather than copying a Rocky-specific pool name. Keep your distribution's default NTP pool or configure verified internal servers. If you choose Google Public NTP, use only Google's recommended servers instead of combining them with non-smeared NTP sources; Google advises against mixing its leap-smeared service with ordinary NTP around leap seconds.
Restart chrony after edits:
# RHEL family
sudo systemctl restart chronyd
# Ubuntu and Debian
sudo systemctl restart chrony2. Set the clock once when NTP is not yet working
If the system clock is still wrong after fixing chrony sources, set a trusted approximate time manually as a bootstrap step. timedatectl set-time updates both the system clock and the RTC, so a separate hwclock --systohc is normally redundant immediately afterward.
sudo timedatectl set-ntp false
sudo timedatectl set-time '2026-07-19 11:30:00'
sudo timedatectl set-ntp true
# RHEL family
sudo systemctl restart chronyd
# Ubuntu and Debian
sudo systemctl restart chronyThe timestamp is interpreted in the currently configured local timezone. Replace the example with a time you trust from the hypervisor console, host clock, or hardware management interface.
When NTP sources are reachable again, ask chrony for fresh measurements before stepping:
sudo chronyc burst 4/4
sleep 10
sudo chronyc makestepburst 4/4 asks chronyd to collect fresh measurements over a short period. Allow the burst to complete before running makestep, which immediately applies the correction chronyd has calculated. makestep does not make an unreachable server reachable or obtain a fresh measurement by itself.
Sample output from makestep:
200 OKThen confirm synchronization:
chronyc trackingSample output on a healthy client:
Reference ID : A29FC87B (time.cloudflare.com)
Stratum : 4
System time : 0.000012345 seconds fast of NTP time
Leap status : NormalFor a one-shot sync with the daemon stopped, use chronyd -q instead, as described in Chrony force sync in Linux.
3. Confirm rtcsync and timezone
Ensure rtcsync is present in chrony.conf (shown above). It lets the kernel copy accurate system time to the RTC about every 11 minutes while chrony is running.
Verify timezone and RTC mode:
timedatectl show | grep -E 'Timezone|LocalRTC|NTP'Sample output:
Timezone=Asia/Kolkata
LocalRTC=no
NTP=yesIf LocalRTC=yes and you do not deliberately require a local-time RTC, return it to UTC with sudo timedatectl set-local-rtc 0. On a Windows dual-boot system, configure both operating systems to use the same RTC convention before changing this setting.
4. VirtualBox and VMware guests
On VirtualBox, Guest Additions can push host time into the guest on a schedule. That conflicts with chrony when both try to control the clock.
If Guest Additions is installed, decide whether VirtualBox or chrony will be the primary time authority. When chrony should control time, disable Guest Additions host-time synchronization on the host while the VM is powered off:
VBoxManage setextradata "VM name" "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled" 1Installing Guest Additions alone does not prevent the conflict; you must choose one authority and disable the other mechanism when needed.
On VMware, install open-vm-tools and review VMware documentation for time sync versus NTP on that platform.
5. Reboot test
Reboot the VM, log in, and run:
date
timedatectl status
# RHEL family
journalctl -u chronyd -b --no-pager | grep -iE 'step|Selected source' || echo "No clock step on this boot"
# Ubuntu and Debian
journalctl -u chrony -b --no-pager | grep -iE 'step|Selected source' || echo "No clock step on this boot"You want no multi-hour step lines and a stable date compared to before reboot.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Clock jumps hours backward on boot | prefer on a lab NTP server with stale time, or clients accepting a wrong local server |
Remove or redesign prefer and local/orphan server roles; sync from public pools or a verified internal stratum peer |
chronyc sources shows all ^? |
Outbound UDP 123 blocked, routing trouble, or server reachability problems | Check chronyc activity, chronyc sources -v, and journalctl -u chronyd -b (RHEL family) or journalctl -u chrony -b (Ubuntu/Debian). For packet-level confirmation: sudo tcpdump -ni any udp port 123 and look for requests without replies |
| Correct until reboot, then wrong | Stale RTC, restored VM state, or RTC local/UTC mismatch | Keep rtcsync; set a trusted time once with timedatectl set-time; avoid custom shutdown scripts that run hwclock --hctosys at the wrong stage |
| Time good after suspend, bad after restore | Hypervisor injected host time | Disable guest time sync or tune chrony; see VM docs |
| Offset equals timezone (e.g. 5h 30m) | RTC stored as local time | timedatectl set-local-rtc 0 |
chronyc reports 506 Cannot talk to daemon |
chrony service not running | RHEL family: sudo systemctl enable --now chronyd. Ubuntu/Debian: sudo systemctl enable --now chrony |
References
- chrony documentation
- chrony.conf(5) — prefer, local, and orphan options (chrony 4.8)
- chrony FAQ — RTC and rtcsync
- timedatectl man page
- Oracle VirtualBox User Manual — Fine Tuning Timers and Time Synchronization
- Google Public NTP — leap smearing and mixing sources
Summary
Persistent wrong time after reboot usually points to the initial virtual RTC value or to a service that adjusts the clock during or shortly after boot. Read journalctl -u chronyd -b on the RHEL family or journalctl -u chrony -b on Ubuntu and Debian first, review whether prefer gave priority to an unsuitable but selectable lab source, and remove local or orphan configurations unless the host is intentionally serving an isolated NTP network. Keep rtcsync, set a trusted approximate time once when NTP is still unavailable, and confirm with a clean reboot.

