Skip to content

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 require and minsources 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.

Upstream references