> ## Documentation Index
> Fetch the complete documentation index at: https://siderolabs-fe86397c-config-evolution.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Time Synchronization

> Configuring time synchronization.

export const VersionWarningBanner = () => {
  const latestVersion = "v1.13";
  const [latestUrl, setLatestUrl] = useState(null);
  const [currentVersion, setCurrentVersion] = useState(null);
  const [isBeta, setIsBeta] = useState(false);
  const parseVersion = v => v.replace("v", "").split(".").map(Number);
  const isGreaterVersion = (a, b) => {
    const [aMajor, aMinor] = parseVersion(a);
    const [bMajor, bMinor] = parseVersion(b);
    if (aMajor > bMajor) return true;
    if (aMajor === bMajor && aMinor > bMinor) return true;
    return false;
  };
  useEffect(() => {
    if (typeof window === "undefined") return;
    const {pathname, hash, search} = window.location;
    const match = pathname.match(/\/talos\/(v\d+\.\d+)\//);
    if (!match) return;
    const detectedVersion = match[1];
    if (detectedVersion === latestVersion) return;
    setCurrentVersion(detectedVersion);
    if (isGreaterVersion(detectedVersion, latestVersion)) {
      setIsBeta(true);
    }
    const newPath = pathname.replace(`/talos/${detectedVersion}/`, `/talos/${latestVersion}/`);
    setLatestUrl(`${newPath}${search}${hash}`);
  }, []);
  if (!latestUrl || !currentVersion) return null;
  return <div className="not-prose sticky top-6 z-50 my-6">
      <div className="border border-yellow-500/30 bg-yellow-500/10 px-4 py-3 rounded-xl">
        <div className="text-sm">
          {isBeta ? <>
              ⚠️ You are viewing a <strong>beta version</strong> of Talos ({currentVersion}).
              This version may be unstable.
              <a href={latestUrl} className="ml-2 underline text-yellow-400 hover:text-yellow-300 font-medium">
                View latest stable version {latestVersion} →
              </a>
            </> : <>
              ⚠️ You are viewing an older version of Talos ({currentVersion}).
              <a href={latestUrl} className="ml-2 underline text-yellow-400 hover:text-yellow-300 font-medium">
                View the latest version {latestVersion} →
              </a>
            </>}
        </div>
      </div>
    </div>;
};

<VersionWarningBanner />

Talos Linux itself does not require time to be synchronized across the cluster, but as Talos Linux and Kubernetes components issue certificates
with expiration dates, it is recommended to have time synchronized across the cluster.
Some workloads (e.g. Ceph) might require to be in sync across the machines in the cluster due to the design of the application.

Talos Linux tries to launch API even if the time is not sync, and if time jumps as a result of NTP sync, the API certificates will be rotated automatically.
Some components like `kubelet` and `etcd` wait for the time to be in sync before starting, as they don't support graceful certificate rotation.

By default, Talos Linux uses `time.cloudflare.com` as the NTP server, but it can be overridden in the machine configuration, or provided via DHCP, kernel args, platform sources, etc.
Talos Linux implements SNTP protocol to sync time with the NTP server, optionally secured with [NTS](#network-time-security-nts).

## Observing status

Current time sync status can be observed with:

```shell theme={null}
talosctl get timestatus
```

```text theme={null}
NODE         NAMESPACE   TYPE         ID     VERSION   SYNCED
172.20.0.2   runtime     TimeStatus   node   2         true
```

The list of servers Talos Linux is syncing with can be observed with:

```shell theme={null}
talosctl get timeservers
```

```text theme={null}
NODE         NAMESPACE   TYPE               ID            VERSION   TIMESERVERS                USENTS
172.20.0.2   network     TimeServerStatus   timeservers   1         ["time.cloudflare.com"]    true
```

The `USENTS` column shows whether the servers are queried with [NTS](#network-time-security-nts) enabled.

More detailed logs about the time sync process can be queried with:

```shell theme={null}
talosctl logs controller-runtime | grep -i time.Sync
```

```text theme={null}
172.20.0.2: 2024-04-17T18:32:16.690Z DEBUG NTP response {"component": "controller-runtime", "controller": "time.SyncController", "clock_offset": "37.060204ms", "rtt": "3.044816ms", "leap": 0, "stratum": 3, "precision": "29ns", "root_delay": "70.617676ms", "root_dispersion": "259.399µs", "root_distance": "37.090645ms"}
172.20.0.2: 2024-04-17T18:32:16.690Z DEBUG sample stats {"component": "controller-runtime", "controller": "time.SyncController", "jitter": "150.196588ms", "poll_interval": "34m8s", "spike": false}
172.20.0.2: 2024-04-17T18:32:16.690Z DEBUG adjusting time (slew) by 37.060204ms via 162.159.200.1, state TIME_OK, status STA_PLL | STA_NANO {"component": "controller-runtime", "controller": "time.SyncController"}
172.20.0.2: 2024-04-17T18:32:16.690Z DEBUG adjtime state {"component": "controller-runtime", "controller": "time.SyncController", "constant": 7, "offset": "37.060203ms", "freq_offset": -1302069, "freq_offset_ppm": -19}
```

## Network time security (NTS)

Plain NTP is unauthenticated, so an on-path attacker can skew the clock of a node, which in turn breaks TLS certificate validation, log ordering and distributed consensus.
[NTS](https://datatracker.ietf.org/doc/html/rfc8915) fixes that: Talos Linux first performs a TLS key exchange with the time server, and the NTP packets exchanged afterwards
are cryptographically authenticated with the keys established during the key exchange.

When no time servers are configured (from the machine configuration or any other source), Talos Linux uses the default `time.cloudflare.com` server with NTS enabled.
Once time servers are configured, NTS is only used when it is explicitly enabled with the `useNTS` field of the
[TimeSyncConfig](../../reference/configuration/network/timesyncconfig) document:

```yaml theme={null}
apiVersion: v1alpha1
kind: TimeSyncConfig
ntp:
  useNTS: true
  servers:
    - time.cloudflare.com
```

Time servers provided by DHCP, kernel arguments or platform metadata are always queried over plain NTP.

With NTS enabled, time servers have to be specified as hostnames (the hostname is required to validate the server's TLS certificate): the machine configuration is rejected
if an IP address is used, and time servers from other sources which are IP addresses are skipped with a warning.
The key exchange runs over TCP port `4460` (unless the server address specifies a different port), while the NTP queries keep using UDP port `123`
(or the port negotiated during the key exchange).
Changing the `useNTS` setting restarts the time syncer without a reboot.

### NTS with an incorrect clock at boot

The NTS key exchange runs over TLS, and TLS validates the server certificate against the current system clock, but the clock is exactly what has not been set yet
at that point of the boot sequence: a machine without a working RTC (or with a badly skewed one) would never be able to establish an NTS session.

To break this cycle, Talos Linux tolerates certificate validity period failures for the first few NTS session attempts of the boot sequence
(before the time has been synced at least once).
In that case the certificate chain and the hostname are still fully verified — only the `notBefore`/`notAfter` timestamps are ignored, with the verification time pinned
to the certificate's own `notBefore`.
Certificates which fail validation for any other reason (unknown authority, hostname mismatch, etc.) are always rejected.

Each time this fallback is used, a warning is logged:

```text theme={null}
172.20.0.2: 2026-05-14T10:15:22.418Z WARN NTS certificate time validation failed, retrying while ignoring certificate time constraints (system clock may not be set yet) {"component": "controller-runtime", "controller": "time.SyncController", "server": "time.cloudflare.com", "bootstrap_attempts_used": 1, "bootstrap_attempts_limit": 5}
```

Once the time has been synced once, or the bootstrap attempts are exhausted, certificate validation is always strict.

> Note: this fallback covers the NTS key exchange only.
> If the time servers are configured as hostnames, they still have to be resolved, and [encrypted DNS](../../networking/host-dns#encrypted-dns-and-time-synchronization)
> (`DoT`/`DoH`) validates certificates strictly at all times.
> When all nameservers use encrypted DNS and the clock is wrong at boot, resolution of the time server names fails, and the time can never be synced:
> keep a plain DNS nameserver as a fallback, or configure time servers by IP address (which in turn disables NTS for them).

## Using PTP devices

When running in a VM on a hypervisor, instead of doing network time sync, Talos can sync the time to the hypervisor clock (if supported by the hypervisor).

To check if the PTP device is available:

```shell theme={null}
talosctl ls /sys/class/ptp/
```

```text theme={null}
NODE         NAME
172.20.0.2   .
172.20.0.2   ptp0
```

Make sure that the PTP device is provided by the hypervisor, as some PTP devices don't provide accurate time value without proper setup:

```shell theme={null}
talosctl read /sys/class/ptp/ptp0/clock_name
KVM virtual PTP
```

To enable PTP sync, set the `TimeSyncConfig` to the PTP device name (e.g. `/dev/ptp0`):

```yaml theme={null}
apiVersion: v1alpha1
kind: TimeSyncConfig
ptp:
  devices:
    - /dev/ptp0
```

After setting the PTP device, Talos will sync the time to the PTP device instead of using the NTP server:

```text theme={null}
172.20.0.2: 2024-04-17T19:11:48.817Z DEBUG adjusting time (slew) by 32.223689ms via /dev/ptp0, state TIME_OK, status STA_PLL | STA_NANO {"component": "controller-runtime", "controller": "time.SyncController"}
```

## Additional configuration

Talos NTP sync can be disabled with the following machine configuration patch:

```yaml theme={null}
apiVersion: v1alpha1
kind: TimeSyncConfig
enabled: false
```

When time sync is disabled, Talos assumes that time is always in sync.

Time sync can be also configured on best-effort basis, where Talos will try to sync time for the specified period of time, but if it fails to do so, time will be configured to be in sync when the period expires:

```yaml theme={null}
apiVersion: v1alpha1
kind: TimeSyncConfig
bootTimeout: 2m
```
