NTP cluster configuration using chrony
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Simon Cornet a692f7fc20 docs: improve readme
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 17:28:21 +02:00
.woodpecker chore(package): update cr.simoncor.net/dockerhub/davidanson/markdownlint-cli2 docker tag to v0.23.3 2026-09-20 05:49:24 +00:00
defaults feat: remove ratelimit 2026-07-25 15:58:10 +02:00
handlers fix: service name 2026-07-25 15:53:43 +02:00
meta feat: initial commit 2026-07-25 15:48:08 +02:00
roles feat: add playbook and role requirement 2026-07-25 15:49:55 +02:00
tasks fix: start chronyd, flush handlers before waitsync, robust self-exclusion, galaxy once 2026-10-02 11:16:19 +02:00
templates fix: start chronyd, flush handlers before waitsync, robust self-exclusion, galaxy once 2026-10-02 11:16:19 +02:00
.ansible-lint feat: initial commit 2026-07-25 15:48:08 +02:00
.editorconfig feat: initial commit 2026-07-25 15:48:08 +02:00
.gitattributes feat: initial commit 2026-07-25 15:48:08 +02:00
.gitignore feat: initial commit 2026-07-25 15:48:08 +02:00
.markdownlint-cli2.jsonc feat: initial commit 2026-07-25 15:48:08 +02:00
.sops.yaml feat: initial commit 2026-07-25 15:48:08 +02:00
.yamllint feat: initial commit 2026-07-25 15:48:08 +02:00
ansible.cfg feat: initial commit 2026-07-25 15:48:08 +02:00
playbook.yaml fix: start chronyd, flush handlers before waitsync, robust self-exclusion, galaxy once 2026-10-02 11:16:19 +02:00
readme.md docs: improve readme 2026-10-05 17:28:21 +02:00
renovate.json feat: initial commit 2026-07-25 15:48:08 +02:00

Ansible Role: NTP Cluster

Installs and configures a Chrony NTP cluster on Alpine Linux. Each node syncs with the upstream servers, peers with the other cluster members, serves time to the allowed subnets and falls back to orphan mode when it is not synced.

Requirements

Operating System Version Notes
Alpine Linux 3.x Uses apk and the service module (OpenRC), root required

Dependencies

None (dependencies: []). The playbook installs this role (ntp_cluster) from roles/requirements.yml.

Variables

Variable Required Default Description
ntp_upstream_servers No time1.ams-ix.net, time2.ams-ix.net, time.cloudflare.com Upstream NTP servers
ntp_cluster_peers No [] All cluster members (hostname and ip), including the host itself
ntp_allowed_subnets No [] Subnets allowed to query this server
ntp_local_stratum No "10" Stratum served when not synced (orphan mode)
ntp_driftfile_path No "/var/lib/chrony/drift" Path of the chrony driftfile
ntp_config_path No "/etc/chrony/chrony.conf" Path of the chrony configuration file
ntp_makestep_threshold No "1" Makestep threshold in seconds
ntp_makestep_limit No "3" Number of clock updates in which makestep may be used
ntp_maxupdateskew No "100" Maximum update skew (ppm) for faster syncing

Example

ntp_upstream_servers:
  - "time1.ams-ix.net"
  - "time2.ams-ix.net"
  - "time.cloudflare.com"

ntp_cluster_peers:
  - hostname: "ntp01.example.internal"
    ip: "10.0.0.1"
  - hostname: "ntp02.example.internal"
    ip: "10.0.0.2"
  - hostname: "ntp03.example.internal"
    ip: "10.0.0.3"

ntp_allowed_subnets:
  - "10.0.0.0/24"
  - "192.168.1.0/24"

ntp_local_stratum: "10"

Usage

Run playbook.yaml via Semaphore. The playbook first runs ansible-galaxy install -f -r roles/requirements.yml on the controller and then includes the ntp_cluster role on all hosts, one host at a time (serial: 1).

Operational notes

  • Installs chrony, then enables and starts the chronyd service.
  • The peers are written to a block in /etc/hosts (existing file only, it is not created) so they resolve without DNS. In chrony.conf every peer is added as a server line, except the host itself (matched on inventory name, hostname, FQDN or one of its IPv4 addresses).
  • Upstream servers and peers use iburst maxpoll 6. local stratum is set with orphan, and rtcsync is enabled.
  • After a configuration change chronyd is restarted and the role runs chronyc waitsync 30 0.01. A failed wait does not fail the play.