Linux Time Namespaces: Clock Offsets, Timer Semantics, and Safe Tests
Test container and checkpoint clock behavior without changing host wall time, by separating monotonic and boottime offsets from CLOCK_REALTIME.
Linux time namespaces let a process tree observe shifted monotonic and boot-time clocks. They are useful for testing software that assumes an uptime, checkpointing a container, or restoring a process whose timers depend on elapsed time. They do not set the host clock or provide a general-purpose way to make a test process’s calendar date run ahead.
The distinction matters because applications choose different clocks. A test that shifts CLOCK_MONOTONIC can exercise elapsed-time logic while leaving wall-clock certificate checks, calendar scheduling, and date at the real system time.
Which clocks move - and which do not
A time namespace stores offsets relative to the initial time namespace. The kernel’s time_namespaces(7) manual documents these clock families:
| Clock family | Time-namespace behavior | Operational meaning |
|---|---|---|
CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, and CLOCK_MONOTONIC_RAW |
Namespaced by an offset | Elapsed-time consumers can see a different monotonic value |
CLOCK_BOOTTIME and CLOCK_BOOTTIME_ALARM |
Namespaced by an offset | Like monotonic time, but includes time spent suspended |
CLOCK_REALTIME |
Not virtualized | Wall-clock time remains the host’s system time |
The namespace affects APIs that use those clocks, including clock_gettime(), sleeps and timers, timerfd, and /proc/uptime. If a service uses CLOCK_REALTIME, shifting its time namespace does not test that path. Likewise, a successful monotonic-time test says nothing about time synchronization, time zones, or changing the machine clock.
The monotonic and boottime offsets can differ. That is deliberate: one clock excludes suspend time and the other includes it. When testing expiration, retries, backoff, leases, or watchdog logic, first determine which clock the application actually reads; an offset on the wrong clock can produce a convincing but irrelevant test.
Inspect the current namespace first
These reads show the current process’s namespace identity and offset file, plus two clocks with intentionally different semantics:
readlink /proc/self/ns/time /proc/self/ns/time_for_children
cat /proc/self/timens_offsets
cat /proc/uptime
date -u
time identifies the namespace of the current process. time_for_children identifies the namespace into which subsequently created children will enter. They can differ after unshare(CLONE_NEWTIME): the caller remains in its existing time namespace while its children are assigned to the new one. This lets the offsets be set before the first child enters.
/proc/<pid>/timens_offsets contains one record per adjustable clock, with a clock name, seconds, and nanoseconds. Nanoseconds must be below one billion; offsets must also satisfy kernel range checks. Writing requires CAP_SYS_TIME in the user namespace that owns the time namespace. After the first process enters, further writes fail with EACCES.
Run a bounded, disposable test
On a Linux system with CONFIG_TIME_NS, a sufficiently recent util-linux unshare, and permission to create a user namespace, a small forward offset can be tested without changing host wall time:
unshare --user --map-root-user --time --fork \
--monotonic 60 --boottime 60 -- \
sh -c 'printf "offsets:\n"; cat /proc/self/timens_offsets; printf "uptime: "; cat /proc/uptime; date -u'
The new user namespace gives the test process namespace-local capabilities; it does not grant host root. --fork matters: the process that calls unshare(CLONE_NEWTIME) does not itself enter the new time namespace, so the child is the first process that observes it. The --monotonic and --boottime options ask util-linux to install the offsets before that child runs.
Expect the offsets and uptime view to reflect the isolated test, while date -u remains on the system’s real-time clock. Exit the shell to end the test namespace. If unshare is blocked by the distribution, container runtime, or host policy, do not disable that policy on a shared system; use an approved disposable VM or test host instead.
The offset change can still affect the program being tested: waits, deadlines, and timer expirations in that process tree are exactly why the namespace is useful. Avoid placing production services, persistent jobs, or a real database workload in the test. Keep it short, use synthetic inputs, and never use date, timedatectl, or clock_settime() as a substitute; those target different clock state and can affect other workloads.
Common test-design mistakes
Testing the wrong clock. Compare the application’s code or tracing evidence with the clock being shifted. A TLS certificate validity check usually uses wall-clock time; a retry interval commonly uses a monotonic clock. Do not infer the choice from a UI timestamp.
Setting offsets too late. The kernel makes namespace offsets immutable after a process enters the new time namespace. Set them through unshare options or through the documented timens_offsets setup path before launching the test workload.
Treating CLOCK_BOOTTIME as identical to CLOCK_MONOTONIC. Their readings can diverge after system suspend. Test both when an application mixes sleep-aware expiry with monotonic deadlines.
Assuming a container creates a time namespace automatically. A namespace is a kernel feature selected by a runtime. Inspect /proc/<pid>/ns/time and /proc/<pid>/ns/time_for_children; do not infer isolation from a container name or from other namespace IDs.
Mistaking offset testing for clock testing. Time namespaces are valuable for controlled elapsed-time behavior, especially checkpoint/restore. They do not test host NTP, RTC drift, wall-clock jumps, or the time zone database.
Keep the scope explicit
Record the kernel version/configuration, util-linux version, namespace IDs, requested offsets, and the actual APIs under test. A reproducible test says which process entered which time namespace and which clock it used. It does not say that “time was changed on Linux.”
Related:
- Linux timerfd: Deadline Clocks, Missed Periods, and Event-Loop Timers
- Linux vDSO and Clock Reads: Fast Userspace Calls Without ABI Guesswork
Sources:
time_namespaces(7)- clock coverage, creation, offsets, permissions, and examples.unshare(1)- time-namespace flags, offset arguments, and process behavior.clock_gettime(2)- Linux clock APIs and clock identifiers.- Linux kernel timekeeping documentation - kernel clock-source and suspend-time concepts.