PTB/NTS node time rollout¶
Use this runbook to audit or enforce the cluster-node clock policy. The desired
state is four hostname-based PTB sources (ptbtime1.ptb.de through
ptbtime4.ptb.de), NTS authentication, no Ubuntu/DHCP fallback source, and at
least two selectable PTB sources in agreement. PTB documents all four public
hostnames and NTS support; ptbtime4 is intentionally at a separate location.
Do not pin endpoint addresses. In July 2026 ptbtime4.ptb.de resolves to
194.94.95.123, while the old playbook still pinned obsolete
192.53.103.122. Hostnames are also required for NTS TLS certificate
validation.
Preflight¶
From the repository root:
ANSIBLE_CONFIG=ansible/ansible.cfg ansible-inventory \
-i ansible/inventory/home.yml -i ansible/inventory/office.yml \
--graph
ANSIBLE_CONFIG=ansible/ansible.cfg ansible \
-i ansible/inventory/home.yml -i ansible/inventory/office.yml \
'control_planes:workers' -m ansible.builtin.ping
ANSIBLE_CONFIG=ansible/ansible.cfg ansible-playbook \
-i ansible/inventory/home.yml -i ansible/inventory/office.yml \
ansible/playbooks/tune-cluster-ntp-ptb.yml --check --diff
The combined-inventory site warning does not apply here: this playbook never
branches on site. It targets the explicit union control_planes:workers and
runs serially. Office-3 inherits the existing k8s SSH user and
~/.ssh/k8s-ci key; no per-host secret is required.
Apply¶
ANSIBLE_CONFIG=ansible/ansible.cfg ansible-playbook \
-i ansible/inventory/home.yml -i ansible/inventory/office.yml \
ansible/playbooks/tune-cluster-ntp-ptb.yml --diff
The play stops before the next node unless the current node has:
- the four exact PTB hostnames with
nts; authselectmode requireandminsources 2;- zero non-PTB sources;
- at least two selectable and NTS-authenticated sources; and
- a selected PTB synchronization source.
Verify¶
ANSIBLE_CONFIG=ansible/ansible.cfg ansible \
-i ansible/inventory/home.yml -i ansible/inventory/office.yml \
'control_planes:workers' -m ansible.builtin.command \
-a 'chronyc selectdata -a'
ANSIBLE_CONFIG=ansible/ansible.cfg ansible \
-i ansible/inventory/home.yml -i ansible/inventory/office.yml \
'control_planes:workers' -m ansible.builtin.command \
-a 'chronyc tracking'
In selectdata, PTB rows must show Y under Auth; at least two must be
selectable and one must have *. Prometheus receives the same policy and
source state from /var/lib/node_exporter/textfile/chrony-ptb.prom. Relevant
alerts are in cluster/monitoring/chrony-ptb-alerts.yaml.
The exporter deliberately reports desired configuration by hostname and live
reachability/authentication by the numeric address returned by chronyc.
NTS-KE may assign an NTP address that differs from the hostname's current DNS
answers, so DNS-to-address matching would create false missing-source and
foreign-source alerts. Alternate Chrony source directories, include files and
duplicate selection-policy directives are treated as configuration drift.
The exported completion timestamp also makes a stopped systemd timer visible;
an old textfile must not look healthy forever.
The per-source trust, require, and prefer options are intentionally not
set. Chrony's trust option assumes a source is always true and would weaken
falseticker detection if placed on all four external endpoints. The global
authselectmode require already makes unauthenticated sources ineligible, and
minsources 2 supplies the desired agreement gate without biasing every source.
If NTS does not converge, check hostname resolution, TCP/4460 for NTS-KE,
UDP/123 for NTP, system CA trust, and chronyc ntpdata <resolved-address>. Do
not restore Ubuntu pools to hide the failure: chronyd should free-run rather
than accept an unauthenticated or unintended authority.