Syncing the System Clock and RTC on Linux
Alongside the system clock the kernel keeps in memory, almost every physical and virtual machine has a battery backed hardware clock that keeps running even while the machine is powered off. Anyone who does not know the difference between the system clock and the hardware clock will sooner or later be puzzled by wrong boot timestamps, mistimed certificate checks, or a dual boot system that shows a different time after every Windows startup. This article explains how hwclock works, how to set the hardware clock correctly, and how to keep it reliably synchronized with the system clock.
Table of contents
- 1. System clock and hardware clock: two separate timekeepers
- 2. The RTC in detail: battery, chip, and resolution
- 3. hwclock in practice: show, systohc, hctosys
- 4. UTC vs. local time in the hardware clock
- 5. Interaction with systemd and timedatectl during boot
- 6. Measuring clock drift and correcting it with adjtime
- 7. The RTC in virtual machines and cloud instances
- 8. Typical symptoms of a wrong hardware clock
- 9. hwclock, timedatectl, and chronyc compared
- 10. Summary
- 11. FAQ
1. System clock and hardware clock: two separate timekeepers
A Linux system practically always has two independent clocks. The system clock is a software counter inside the kernel that gets initialized at boot and then keeps ticking driven by the CPU timer interrupt. The hardware clock, also called RTC or Real Time Clock, is a separate chip on the mainboard with its own battery that keeps counting seconds even while the machine is switched off. The hwclock command is the classic interface between these two worlds and is responsible at boot time for making the system clock take over the value stored in the hardware clock.
The practical difference becomes visible as soon as a machine stays powered off longer than planned, or when the hardware clock battery runs empty. While the system clock starts with an arbitrary, often wrong value on the next boot, the RTC usually provides a usable starting point as long as the battery still works. For servers that validate certificates, correlate logs, or timestamp database transactions, a correctly set hardware clock is therefore not a minor detail but a basic requirement for correct operation right after startup, before network time synchronization can even take effect.
2. The RTC in detail: battery, chip, and resolution
Physically, the RTC consists of a small, extremely power efficient chip, usually based on a quartz oscillator, powered by a coin cell battery or a capacitor. This battery lasts several years depending on the model, which is why a laptop that has been sitting in a drawer for a long time still shows a roughly correct time. On Linux the RTC appears as a device file under /dev/rtc0 and exposes additional information under /sys/class/rtc/rtc0/, including the current value, supported wakeup features, and the name of the underlying driver.
An important difference from the system clock: the hardware clock stores only seconds in many implementations, no sub second resolution. That is unproblematic for a pure boot timestamp, but it makes the RTC unsuitable as the sole time source for applications with millisecond requirements. In addition, practically every quartz oscillator drifts over time, usually within a range of a few seconds per month, depending on temperature and component quality. This drift is the actual reason why combining the RTC with network time makes more sense than relying exclusively on the hardware clock.
# Inspect the RTC device and its capabilities
ls /sys/class/rtc/
cat /sys/class/rtc/rtc0/name
cat /sys/class/rtc/rtc0/since_epoch
# Read the raw RTC time directly from the kernel driver
sudo hwclock --show --verbose
3. hwclock in practice: show, systohc, hctosys
The hwclock tool has three core operations. hwclock --show reads the hardware clock and displays it without changing anything, ideal for a quick diagnosis. hwclock --systohc writes the current system clock into the hardware clock and is typically called after a successful network synchronization so the RTC provides an up to date value on the next cold boot. hwclock --hctosys goes the opposite direction and sets the system clock from the hardware clock, a step systemd already performs automatically at boot, before network services are even started.
In practice you rarely call hwclock manually, because modern distributions automate the process through systemd services. Still, the manual invocation is indispensable for diagnostics, for example to check whether there is a discrepancy between the system clock and the hardware clock, or to deliberately reset the hardware clock after a BIOS update or battery replacement. It is important to never call hwclock in parallel with a running NTP daemon like chrony without briefly pausing it, since otherwise two processes try to correct the same clock at once.
# Show the current hardware clock value without changing anything
sudo hwclock --show
# Write the (already synced) system clock into the hardware clock
sudo hwclock --systohc
# Set the system clock from the hardware clock (normally done at boot)
sudo hwclock --hctosys
# Manually set a specific hardware clock value (rare, for recovery only)
sudo hwclock --set --date="2026-07-30 14:32:00"
4. UTC vs. local time in the hardware clock
One of the most common sources of confusion is whether the hardware clock stores UTC or local time. Linux distributions default to assuming UTC in the RTC, which is the right choice for several reasons: timezone changes and daylight saving transitions then only affect how the system clock interprets the time, not the raw values stored in the hardware clock itself. Windows, on the other hand, historically stores local time in the RTC, which leads to a shift of several hours on dual boot systems whenever you switch between the two operating systems.
The command timedatectl set-local-rtc 1 switches Linux to local RTC time, which can be a practical solution on pure dual boot desktops but should absolutely be avoided on servers. Servers should consistently stay at timedatectl set-local-rtc 0, meaning UTC in the hardware clock, because that is the only setting that works without additional pitfalls with automated deployments, container hosts, and virtual machines. The setting itself lives in the file /etc/adjtime, which besides the RTC mode also stores the most recently measured drift rate.
# /etc/adjtime — persisted state used by hwclock
# Line 1: measured drift factor (seconds per day) and last calibration timestamp
# Line 2: last time hwclock was called
# Line 3: RTC mode, either UTC or LOCAL
0.123456 1785500000 0.500000
1785500000
UTC
5. Interaction with systemd and timedatectl during boot
On systemd systems, either systemd-timesyncd or chrony implicitly takes over most of the classic hwclock tasks at boot. Right after the kernel starts, systemd reads the hardware clock, sets the system clock from it, and then starts network time synchronization. Once that succeeded, systemd can be configured to periodically write the new, precise time back into the hardware clock, so a later cold boot benefits from a reasonably accurate starting value, even if the network is briefly unreachable at the next boot.
The timedatectl command is the modern, systemd native view of both clocks at once and shows in its output both the local time and the RTC status, plus whether time is synchronized via NTP. Anyone who carries over old init scripts or cron based hwclock calls from pre systemd times into a current system risks race conditions with the built in systemd mechanisms. On modern distributions it is almost always enough to set timedatectl set-ntp true and let chronyd handle fine grained synchronization as well as periodically updating the hardware clock.
6. Measuring clock drift and correcting it with adjtime
Every quartz oscillator has an individual, usually temperature dependent deviation from its nominal frequency, referred to as drift. The kernel measures this drift automatically once the hardware clock is regularly compared against an accurate external source, and stores the determined rate in seconds per day in /etc/adjtime. hwclock then uses this stored rate to pre correct the RTC between two synchronizations, so the deviation is practically unnoticeable in day to day operation.
In practice, checking the drift value is especially worthwhile on older server hardware or virtual machines with an emulated RTC, where unusually high drift values can point to an underlying problem in the hypervisor. A drift of more than a few seconds per day is a warning sign and justifies a closer look at the virtualization layer, since a working network time synchronization masks the underlying drift but does not fix the actual problem at the hypervisor level.
7. The RTC in virtual machines and cloud instances
Virtual machines do not have a real, battery backed hardware clock, but rather an RTC emulated by the hypervisor. KVM, VMware, and most cloud platforms mimic the behavior of a physical clock but additionally couple its accuracy to the timing of the host system. This leads to a well known phenomenon: under high host load, or after restoring a snapshot, the emulated time of the guest VM can drift noticeably from the real time, sometimes by several minutes, until a network synchronization corrects the error.
For cloud instances it is therefore recommended to run chronyd with short poll intervals and, where available, use the internal time source provided by the cloud vendor instead of relying exclusively on public NTP pools. After restoring a snapshot or after a live migration, an immediate, forced time correction makes sense, for example with chronyc makestep, so applications do not start with an outdated clock while the regular, gradual correction is still in progress.
# Force an immediate time step instead of gradual slewing (use after snapshot restore)
sudo chronyc makestep
# Check current offset and whether the system is considered synchronized
chronyc tracking
# On cloud VMs: verify which RTC device is actually exposed to the guest
dmesg | grep -i rtc
8. Typical symptoms of a wrong hardware clock
An incorrectly set or drifting hardware clock rarely shows up directly, but rather through symptoms elsewhere. TLS connections fail with certificate errors because the validity window of a certificate is barely missed. Log entries from different systems can no longer be correlated correctly because timestamps are systematically shifted. Scheduled backups run at the wrong time or overlap because cron jobs are triggered based on a wrong system time before network time synchronization has even taken effect.
The first diagnostic step for such symptoms is always the same: compare hwclock --show with date and check whether both values differ significantly. A deviation in the range of minutes or hours points to an empty RTC battery, a wrong UTC or local time mode, or a failed boot sync. A deviation in the range of seconds that repeats regularly is usually normal drift, which is continuously corrected anyway by a working network time synchronization and does not require manual intervention.
9. hwclock, timedatectl, and chronyc compared
All three tools relate to time on Linux but cover different layers and do not exclude each other. Knowing which tool is responsible for which task saves a lot of misdiagnosis and unnecessary manual intervention in systems that actually already work correctly on their own.
| Tool | Responsible for | Typical use | Automated? |
|---|---|---|---|
| hwclock | Direct access to the RTC | Diagnosis, manual correction, recovery | No |
| timedatectl | Timezone, NTP status, RTC mode | Status queries, timezone changes | Partially |
| chronyc | Network time synchronization | Ongoing offset correction, monitoring | Yes |
| adjtime file | Stored drift rate and RTC mode | Automatically maintained by hwclock | Yes |
Usually the tools work together automatically: chronyd keeps the system clock precise, systemd ensures the initial synchronization with the hardware clock at boot, and hwclock remains in the background as a manual diagnostic and recovery tool. Only in special cases like dual boot systems, old init systems, or hardware without a working RTC battery do you actually have to intervene manually and understand the relationships between the three layers in detail.
Mironsoft
Linux server administration and operational practice for hosting environments
Want your servers' time issues reliably solved?
We check the hardware clock, system clock, and time synchronization on your servers, identify drift problems, and set up a robust configuration for boot timestamps, certificate checks, and log correlation.
Time audit
Systematically check RTC mode, drift rate, and synchronization status
Configuration
Cleanly set up UTC mode, chrony fine tuning, and boot ordering
Monitoring
Build alerting for drift and synchronization failures
10. Summary
The hardware clock and the system clock are two separate timekeepers, connected through hwclock at boot and afterward through systemd. The RTC keeps running on battery power while the machine is off, but it drifts over time and only offers second level resolution. UTC is the correct mode for the hardware clock on servers, local time does not belong there and should at most be used on dual boot desktops. The drift rate is automatically stored in /etc/adjtime and used by hwclock for pre correction.
In virtual machines the RTC is emulated and can deviate noticeably for a short time after snapshots or migrations, which is why a forced time step with chronyc makestep makes sense. Symptoms of a wrong hardware clock usually show up indirectly, through certificate errors, shifted logs, or wrongly triggered cron jobs, not through an obvious error message. Anyone who understands hwclock, timedatectl, and chronyc as complementary tools operating at different layers diagnoses time problems much faster.
Hardware clock and hwclock on Linux, the essentials at a glance
Two separate clocks
System clock in kernel RAM, hardware clock battery backed on the mainboard. hwclock connects both at boot.
Always UTC on servers
timedatectl set-local-rtc 0 keeps the RTC in UTC. Local time only belongs in the hardware clock on dual boot desktops.
Drift is normal
Every quartz drifts slightly. /etc/adjtime stores the rate, chrony compensates the rest continuously.
VMs need makestep
After snapshot restore or migration use chronyc makestep instead of waiting for slow slewing.