Skip to content
LinuxDeep Dive Published Updated 5 min readViews unavailable

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:

Sources:

Comments