Stage 1: The clock nobody is keeping
Run the hinge here, and then run the tool that is supposed to read what it finds:
$ ls -l /sys/class/rtc/
ls: cannot access '/sys/class/rtc/': No such file or directory
$ hwclock --verbose --show
hwclock: Cannot access the Hardware Clock via any known method.
System Time: 1788538767.232614
Trying to open: /dev/rtc0
Trying to open: /dev/rtc
Trying to open: /dev/misc/rtc
No usable clock interface found.Three device paths, tried in order, all absent. This is not an exotic machine — it is a microVM, and the same is true of most ARM single-board computers without an add-on module, of every container, and of a great many cloud instances. “Every machine has a battery-backed hardware clock” is a sentence from the era when every machine was a PC under a desk.
So where did the number come from? The kernel says so at boot, and only in the kernel log:
[ 0.000026] clocksource: kvm-clock: mask: 0xffffffffffffffff ...
[ 0.171881] clocksource: refined-jiffies: ...
[ 0.465869] clocksource: tsc-early: ...
[ 0.834216] clocksource: Switched to clocksource kvm-clock
[ 0.945944] clocksource: Switched to clocksource tsc
[ 0.947587] platform rtc_cmos: registered platform RTC device (no PNP device found)The kernel changed its mind about which counter to trust twice before userspace existed, and the last line is it registering an RTC driver that then found no device. Nothing in timedatectl, date or uptime mentions any of this. It is in dmesg and nowhere else, which is why almost nobody has ever seen the sentence that describes their own machine.
None of these counters knows the date. A clocksource counts; it does not remember. On a machine with no RTC the wall clock’s starting value comes from somewhere else entirely — the hypervisor handing one over, a filesystem superblock’s last-mounted time acting as a floor so that the clock is at least not before the last boot, or a file that something wrote on shutdown. And if none of those is present, the first number the machine believes is whatever the first thing to speak to it says, which is stage 3’s subject.
From Power-On to a Login Prompt follows eight handoffs from firmware to a shell and never mentions the clock once. It tells you to read journalctl -b -1 and does not say where the timestamps on those lines came from. This stage is that page’s missing half.
Verify: ls -l /sys/class/rtc/, then dmesg | grep -i clocksource. If the first is empty and the second shows a switch, you now know two things about this machine that no status command will ever tell you.
Stage 2: Which clock, and who may move it
This is the vocabulary stage, and everything after it is a consumer of it. There is not one clock. There are several, they are separate objects in the kernel, and the useful column is not what they measure but who is allowed to move them.
| Clock | What it counts | Who moves it |
|---|---|---|
CLOCK_REALTIME | seconds since 1970, rendered as the date | anything holding CAP_SYS_TIME — your NTP daemon, your init system, root with date -s, and a leap second |
CLOCK_MONOTONIC | time since an arbitrary point, never going backwards | nobody. It is slewed in rate along with REALTIME, but never stepped |
CLOCK_MONOTONIC_RAW | the same, without even the rate correction | nobody, and nothing |
CLOCK_BOOTTIME | monotonic, but it also counts time spent suspended | nobody — but a time namespace can offset what a container sees |
CLOCK_TAI | REALTIME plus a leap-second offset held in the kernel | whatever last wrote that offset, which is usually nothing at all |
CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_COARSE | the same two, to the last tick rather than the nanosecond | as their non-coarse versions |
Move the wall clock back an hour and read all of them at once. Only two entries change:
before after -3600 s
CLOCK_REALTIME 16:23:40 15:23:40 <- moved
CLOCK_TAI 16:23:40 15:23:40 <- moved (it is REALTIME plus an offset)
CLOCK_MONOTONIC 1240.117 1240.121
CLOCK_MONOTONIC_RAW 1240.115 1240.119
CLOCK_BOOTTIME 1240.117 1240.121
CLOCK_PROCESS_CPUTIME_ID 0.002 0.002That is the distinction the corpus has lost, and it is why a page can call UTC “monotonic” without anybody noticing. Across all one hundred and forty-two pages published on this site before this one, the word monotonic appeared on three — correctly on two, and on the timedatectl page describing UTC, where it is wrong. The distinction itself had never been explained anywhere here.
Who may move it
Reading a clock needs nothing. Writing one needs CAP_SYS_TIME, and the split is exact:
as uid 65534: clock_settime(CLOCK_REALTIME) -> EPERM
as root: clock_settime(CLOCK_REALTIME) -> OK
as uid 65534: ntp_adjtime(read-only) -> OKWhich is the whole reason date -s behaves differently in a container: the capability is usually dropped there, so it fails; in a privileged container it is present, and what it then moves is the host’s clock, because there is only the one. The absence of CAP_SYS_TIME from a page about time is diagnostic on its own.
A container has no wall clock of its own
This site already got this right, in Containers, All the Way Down:
The time namespace is the one to be careful about: it virtualises CLOCK_MONOTONIC and CLOCK_BOOTTIME and their variants, and explicitly does not virtualise CLOCK_REALTIME … You cannot give a container a different wall-clock date this way.
$ unshare --time --fork --boottime 86400 -- sh -c 'cut -d" " -f1 /proc/uptime'
86970.09 # the host, at the same instant: 570.09
$ unshare --time --fork -- sh -c 'echo "0 100 0" > /proc/self/timens_offsets'
WRITE FAILED for clock id 0 (CLOCK_REALTIME)A container’s uptime is whatever its namespace was told to say. Its wall clock is the host’s, byte for byte, and cannot be otherwise. So the timedatectl page’s row — “container time differs from the host … set TZ in the container” — is one clause short in a way that matters. If the time looks different, only the rendering can be, and TZ is the right answer. If it genuinely is different, you are looking at the host’s clock and the container is innocent. The row treats a rendering problem and a clock problem as one problem, and they have different owners.
And CLOCK_TAI is not atomic time
It is CLOCK_REALTIME plus an offset that lives in the kernel and is only ever there because something put it there. On the machine here it read tai=0 at baseline — meaning CLOCK_TAI was exactly UTC, and therefore thirty-seven seconds from the thing it claims to be. After the leap experiment it read tai=1. Nothing has ever set it to 37. chrony can load a real leap table (leapsectz right/UTC, which resolves to a table with twenty-seven entries on a stock Ubuntu), while systemd-timesyncd speaks SNTP, which carries no leap table at all — so on a machine running only timesyncd, CLOCK_TAI is silently UTC.
Verify: read CLOCK_REALTIME and CLOCK_MONOTONIC once each, note the difference, and read them again tomorrow. If the difference has changed, somebody moved the wall clock and this page’s subject is your subject.
Stage 3: Discipline — step, slew, and the ceilings
A time daemon has two ways to fix a wrong clock. It can step it — write a new value, and the clock jumps — or it can slew it, telling the kernel to run the clock slightly fast or slow until the error is gone. “Let NTP correct it gradually rather than stepping it yourself” is good advice given without the number, and the number is the entire point.
The kernel’s own phase-locked loop will not exceed 500 parts per million. Ask for more and it keeps 500:
requested freq = 1000 ppm -> kernel reports 500.000 ppm
requested freq = 400 ppm -> kernel reports 400.000 ppmReal daemons escape that ceiling through a second knob, ADJ_TICK, which changes the length of a tick rather than the frequency correction. Its range on this kernel is 9000 to 11000 microseconds — ten per cent either way — and 12000 is rejected. chrony’s documented default ceiling, built on that, is 83 333.333 ppm, one twelfth. Which turns the advice into arithmetic:
| Error to remove | Kernel PLL alone, 500 ppm | chronyd at its default ceiling |
|---|---|---|
| 0.5 s | 17 minutes | 6 seconds |
| 1 s | 33 minutes | 12 seconds |
| 60 s | 1.4 days | 12 minutes |
| 1 hour | 83 days | 12 hours |
| 1 day | 5.5 years | 12 days |
An hour of error, corrected the way everyone recommends, takes half a day. During all of it every timestamp the machine writes is wrong by a decreasing amount, and nothing anywhere says so. The clock is not broken and it is not right; it is arriving, and no status command has a word for that state.
Which is why daemons step as well as slew, and why the condition under which they do it is worth reading rather than assuming. Ubuntu ships this in its default chrony.conf:
makestep 1 3Step the clock if the error exceeds one second — but only for the first three clock updates after the daemon starts. On the fourth and afterwards, an hour of error means twelve hours of slewing. The number most people remember from that line is the 1; the one that decides how your afternoon goes is the 3.
There is a separate clamp on how large a single phase adjustment may be, which this page did not measure and will not put a number on. The frequency clamp above was measured; do not let the two be confused, and be suspicious of any page that quotes one as though it were the other.
Verify: read your own makestep line, and know which of its two numbers you are relying on. Then ask the daemon what it thinks it is doing — chronyc tracking, or timedatectl show-timesync --all under timesyncd — rather than asking the machine whether it is happy, which is stage 4.
Stage 4: The latch that says it worked
Everyone’s first check is timedatectl, and the line everyone reads is this one:
System clock synchronized: yesIt is not a statement about whether a daemon is running, or whether it succeeded, or whether it has ever spoken to anything. systemd calls adjtimex() and returns whether the kernel’s maxerror field is below sixteen seconds — and its source says in a comment that it deliberately ignores the kernel’s own STA_UNSYNC flag, which is the flag whose entire job is to say “nobody is disciplining this clock”.
So what happens to maxerror when the daemon dies? The kernel grows it on its own, at the maximum frequency error it is willing to assume, and nothing else touches it. Here is that growth, measured by behaving like a time daemon — clearing the flag, zeroing the field — and then not existing any more:
immediately after 'sync' ret=0(TIME_OK) status=0x0001 [synced] maxerror=0 us
+10s (daemon now DEAD) ret=0(TIME_OK) status=0x0001 [synced] maxerror=5000 us
+20s (daemon now DEAD) ret=0(TIME_OK) status=0x0001 [synced] maxerror=10000 us
+30s (daemon now DEAD) ret=0(TIME_OK) status=0x0001 [synced] maxerror=15000 usExactly 500 microseconds per second, exactly as the kernel source says it should. Which makes the arithmetic short: 16 000 000 ÷ 500 = 32 000 seconds = 8 hours, 53 minutes and 20 seconds.
Kill your NTP daemon at nine in the morning and timedatectl will print System clock synchronized: yes until nearly six in the evening. It is not lying, exactly — it is answering a narrower question than the one you asked, and the narrow question has a latch on it.
The other direction is worse and takes one syscall. Anything with CAP_SYS_TIME can set maxerror to zero and clear the flag, and the line then says yes on a machine that has never spoken to a time server in its life. The program that did it for the transcript above is fifteen lines long, and it is the same program a badly written provisioning script is.
Verify: ask the daemon, not the flag. chronyc tracking names an upstream server and a stratum, and both of those belong to somebody who is not you. If the daemon is not running, that command fails — which is the honest answer, and the reason it is a better check than a line that keeps its composure for nearly nine hours.
Stage 5: What a step does to what is already running
This is the event the whole page is about, and it is the one the corpus has no verb for. A process is sleeping, or waiting on a timer, and the wall clock moves underneath it. What is told?
Five ways of waiting, each in its own process, with the clock stepped an hour backwards a second and a half in:
1. sleep(3) -> slept 3.000 s
2. clock_nanosleep(CLOCK_REALTIME, rel 3s) -> slept 3.000 s
3. clock_nanosleep(CLOCK_REALTIME, ABS now+3s) -> STILL BLOCKED after 12 s, killed
4. timerfd CLOCK_REALTIME ABS now+30s -> fired after 8.008 s, read ok
5. timerfd + TFD_TIMER_CANCEL_ON_SET -> fired after 1.501 s, read = -1 (ECANCELED)And the same five with the clock stepped an hour forwards:
1. sleep(3) -> slept 3.000 s
2. clock_nanosleep(CLOCK_REALTIME, rel 3s) -> slept 3.000 s
3. clock_nanosleep(CLOCK_REALTIME, ABS now+3s) -> woke after 1.501 s
4. timerfd CLOCK_REALTIME ABS now+30s -> fired after 1.500 s, read ok
5. timerfd + TFD_TIMER_CANCEL_ON_SET -> fired after 1.501 s, read = -1 (ECANCELED)Three things to take from that. Relative sleeps run on a monotonic base and never see the step at all — three seconds is three seconds whatever the date does, which is why most code survives this without knowing. Absolute waits on CLOCK_REALTIME fire an hour early or wait out an extra hour, silently and with a successful return. Case 3 backwards is the one to look at twice: a program asked to wake in three seconds, and at twelve seconds it had to be killed. Nothing failed. It was still waiting for a moment that had been moved into the future.
And exactly one of the five was told. TFD_TIMER_CANCEL_ON_SET is the only mechanism on the machine that says “the clock you were waiting against has been set” — and the way it says it is by making your read() fail with ECANCELED. That is the entire notification. There is no signal, no log line, no counter, and nothing in dmesg. When the wall clock moves, nothing writes it down.
And two answers to “when did this machine boot?”
The machine keeps that answer twice, and only one of them is recomputed from the wall clock:
before uptime -s -> 2026-09-04 16:00:21 who -b -> Sep 4 16:07
clock -2h uptime -s -> 2026-09-04 14:00:21 who -b -> Sep 4 16:07
restored uptime -s -> 2026-09-04 16:00:21 who -b -> Sep 4 16:07uptime -s is derived from /proc/stat‘s btime, which the kernel recomputes as “now minus how long I have been up” — so it moves with the clock, and a machine whose clock was corrected reports a boot time that never happened. who -b reads a record written into a file at boot and never touched again, so it stays put and is wrong in the other direction. Neither is a lie and they cannot both be right.
Verify: compare uptime -s against who -b on a machine that has been up a while. If they disagree, the gap between them is roughly how far the clock has been moved since boot — which is the only place on the machine that number is recorded, and it is recorded by accident.
Stage 6: What the log recorded
A timestamp is not a fact about when something happened. It is a receipt: a record of what some clock said when some process asked. The journal is unusually honest about this, because it keeps two of them for every entry — the sending process’s own claim, and journald’s reading when it received the line. Both are real, both are in the entry, and the second is the only one journald can vouch for. Ordinary output shows you one of them and does not say which.
Which is the missing clause in this site’s own journalctl row — “timestamps disagree with a remote system: use --utc on both”. --utc is a renderer. If the two machines’ clocks differ by four minutes, --utc on both leaves them differing by four minutes, in a format that now looks authoritative on both ends. The row should route to a clock comparison, and the honest version of that comparison is not on either machine — it is stage 7.
The zone is cached, but not when you think
The timedatectl page is right that long-running services keep the old zone after you change it, and wrong about why — it says they “cached it at startup”. They cache it at the first call to tzset(), which in practice means the first time they format a time. Three processes, one zone change:
| Process | After /etc/localtime changes |
|---|---|
| Already formatting times before the change | keeps the old zone, across two further changes |
Started with TZ= in its environment | ignores /etc/localtime entirely, permanently |
| Started before the change, but had not yet formatted a time | picks up the new zone |
“Cached at startup” predicts that the third case is impossible. It is not: two workers forked from the same unit in the same second can render different zones for the rest of their lives, decided by which of them happened to log first. That is a mechanism worth having right, because readers use mechanisms to predict cases the page did not cover.
One place a 64-bit machine still keeps 32-bit seconds
On a fully 64-bit build, time_t, timespec.tv_sec and a file’s st_mtim.tv_sec are all eight bytes. Three time fields are four, on purpose, and glibc’s own header says why:
/* The ut_session and ut_tv fields must be the same size when compiled
32- and 64-bit. This allows data files and shared memory to be
shared between 32- and 64-bit applications. */
utmp, utmpx and lastlog — which is to say what last, who, w and lastlog read — carry a 32-bit seconds field on a machine where nothing else does. This page measured the widths and stops there: what each of those tools does on the far side of 2038 was not tested here, and a page that tells you would need to have built the file and read it back.
Verify: take one log line you care about and ask what it is a receipt for — which clock, read by which process, at a moment nobody recorded the provenance of. If you cannot answer, that is the normal case, and it is why the next stage exists.
Stage 7: Somebody else’s tolerance
Every page on this subject ends when the clock is right. That is where the mechanism ends. The reader’s problem ends somewhere else: when somebody else accepts a timestamp their machine produced — and the tolerance that decides it is chosen by the other party, is different for every protocol, and is not readable from the machine being judged.
Here is the strictest of them, measured by moving a verifier’s clock across a certificate’s notBefore one second at a time:
verifier clock at notBefore -3600s : error 9 ... certificate is not yet valid
verifier clock at notBefore -60s : error 9 ... certificate is not yet valid
verifier clock at notBefore -1s : error 9 ... certificate is not yet valid
verifier clock at notBefore +0s : c.pem: OKZero tolerance. One second early is a hard failure — and the error names the certificate rather than the clock that judged it. That is the sentence to keep, because it is how this whole class of problem presents: as a fault in the thing being examined, reported by a machine whose own clock is not in the message.
Against that, what everybody else allows:
| Who is judging | Tolerance | Where the number lives |
|---|---|---|
X.509 / TLS notBefore, notAfter | none | RFC 5280 — a hard window |
| Kerberos v5 | 300 s | clockskew in the KDC’s krb5.conf |
| TOTP | one 30-second step, usually ±1 | RFC 6238, and the verifier’s choice |
| AWS SigV4 request signing | 300 s | the service, as RequestTimeTooSkewed |
JWT exp / nbf | whatever leeway the library chose | somebody else’s dependency |
SAML NotOnOrAfter | whatever the identity provider set | their configuration |
| Raft and lease-based systems | none — deliberately not on the wall clock | the design, on purpose |
Not one of those numbers is printed anywhere on the machine being judged. There is no file to read, no command to run, no status line. You can make your clock as correct as you like and still not know whether the party you are about to talk to will accept it, because acceptance is not a property of your clock — it is a property of the gap between two clocks, and you can only see one end.
The oldest tool on the system has been saying so for decades, and says it about your own machine judging your own files:
make: Warning: File 'in' has modification time 7199 s in the future
make: warning: Clock skew detected. Your build may be incomplete.make compares an mtime it read against a clock it read, and the most it can honestly say is skew — it cannot tell you which of the two is wrong. Neither can anything else. And it is worth noticing that before this page, the phrase “clock skew” appeared on none of the one hundred and forty-two pages published here, on a site that has a page about certificates, a page about Kubernetes and a page about logs.
Verify: you cannot, from here — and that is the finding. The closest you can get is to ask the party that will judge you what it allows, and to know your own error well enough to compare. Which means stage 4’s honest form: ask the daemon what it thinks your offset is, and treat the number as the thing you are managing.
A worked diagnosis: the certificate from the future
A small VM is rebooted during a maintenance window. It comes back, everything starts, monitoring is green. Some hours later a scheduled job that calls an internal API starts failing, and the error is certificate is not yet valid. The certificate was issued that morning and is fine everywhere else — it validates from a laptop, from the CI runner and from the load balancer. One machine refuses it.
The obvious hypotheses all fail. The chain is complete. The system trust store has the issuer. openssl s_client from the failing box shows the same certificate everyone else is happy with. And the machine’s own status is reassuring:
$ timedatectl | grep -i sync
System clock synchronized: yesRun the hinge:
$ ls -l /sys/class/rtc/
ls: cannot access '/sys/class/rtc/': No such file or directoryNobody kept the time across the reboot. That routes to stage 2 — and from there the descent is two commands. systemctl status chrony shows the daemon failed to start, because a firewall change in the same window closed outbound UDP 123. And the reassuring line is stage 4’s latch: the daemon had run long enough after boot to zero maxerror and then died, so synchronized: yes was true about a field and false about the world, and would have stayed true until nearly the end of the working day.
The clock had come up behind, taken from a value the hypervisor handed over, and had never been corrected. The certificate’s notBefore was that morning; the machine believed it was still the previous evening. From stage 7: the tolerance is zero, and the error names the certificate.
Three things generalise. The hinge answered by naming an owner — nobody — and the whole diagnosis follows from a machine that has no memory of the time. Every observable on that box was true: the chain really was complete, the store really did have the issuer, and the clock really did satisfy the test timedatectl applies. And the moral, which is the sentence this page turns on: when the wall clock moves, nothing writes it down. Every timestamp on the machine is true, and no two of them are necessarily comparable.
Symptoms, and which stage they belong to
| Symptom | Stage | What is actually true |
|---|---|---|
hwclock cannot find a clock at all | 1 | Plenty of machines have no RTC; the first guess came from elsewhere |
| The clock is wrong immediately after a reboot | 1 | Nothing kept it, and it took the first number offered |
| A VM’s time jumps after a live migration | 1, then 3 | The “hardware clock” was a value the host computed |
| An RTC exists and the kernel ignored it | 1, then 2 | hctosys is 0 — the kernel found it and declined to believe it |
date -s fails in a container | 2 | CAP_SYS_TIME is dropped; there is no container wall clock to set |
| A container shows a different time from the host | 2 | Only the rendering can differ; if the clock differs it is the host’s |
CLOCK_TAI is not 37 seconds ahead of UTC | 2 | The offset is whatever last wrote it, and usually nothing has |
| A corrected clock takes hours to become right | 3 | Slewing has a ceiling; an hour of error is about twelve hours |
| chronyd stepped the clock at boot and refuses to now | 3 | makestep 1 3 — the 3 is the number that decides it |
System clock synchronized: yes and the time is wrong | 4 | A latch on maxerror, good for 8 h 53 min 20 s after the daemon dies |
| A timer fired an hour early, and nothing failed | 5 | An absolute CLOCK_REALTIME wait; only TFD_TIMER_CANCEL_ON_SET is told |
| A three-second sleep took twelve seconds and was still going | 5 | Absolute wait, clock stepped backwards, no error anywhere |
uptime -s and who -b disagree | 5 | One is recomputed from the wall clock, one was written at boot |
| Two workers from one unit render different time zones | 6 | The zone is cached at the first tzset(), not at startup |
Two machines’ logs disagree and --utc did not help | 6 | --utc is a renderer; the clocks still differ |
last or who shows an impossible date | 6 | utmp keeps 32-bit seconds on a 64-bit machine, deliberately |
certificate is not yet valid on one machine only | 7 | Zero tolerance, and the error names the certificate |
RequestTimeTooSkewed, or a rejected TOTP code | 7 | Somebody else’s tolerance, not published to you |
Clock skew detected. Your build may be incomplete. | 7 | Two readings compared; neither can be shown to be the wrong one |
Advice that has expired
| Still repeated | What to say now |
|---|---|
ntpdate -u pool.ntp.org | The classic ntp/ntpdate package is gone from Debian and Ubuntu main; the binary is now ntpsec’s wrapper and rejects the old flags outright. Use chronyd -q, or chronyc makestep |
“Install ntpd and edit /etc/ntp.conf“ | Replaced by chrony on the RHEL family since 2014, and by ntpsec or timesyncd on the Debian family |
“hwclock --systohc at shutdown keeps the RTC right” | The kernel writes the RTC periodically while the clock is disciplined, and chrony’s rtcsync — shipped in Ubuntu’s default config — hands it that job. Nothing to do, unless there is no RTC at all |
“System clock synchronized: yes means you are fine” | Never true; measurably wrong for 8 h 53 min 20 s after the daemon dies |
| “Leap seconds are coming — harden for them” | The last was 31 December 2016, and the CGPM has asked for insertions to stop by 2035. They are a historical hazard your code still has to survive, not a scheduled one |
| “The RTC is UTC on Linux and local on Windows, and that is the whole story” | Still true where an RTC exists. Increasingly there is not one — ask /sys/class/rtc/ first |
| “Containers get their own clock” | Time namespaces arrived in Linux 5.6 and cover MONOTONIC and BOOTTIME only. The wall clock is the host’s, always |
| “64-bit fixed the 2038 problem” | Named exceptions rather than general reassurance: utmp, utmpx and lastlog keep 32-bit seconds on purpose |
How to tell whether a page about time is worth reading
Every error this page corrects comes out of one sentence:
There is one clock, and the only question is whether it is right.
Watch it produce the rest. If there is one clock, describing it needs only words about correctness — so monotonic gets borrowed as a synonym for trustworthy instead of being the name of the other clock. If there is one clock, the RTC and the system clock are two readings of it that can disagree, so of course every machine has one. If there is one clock, “is it right?” is a boolean and a single status line can carry it. If there is one clock, a process either has the right time or the wrong one, so the moment it acquired the zone must be the moment it started. If there is one clock, two timestamps in the same format are two views of it, so making the format the same makes them comparable. If there is one clock, correcting it is an event with no duration, so “let NTP fix it gradually” needs no number. And if there is one clock, nobody needs to be told which one to use for what — which is how a site can run to a hundred and forty-two pages without ever explaining the difference between CLOCK_REALTIME and CLOCK_MONOTONIC.
The absences worth noticing. No mention of CLOCK_MONOTONIC means the author has only ever read the clock, never measured with it. No mention of a step or a slew means they have watched a clock converge and never watched one arrive. No mention of /sys/class/rtc/ being empty means they have only administered physical servers, or only read pages by someone who did. No mention of who is allowed to move the clock means they have only ever been root. And no mention of the tolerance on the other side means they have fixed a clock but never had one rejected — they have set-time in their hands and have never seen certificate is not yet valid.
Before you trust a timestamp
ls -l /sys/class/rtc/ # who keeps the time while it is off
dmesg | grep -i clocksource # which counter the kernel settled on
chronyc tracking || timedatectl show-timesync --all # ask the daemon, not the flag
grep -E 'makestep|rtcsync' /etc/chrony/chrony.conf # and know which number you rely on
uptime -s; who -b # two answers; the gap is how far the clock movedRelated reading
- timedatectl — the command reference, and the page these seven stages correct in six places.
- The Life of a System Call — what a program actually pays to read a clock, why
straceundercounts it, and wherecurrent_clocksourcecomes in. - From Power-On to a Login Prompt — the eight handoffs this page’s stage 1 is the missing half of.
- Containers, All the Way Down — the time namespace, stated correctly and in full.
- The Life of a Certificate — for the validity window whose edges stage 7 walks across.
- The Life of a Scheduled Job — scheduling: why the job that runs twice on cron runs once on a systemd timer, twenty-five hours after the last one.
