systemd-timesyncd is a minimal SNTP client for synchronizing the local system clock against configured time sources. It can help keep certificate checks and wall-clock timestamps meaningful, but it does not provide an unconditional accuracy guarantee. Whether the daemon can run, whether it receives acceptable samples, and whether the system reports synchronization are separate facts to establish.

When it breaks

The daemon does not always smooth corrections. The documented behavior steps the clock for large offsets and adjusts it gradually for smaller differences. A machine starting far from correct time may therefore experience a discontinuity. Use monotonic clocks for elapsed-duration measurements, and design applications that interpret wall-clock timestamps to handle corrections rather than assuming time always moves smoothly.

FallbackNTP is not a general emergency list used whenever an explicitly configured server becomes unreachable. It applies when no other server information is known. Global NTP entries and per-link sources supplied through networkd take precedence over that fallback. Inspect the actual source selection and configuration drop-ins before concluding that a particular backup server must have been contacted.

An unsynchronized state also does not prove UDP traffic is blocked. A service can be absent, disabled, excluded by unit conditions, unable to adjust time, or rejecting unsuitable samples. Conversely, a global synchronization property does not identify which process supplied time. Distinguish the manager’s service state, the time client’s observations, and the kernel-level synchronization indication.

Check it quickly

This failure-mode exercise runs as root inside a disposable Debian 12 Docker container with systemd 252.39 as PID 1. Its init process was launched through /usr/bin/setpriv --bounding-set=-sys_time,-sys_module /sbin/init; the capability restriction prevents adjustment of the Docker VM’s shared clock. The script verifies that prerequisite before requesting any service start, and requires the packaged container-exclusion condition:

from pathlib import Path
import json,subprocess

def run(*args):
    result=subprocess.run(args,text=True,capture_output=True,timeout=10)
    assert result.returncode==0,(args,result.stderr)
    return result.stdout.strip()
def properties(unit):
    output=run('systemctl','show',unit,'-p','ActiveState','-p','SubState','-p','MainPID','-p','ConditionResult')
    return dict(line.split('=',1) for line in output.splitlines())
# This failure-mode exercise requires an isolated container that cannot set time.
status=dict(line.split(':',1) for line in Path('/proc/1/status').read_text().splitlines() if ':' in line)
assert int(status['CapBnd'].strip(),16)&(1<<25)==0,'PID 1 must lack CAP_SYS_TIME'
assert run('systemd-detect-virt','--container')=='docker'
unit='systemd-timesyncd.service';definition=run('systemctl','cat',unit)
assert 'ConditionCapability=CAP_SYS_TIME' in definition
assert 'ConditionVirtualization=!container' in definition
assert properties(unit)['ActiveState']=='inactive'
run('systemctl','start',unit)
state=properties(unit)
assert state=={'MainPID':'0','ActiveState':'inactive','SubState':'dead','ConditionResult':'no'},state
clock=dict(line.split('=',1) for line in run('timedatectl','show','-p','CanNTP','-p','NTP','-p','NTPSynchronized').splitlines())
assert all(value in ('yes','no') for value in clock.values())
print('start command returned zero; unit stayed inactive with PID 0 and failed conditions')
print('clock properties:',json.dumps(clock,sort_keys=True))
print('PID 1 lacks time-setting capability; Docker condition also excludes this packaged service')
print('no clock-setting command was issued and no NTP synchronization was claimed')

The start command returns zero, yet the unit remains inactive with main PID zero and ConditionResult=no. A skipped conditional activation is not the same as a successfully running daemon. The fixture’s packaged unit requires the time-setting capability and excludes containers; this environment satisfies neither prerequisite. The observed outcome is therefore explained before any theory about blocked NTP packets is needed.

The recorded clock properties are CanNTP=yes, NTP=no, and NTPSynchronized=no. They describe different aspects of the system’s time-service interface. In particular, knowing that NTP service support is available does not make that service active or establish that the clock has synchronized. The script reports the actual values rather than replacing them with an assumed healthy result.

No clock-setting command is issued, and no successful NTP exchange or clock adjustment is claimed. This verifies a real activation failure mode in a container that cannot set time. Testing successful clock discipline requires an appropriate machine or independently clocked virtual machine with the required authority and a reachable, suitable time source.

When the daemon is actually running, its timedatectl timesync-status interface provides client-specific server and synchronization information. Check service state before relying on that interface; a request to an unavailable service can wait for D-Bus activation and time out. The container probe reached its five-second observation limit, which was treated as unavailable evidence, not a successful synchronization check.

Build a time requirement that can be verified

Choose sources appropriate to the network and inspect offset, delay, and accepted-source quality over time. Reachability alone does not make a server accurate or trustworthy. Polling and root-distance settings influence sample handling; they do not turn a minimal client into a complete time-distribution service or establish an accuracy service-level agreement by themselves.

The saved /var/lib/systemd/timesync/clock timestamp helps initialize time across boots without a reliable hardware clock. It can also be updated periodically without a recent synchronization, so its existence is not proof of a fresh accepted network sample. Rough initialization and synchronization to a reference clock are different milestones.

If dependent work requires synchronized time, configure and verify the appropriate wait service and ordering after time-sync.target. Starting timesyncd alone does not delay every application until an accurate reference is reached. Keep the startup requirement and runtime monitoring explicit, and use the evidence from the actual deployment rather than a generic “NTP enabled” label.

Further reading

Sources