Skip to content

Last updated: 2026-02-11

Chrony NTS Configuration Guide

This guide provides recommended Network Time Security (NTS) settings for chrony. NTS is a cryptographic extension to NTP that authenticates time synchronization using TLS, preventing man-in-the-middle attacks on time data. Chrony has supported NTS since version 4.0.

Prerequisites

Path note: This guide uses RHEL/CentOS paths (/etc/chrony.conf). On Debian/Ubuntu, the config file is /etc/chrony/chrony.conf and the chrony user/group is _chrony (with underscore) instead of chrony.

NTS Overview

NTS works in two phases:

  1. NTS-KE (Key Establishment) -- A TLS 1.3 handshake on port 4460 authenticates the server and establishes shared keys
  2. NTP with cookies -- Subsequent NTP packets on port 123 carry encrypted cookies and authentication extensions derived from the NTS-KE session

Client Configuration

Basic NTS Client

Enable NTS for upstream time servers by adding the nts keyword:

# /etc/chrony.conf

server time.cloudflare.com iburst nts
server nts.netnod.se iburst nts
server ptbtime1.ptb.de iburst nts
server ntppool1.time.nl iburst nts

NTS-KE Port

If a server uses a non-standard NTS-KE port:

server nts.example.com iburst nts ntsport 4461

Certificate Trust Store

Chrony uses the system trust store by default. To specify a custom CA bundle:

# RHEL/CentOS:
ntstrustedcerts /etc/pki/tls/certs/ca-bundle.crt
# Debian/Ubuntu: /etc/ssl/certs/ca-certificates.crt
# SLES: /etc/ssl/ca-bundle.pem

NTS Cookie Storage

Persist NTS cookies across restarts to avoid repeated NTS-KE handshakes:

ntsdumpdir /var/lib/chrony

Rate Limiting NTS-KE

Limit how often chrony retries NTS-KE on failure:

ntsrefresh 86400

Version note: ntsrefresh requires chrony 4.1+. On chrony 4.0 (Debian 11), omit this directive.

This sets the maximum interval (in seconds) between NTS-KE requests to a server.

Server Configuration

NTS-KE Server

Configure chrony as an NTS-capable NTP server:

# /etc/chrony.conf

# NTS-KE server settings
ntsserverkey /etc/chrony/ssl/privkey.pem
ntsservercert /etc/chrony/ssl/fullchain.pem

The certificate must be valid for the server's hostname. Let's Encrypt certificates work well.

NTS-KE Port

By default, chrony listens on port 4460 for NTS-KE. To change it:

ntsport 4460

Key Rotation

NTS uses server cookies encrypted with a key that chrony rotates automatically. Persist the keys across restarts:

ntsdumpdir /var/lib/chrony

Chrony generates new cookie keys automatically and retains old keys long enough for clients to transition.

NTS Process Separation

For security, run the NTS-KE helper as a separate unprivileged process:

ntsprocesses 1

Version note: ntsprocesses requires chrony 4.1+. On chrony 4.0 (Debian 11), omit this directive — NTS-KE will run in the main process instead.

Complete Client Configuration

# /etc/chrony.conf

# NTS-authenticated time sources
server time.cloudflare.com iburst nts
server nts.netnod.se iburst nts
server ptbtime1.ptb.de iburst nts
server ntppool1.time.nl iburst nts

# Fallback (unauthenticated) sources
pool pool.ntp.org iburst maxsources 2

# NTS cookie storage
ntsdumpdir /var/lib/chrony

# Allow system clock to be stepped on first sync
makestep 1.0 3

# Record tracking data
driftfile /var/lib/chrony/drift

# RTC synchronization
rtcsync

# Logging
logdir /var/log/chrony
log measurements statistics tracking

Complete Server Configuration

# /etc/chrony.conf

# Upstream NTS sources
server time.cloudflare.com iburst nts
server nts.netnod.se iburst nts

# NTS server settings
ntsserverkey /etc/chrony/ssl/privkey.pem
ntsservercert /etc/chrony/ssl/fullchain.pem
ntsprocesses 1
ntsdumpdir /var/lib/chrony

# Serve time to clients
allow 10.0.0.0/8
allow 172.16.0.0/12
allow 192.168.0.0/16

# Serve as stratum 2 if upstream sources are lost
local stratum 2

# System clock
makestep 1.0 3
driftfile /var/lib/chrony/drift
rtcsync

# Rate limiting
ratelimit interval 1 burst 16

# Logging
logdir /var/log/chrony
log measurements statistics tracking

Certificate Management

Using Let's Encrypt with Certbot

certbot certonly --standalone -d ntp.example.com

Configure chrony to use the certificates:

ntsserverkey /etc/letsencrypt/live/ntp.example.com/privkey.pem
ntsservercert /etc/letsencrypt/live/ntp.example.com/fullchain.pem

Chrony automatically detects certificate file changes and reloads them without restart, which works well with certbot's automatic renewal.

File Permissions

Ensure chrony can read the private key:

chown root:chrony /etc/chrony/ssl/privkey.pem   # Debian/Ubuntu: chown root:_chrony
chmod 640 /etc/chrony/ssl/privkey.pem

Firewall

Open the required ports:

# NTS-KE (TLS 1.3 key establishment)
firewall-cmd --permanent --add-port=4460/tcp

# NTP (time synchronization)
firewall-cmd --permanent --add-service=ntp

firewall-cmd --reload

Verification

Check NTS status for all sources:

chronyc -N authdata

You should see NTS in the Mode column and a non-zero cookie count:

Name/IP address  Mode KeyID Type KLen Last Atmp  NAK Cook CLen
================================================================
time.cloudflare>  NTS     1   15  256  33m    0    0    8  100
nts.netnod.se    NTS     1   15  256  33m    0    0    8  100

Check NTS-KE connection details:

chronyc ntpdata time.cloudflare.com

Verify the NTS-KE handshake uses TLS 1.3:

gnutls-cli --port 4460 time.cloudflare.com < /dev/null

Check overall time synchronization:

chronyc tracking
chronyc sources -v