NTP.TOML(5)
๐ NAME ๐
ntp.toml โ bestie, this is the configuration file for the ntpd-rs ntp-daemon. That's it, that's the whole vibe check. ๐
โจ DESCRIPTION โจ
Okay so, configuring ntpd-rs happens in the ntp.toml configuration format. The TOML format is in lots of ways similar to a simple INI with several extensions allowing a JSON-like syntax. fr fr, it's giving INI's cooler cousin who went to art school. ๐
The ntpd-rs configuration file consists of several sections, each of which configures a separate part of the ntp-daemon process. Each of the sections is described in the rest of this document. Many settings will have defaults, which will be indicated by each configuration setting shown. So whenever you see a default in parentheses next to a setting, that's the little guy who shows up if you don't say anything. ๐ซถ
The NTP daemon only supports unicast client-server connections. Most NTP traffic, especially across the public internet, almost exclusively uses this mode, so it is not considered a practical limitation for most scenarios. Not me worried about missing modes โ it's literally fine, this is the mode everybody uses anyway. ๐
๐ SOURCE MODES ๐
Different types of sources (see the section below for details) are supported by the NTP daemon. To set the type of the source, you can configure the mode field with any of these options:
serverโ A server source connects to a single specific NTP server. If a connection is lost, attempts will be made to reconnect to the source. Loyal, monogamous, one-server energy. ๐poolโ A pool source retrieves multiple NTP servers by resolving a hostname via DNS. It then attempts to connect to multiple of these servers at the same time. If a connection is lost, a new server will be retrieved from the pool. It's giving group chat, not a situationship.ntsโ Connect to a single Network Time Security (NTS) source. The NTS protocol uses a TLS handshake to exchange secrets with a server to allow verifying that NTP messages have not been tampered with. Note that the TLS protocol requires that both the client and server have a rough idea of the current time. Security girlies, this one's for you. ๐โจnts-poolโ Connect to a pool of Network Time Security (NTS) sources. This is currently experimental, and may change in backwards incompatible ways. Okay so it's in its beta era, don't get attached to the exact behaviour. ๐sockโ A sock source connects to a GPSd socket to get timing data from GPS receivers. Note that GPSd must be (re-)started after startingntpd-rsfor GPSd to connect to the socket. Yes, the restart order is a whole thing. Don't skip it. ๐ppsโ A PPS source connects to a Pulse Per Second device, which is by default assumed to send a pulse every rounded second. As these devices only provide periodic data, they do not count towardsminimum-agreeing-sources. Important!!! They literally don't get a vote. ๐
๐ ๏ธ CONFIGURATION ๐ ๏ธ
๐ธ [source-defaults] ๐ธ
Some of the behavior of a source is configurable. You can set defaults for those settings in the [source-defaults] section. Think of it as the setting the vibe for every source so you don't have to repeat yourself forty times. ๐ง
poll-interval-limits = { min = min, max = max }({ min = 4, max = 10})โ Specifies the limit on how often a source is queried for a new time. For most instances the defaults will be adequate. Theminandmaxare given as the log2 of the number of seconds (i.e. two to the power of the interval). An interval of 4 equates to 16 seconds, 10 results in an interval of 1024 seconds. If specified, bothminandmaxmust be specified. No half-sending, bestie. Both or nothing.initial-poll-interval = interval(4)โ Initial poll interval used on startup. The value is given as the log2 of the number of seconds (i.e. two to the power of the interval). The default value of 4 results in an interval of 16 seconds. First-impressions interval fr.
๐ [[source]] ๐
Each [[source]] is a set of one or more time sources for the daemon to retrieve time information from. Any number of sources can be configured by repeating a [[source]] section (note the double brackets) for as many times as required. Each source can be configured to connect to a specific remote location. Multiple modes for connecting to sources are supported. If less than minimum-agreeing-sources time sources have been configured, no time will be synchronized to the local clock. Note that a pool counts as multiple time sources. So yes, the double brackets MATTER, and yes, a pool is a whole squad, not one person. ๐
mode = modeโ Specify one of the source modes thatntpd-rssupports:server,pool,nts,nts-pool,sockorpps. For a description of the different source modes, see the SOURCE MODES section. Note that sources of typents-poolare experimental and may change their behavior in backwards-incompatible ways between versions.address = addressโ Specify the remote address of the source. Forserversources this will be the remote address of the NTP server. Forpools, this will be the DNS address of the NTP pool and for NTS this will be the address of the key exchange server. The server address may include a port number by appending a colon (:) followed by a port number. If not specified the daemon will connect toserverandpoolservers via port123, forntssources the default port is4460. Two different default ports, keep up bestie. ๐ซถcertificate-authority = certโ Can only be set on sources with thentsmode. Path to a certificate for an additional certificate authority to use, aside from the certificate authorities specified by the system configuration. Note that this cannot be used to specify a self-signed certificate. Sorry, your homegrown cert isn't invited. ๐count = number(4)โ Can only be set on sources with thepoolmode. Specifies the maximum number of servers that the daemon will attempt to connect to from a pool. The daemon will keep retrying to get more sources from the pool when connections are lost, up to the maximum specified by this configuration value.enable-srv-resolution = bool(false)โ Can only be set on source with thentsornts-poolmode. Enables following SRV records when resolving the address of the NTS-ke server. Such records must be DNSSEC signed. Note that this feature is still experimental and may change in backwards incompatible ways.ignore = ip addressesโpoolmode only. Specifies a list of IP addresses of servers in the pool which should not be used. For example:["127.0.0.1"]. Empty by default. Blocked, blocked, blocked. ๐ซmeasurement_noise_estimate = Noise variance (seconds squared)โppsandsockmode only. Deprecated, useprecisioninstead. Old and busted, use the new one, periodt.precision = Noise standard deviation (seconds)โppsandsockmode only. Precision of the source. This should be an estimate of the size of the expected measurement noise. Technically defined as the 1-standard deviation bound on the measurement error. This is needed assockandppssources don't have a good way to estimate their own error. So you have to do it for them, generosity ๐accuracy = Uncertainty standard deviation (seconds)โppsandsockmode only. Accuracy of the underlying time source. This should be an estimate of the size of the error in the clock you are synchronizing with, as well as any mostly-unchanging offset in the measurement process. This can be used to deprioritize sources which have large offsets in the measurement process or which are of poorer quality than others. It's giving "we listen and we don't judge, but we do rank". ๐poll-interval-limits = { min = min, max = max }(defaults from[source-defaults]) โ Specifies the limit on how often a source is queried for a new time. For most instances the defaults will be adequate. Theminandmaxare given as the log2 of the number of seconds (i.e. two to the power of the interval). An interval of 4 equates to 16 seconds, 10 results in an interval of 1024 seconds. If only one of the two boundaries is specified, the other is inherited from[source-defaults].initial-poll-interval = interval(defaults from[source-defaults]) โ Initial poll interval used on startup. The value is given as the log2 of the number of seconds (i.e. two to the power of the interval). The default value of 4 results in an interval of 16 seconds.ntp-version = 4 | 5 | "auto"(4)โ Which NTP version to use for this source. By default this uses NTP version 4. You can use5to set the protocol version to the draft NTPv5 specification, but your source must support the same draft. To use the experimental upgrade protocol set the NTP version to"auto". This uses a specially constructed NTPv4 packet to attempt an upgrade to the supported draft NTPv5 version if the source also supports the same draft version. NTPv5 support is currently in beta and can still change in a backwards incompatible way. Beta era, handle with care bestie. ๐ฆ
๐ [[server]] ๐
The NTP daemon can be configured to distribute time via any number of [[server]] sections. If no such sections have been defined, the daemon runs in a client only mode. Any number of servers can be configured by repeating the [[server]] section (note the double brackets) for as many times as required. Each server can serve a specific socket address or listen on all available network interfaces on a specific port. Servers always serve the system clock time. Main character energy: everybody gets your time, you get to just have it. โจ
listen = socketaddrโ Address of a UDP socket on which the server should listen for incoming NTP requests. Specified as an interface IP address, a colon and a port number. The standard port number for NTP is UDP port123. Both IPv4 and IPv6 are supported. For example to listen on localhost port123in IPv4 you can use127.0.0.1:123. You can listen on all available network interfaces at once using0.0.0.0:123for IPv4 or[::]:123for IPv6. All the interfaces, all at once, no cap. ๐ฅrate-limiting-cache-size = size(0)โ Number of elements in the rate limiting cache. At mostsizeelements are kept in the cache. This means that if more thansizedifferent clients attempt to connect to the server too frequently, the cache size will have reduced functionality, as rate limiting information gets lost when new clients connect to the server. If set to zero, the cache is unused, this is the default. Small cache = short memory, and it WILL forget you. ๐rate-limiting-cutoff-ms = cutoff(0)โ Minimum time between two requests from the same client, if a request was sent sooner than thecutofftime, the client will be asked to slow down their requests by the server responding with a packet with the NTPRATEkiss code. No actual time measurement will be returned to the client in that case. If set to zero, no rate limiting is applied, this is the default. It's the politest possible "bestie, chill". ๐งallowlist = { filter = [ subnet, .. ], action = "deny" | "ignore" }(unset) โ bestie, this is your VIP list ๐ only the subnets you name infilterare allowed to even breathe on the daemon. Any IP that matches one of the subnets you listed gets to contact this server, and everyone else gets the vibe check. The subnets must be written in CIDR notation, which is an IP address, then a slash, then the number of masked bits โ like127.0.0.1/8or192.168.1.1/24โจ. Theactiondecides what happens to IP addresses that aren't in any of the subnets you listed. When it'sdeny, an explicit packet carrying the NTP DENY kiss code gets sent back to the sender to let them know they're not allowed to do that ๐ญ. When it'signore, the messages just get yeeted into the void with no response sent at all ๐ซฅ. The default value is equivalent to allowing any IP address, and it would be equivalent to setting the filter to["0.0.0.0/0", "::/0"], with either action.denylist = { filter = [ subnet, .. ], action = "deny" | "ignore" }(unset) โ and this one is the blocklist, the exact opposite energy ๐ซ. Do not allow any number of filtered subnets to connect to the daemon. Any IP that matches one of the subnets you specify is not allowed to contact this server, periodt. Same CIDR rules as before: an IP address, a slash, and the number of masked bits, for example127.0.0.1/8or192.168.1.1/24๐ธ. Now here's the twist โ withdenylist, theactiondetermines what measure is taken for IP addresses that are in any of the specified subnets. Whendeny, an explicit packet with the NTP DENY kiss code goes back to the sender telling them nope ๐. Whenignoreis specified, messages are discarded with no response sent. The default value is equivalent to allowing any IP address, and would be equivalent to setting the filter to[], with either action. Empty list, empty drama.require-nts = true | false | "deny" | "ignore"(false) โ do the incoming requests to the server have to have NTS enabled? Iconic question. When it's set totrueor"ignore", any non-NTS enabled messages get ignored entirely ๐. When it's set to"deny", non-NTS enabled messages get explicitly denied with an NTP DENY kiss code. When it'sfalse(that's the default, bestie), normal NTP messages are also allowed โ so everyone can come to the party ๐.accept-ntp-versions = [ 3 | 4 | 5, .. ]([3, 4]) โ an array of the NTP versions the server accepts โญ. By default only NTPv3 and NTPv4 packets are supported. Wanna also let draft NTPv5 packets in? Set this value to[3, 4, 5]. Fair warning though: NTPv5 support is currently in beta and can still change in a backwards incompatible way โ ๏ธ.
โจ [observability] โจ
The settings chilling in this section configure how you can observe the behavior of the daemon ๐. Currently, the daemon can be observed either through the logs, or by retrieving several key metrics either through ntp-ctl(8) or through ntp-metrics-exporter(8). Both. Choose your fighter.
log-level = "trace" | "debug" | "info" | "warn" | "error"(info) โ sets the logging level for messages printed tostdout๐. The lowest level,trace, gives you very detailed information about anything going on in the daemon โ like, anything. The highest level,error, only logs error conditions in the daemon. And heads up: levels higher than the given log level are logged as well, so it's a floor, not a ceiling ๐ง.log-path =path (unset) โ the path where the daemon should direct its log output ๐. The file at this location is reopened onSIGHUP. When this isn't present, log output goes tostdoutinstead.log-path-metrics-exporter =path (unset) โ same deal but for the metrics exporter daemon ๐: the path where it should direct its log output. The file at this location is also reopened onSIGHUP. When not present, log output is directed tostdout.ansi-colors = true | false(see note) โ controls ANSI escape codes in logs ๐จ. By default, ANSI escape codes are used to add some colors and other formatting to the logs when the log target is a terminal. Cute when it's a terminal, cursed when it's a file โ you know the deal.observation-path =path (unset) โ the path where the daemon will create an observation Unix domain socket ๐. This socket is whatntp-ctlandntp-metrics-exporteruse to read the current status of the daemon. If it's not set (the default), no observation socket gets created, and it is not possible to usentp-ctlorntp-metrics-exporterto observe the daemon ๐ญ. So like... set it, bestie.observation-permissions =mode (0o666) โ the file system permissions the observation socket gets created with ๐. Warning: you should always write this number with the octal prefix0o, otherwise your permissions might be interpreted wrongly ๐. The default should be OK for most applications however.metrics-exporter-listen =socketaddr (127.0.0.1:9975) โ the listen address that is used for thentp-metrics-exporter(8)๐ก.
โจ [keyset] โจ
The keyset configures the internal key infrastructure for NTS packets ๐. Quick note, bestie: this is separate from the TLS certificate and private key โ for those, see the relevant configuration in the [[nts-ke-server]] section. Don't mix them up, iykyk.
stale-key-count =count (7) โ the maximum number of old keys to retain in the cache ๐๏ธ. Whenever keys are rotated, the old keys become invalid, but clients may still have NTS cookies encrypted with any of the old keys. So we keep a few around. Be nice.key-rotation-interval =seconds (86400) โ time between key rotation events โฐ. Every time seconds elapses, a new internal key will be generated for creating NTS cookies. By default this is set to a day. Fresh keys, fresh start ๐.key-storage-path =path (unset) โ if set, stores the internal NTS keys in the file indicated by path ๐พ. This lets your keys survive a server reboot, which is huge. If it's not set, clients using NTS may need to redo a key exchange operation to get new NTS cookies ๐ฅฒ. Two things to know: the daemon will not create any parent directories if they don't exist, but it will create the file if it doesn't exist.
โจ [[nts-ke-server]] โจ
The daemon can be configured to operate as an NTS key exchange server by repeating any number of [[nts-ke-server]] sections ๐โจ. If no such sections have been defined, the daemon offers no NTS key exchange services. All NTS-KE servers make use of the shared keyset. The whole purpose of the key exchange server is to safely distribute cookies to clients ๐ช โ and those cookies can then be used in NTP packets with the normal server to validate that the traffic was not tampered with. Chef's kiss security.
listen =socket โ address of a TCP socket on which the server should listen for incoming NTS key exchange requests ๐ฅ. Specified as an interface IP address, a colon, and a port number. The standard port number for an NTS key exchange server is TCP port4460. Both IPv4 and IPv6 are supported ๐ซถ. For example, to listen on localhost port4460in IPv4 you can use127.0.0.1:4460. You can listen on all available network interfaces at once using0.0.0.0:4460for IPv4 or[::]:4460for IPv6.certificate-chain-path =path โ path to a certificate chain for the public certificate that the server offers to clients ๐.private-key-path =path โ path to the private key associated with the server certificate in the certificate chain ๐.key-exchange-timeout-ms =timeout (1000) โ timeout in milliseconds for how long a key exchange may take โ. If the timeout is exceeded, the connection gets dropped. No loitering.concurrent-connections =number (512) โ maximum number of total concurrent connections the key exchange server will handle ๐งฎ. Any connections above the threshold will be held in an OS level queue. They're waiting in line, respectfully.longlived-connections =number (concurrent-connections/10) โ maximum number of concurrent longlived connections from nts pools the key exchange server will handle ๐. Note that pool support is currently in beta and can still change in backwards incompatible ways โ ๏ธ.ntp-port =port โ port number the key exchange server should instruct clients to use ๐ข. Should be used when the port number of the NTP server is not the default.ntp-server =server-name โ server address (either as ip or as domain name) where clients can find the NTP server ๐บ๏ธ. Should be used when this name does not match the name of the NTS key exchange server.accept-ntp-versions = [ 4 | 5, .. ]([4]) โ an array of NTP versions accepted by the server โญ. By default only NTS for NTPv4 packets is supported. To also allow negotiating NTS for draft NTPv5 packets, you can set this value to[4, 5]. NTPv5 support is currently in beta and can still change in a backwards incompatible way, so don't be shocked if it shifts ๐ซ.accepted-pool-authentication-tokens =list of keys ([]) โ tokens pools can use to authenticate pool-specific requests to this NTS server ๐. Note that pool support is currently in beta and can still change in a backwards incompatible way ๐.
โจ [synchronization] โจ
This section of the configuration focuses on how the time information from the time sources is gathered and applied to the system clock โฑ๏ธ๐. This is where the magic actually happens.
minimum-agreeing-sources =count (3) โ the minimum number of sources that should agree on the current time before the daemon does any steering operation on the clock ๐ค. Heads up: if you have configured fewer than this amount of sources, this may result in the daemon never updating the clock ๐ญ๐. So like, count your sources.single-step-panic-threshold =seconds |{ forward =forward, backward =backward}** *(1000)* โ the threshold in seconds at which the daemon will completely exit (i.e. **panic**) when a single non-startup step occurs ๐จ. Generally during normal operation the clock on your system should run somewhat close to the time it's synchronized to, so it is *highly* unlikely that such a large step will take place โ and the daemon exits specifically to prevent any accidental mistakes ๐. If you set it to the value"inf"`, any step will be allowed (chaos mode, your call). You may either configure it as one number of seconds for both forward and backward steps, or as separate values for forward and backward steps.startup-step-panic-threshold = seconds | { forward = forward, backward = backward }(default{ forward = โinfโ, backward = โ86400โ }) โ the threshold in seconds at which the daemon will literally fully exit (aka panic ๐) when a step happens at startup. The default lets any forward step through, but blocks backward steps bigger than a single day. Why? Because clocks that aren't synced usually run behind true time, not ahead โ so if your box is running ahead and then steps back a big chunk of time, that's usually a ๐ฉ problem. Set it to"inf"and any step gets allowed, no questions asked. You can give it either one number of seconds covering both forward and backward steps, or separate values for forward and backward. And bestie, even with this locked down,ntp-ctl'sforce-synccommand can still manually set the system time past these limits โ that's your rescue hatch if the system clock goes rogue ๐ซถ.accumulated-step-panic-threshold = seconds(unset) โ every single time the daemon steps the time instead of gently adjusting via frequency changes, this counter goes up by the absolute value of the step (so forward and backward steps both count, no favouritism ๐ ). Once the threshold is hit, the daemon exits immediately (panic time ๐ญ). During normal operation steps basically shouldn't happen, so any step might mean someone or something is triggering illicit steps ๐. By default though, this panic mechanism is disabled โ it stays off if you leave it unset or set it to0.warn-on-jump = bool(true) โ should the daemon yell a warning when it steps the clock? Those jumps can be messy for other software โ database servers, I'm looking at you ๐. Flip this off on systems where steps are expected and harmless to everything else.local-stratum = stratum(16) โ sets the NTP clock stratum of the system clock when no NTP time sources are configured, or when the time hasn't been synchronized from an NTP time source yet. Servers can use this to announce that some external mechanism is out here doing the syncing โจ.reference-id = reference-id(XNON) โ sets the reported NTP clock reference id, but only whenlocal-statumis set to1. This is how you tell the world what your time reference actually is (GPS etc.) ๐ฐ๏ธ.
๐ธ [synchronization.algorithm]
โ ๏ธ Warning bestie: the algorithm section is mostly internal algorithm tweaks that you generally should not be touching. They're offered here for specific use cases, but these settings are considered implementation details โ meaning they may change in future ntpd-rs versions. Don't get attached ๐.
precision-low-probability = probability(1/3) โ the probability bound below which we start moving towards decreasing our precision estimate. Unit: probability,0-1.precision-high-probability = probability(2/3) โ the probability bound above which we start moving towards increasing our precision estimate. Unit: probability,0-1.precision-hysteresis = hysteresis(16) โ how much hysteresis there is in changing the precision estimate. Unit: count,1+.precision-minimum-weight = weight(0.1) โ lower bound on how much effect our precision estimate has on the total noise estimate before we allow the precision estimate to decrease. Unit: weight,0-1.poll-interval-low-weight = weight(0.4) โ how much a measurement contributes to the state, below which we start increasing the poll interval. Unit: weight,0-1.poll-interval-high-weight = weight(0.6) โ how much a measurement contributes to the state, above which we start decreasing the poll-interval interval. Unit: weight,0-1.poll-interval-hysteresis = hysteresis(16) โ how much hysteresis there is in changing the poll interval. Unit: count,1+.poll-interval-step-threshold = threshold(1e-6) โ the probability threshold for when a measurement counts as a significant enough outlier that we decide something weird is going on and we need to take more measurements ๐. Unit: probability,0-1.delay-outlier-threshold = threshold(5.0) โ the threshold (in number of standard deviations) above which measurements with a significantly larger network delay get rejected. Unit: standard deviations,0+.initial-wander = wander(1e-8) โ the initial estimate of the clock wander from the combination of our local clock and the source's clock. Unit:s/s^2.initial-frequency-uncertainty = uncertainty(100e-6) โ the initial uncertainty of the frequency difference between our clock and the source's clock. Unit:s/s.maximum-source-uncertainty = uncertainty(0.25) โ the maximum source uncertainty before we start disregarding it. Heads up: this is the combined uncertainty from noise plus possible asymmetry error (see the weights below too ๐ซถ). Unit: seconds.range-statistical-weight = weight(2.0) โ the weight of statistical uncertainty when constructing overlap ranges. Unit: standard deviations,0+.range-delay-weight = weight(0.25) โ the weight of delay uncertainty when constructing overlap ranges. Unit: weight,0-1.steer-offset-threshold = threshold(2.0) โ how far from0(in multiples of the uncertainty) the offset should be before we correct it. Unit: standard deviations,0+.steer-offset-leftover = stddev(1.0) โ how many standard deviations do we leave behind after offset correction? Unit: standard deviations,0+.steer-frequency-threshold = threshold(0.0) โ how far from0(in multiples of the uncertainty) the frequency estimate should be before we correct it. Unit: standard deviations,0+.steer-frequency-leftover = stddev(0.0) โ how many standard deviations do we leave behind after frequency correction? Unit: standard deviations,0+.step-threshold = threshold(0.010) โ from what offset should we step the clock instead of trying to adjust gradually? Unit: seconds,0+.slew-maximum-frequency-offset = offset(200e-6) โ what's the maximum frequency offset during a slew? Unit:s/s.slew-minimum-duration = duration(8.0) โ what's the minimum duration of a slew? Unit: seconds.maximum-frequency-steer = frequency(495e-6) โ the absolute maximum frequency correction. Unit:s/s.ignore-server-dispersion = bool(false) โ ignore a server's advertised dispersion when synchronizing. This can improve synchronization quality with servers reporting overly conservative root dispersion ๐.meddling-threshold = threshold(5.0) โ the threshold for detecting external clock meddling ๐๐. Unit: seconds.
๐โจ SEE ALSO โจ๐
and that's the whole page bestie!! ๐ this one's NTP.TOML(5) from ntpd-rs version 1.9.0 โ that's the source and version stamped on it, and the date slot at the bottom is just sitting there empty, no date, nothing, an absolute icon of leaving the date field blank ๐. go configure your clocks, you got this, periodt ๐ฆ๐
๐ฆ where this page comes from
- arch rolling: ntpd-rs 1.9.0-1 ยท Apache-2.0 OR MIT
- nixpkgs unstable: ntpd-rs 1.9.0
- ubuntu stonking: ntpd-rs 1.9.0-0ubuntu2 ยท Apache-2.0 OR MIT
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms