Network Time Synchronisation

Posted on 2 2026

Time synchronisation is one of those things that works silently in the background until it does not, at which point it causes failures in places that do not obviously relate to the clock. TLS handshakes fail because a certificate appears to be from the future. Log correlation becomes impossible because two machines disagree on when something happened. Kerberos authentication breaks entirely. Getting this right early and doing it properly is worth the small amount of effort it takes.

Kubuntu 24.04 LTS uses systemd-timesyncd as the default NTP client. It is lightweight, integrated with the init system, and adequate for most desktop use. This page covers configuring it to use your internal NTP server when on the home network, fall back sensibly when off-network, and pick up NTP server addresses pushed by DHCP automatically via a NetworkManager dispatcher script.

The current state of time sync on Ubuntu

The Ubuntu ecosystem is in transition. systemd-timesyncd is the current default on Kubuntu 24.04. Chrony is becoming the default from Ubuntu 25.10 onward. The configuration here targets systemd-timesyncd as the installed tool. If you are running a later release and chrony is already installed, skip to the chrony section at the end of this page.

Check what is currently running:

timedatectl status

The output should show NTP service: active and System clock synchronized: yes. The NTP service line indicates which daemon is handling synchronisation.

Configuring systemd-timesyncd

The main configuration file is /etc/systemd/timesyncd.conf. By default it contains only comments. Create an override in the drop-in directory instead, which is cleaner and survives package upgrades:

sudo mkdir -p /etc/systemd/timesyncd.conf.d

Create /etc/systemd/timesyncd.conf.d/local.conf:

[Time]
# Primary NTP server: your internal server once it is built
# Uncomment and set this once the server section of this series is complete
# NTP=ntp.yourdomain.net

# Fallback NTP servers using NTS (Network Time Security) where possible
# These authenticated alternatives to plain NTP are available from several providers
FallbackNTP=time.cloudflare.com 2.pool.ntp.org 0.uk.pool.ntp.org 1.uk.pool.ntp.org

# Poll interval settings (in seconds, expressed as power of 2)
# Default: 32s to 2048s. Leaving at defaults is fine for a desktop.

Restart the service after making changes:

sudo systemctl restart systemd-timesyncd

Verify it is synchronising:

timedatectl timesync-status

The output shows the server being used, the stratum, and the current offset. An offset in the low milliseconds or microseconds is healthy.

Picking up NTP servers from DHCP via NetworkManager

The original problem with the desktop network setup is that NetworkManager does not automatically pass NTP server addresses received from DHCP to systemd-timesyncd. The two systems do not talk to each other by default. A NetworkManager dispatcher script bridges this gap.

Create the dispatcher script at /etc/NetworkManager/dispatcher.d/10-update-timesyncd:

#!/usr/bin/env dash
#
# Update systemd-timesyncd with NTP servers received via DHCP.
# Runs when a NetworkManager connection comes up or goes down.
#
set -e

INTERFACE=$1
ACTION=$2
CONF_DIR=/etc/systemd/timesyncd.conf.d

# Exit if no connection UUID (not a connection event)
if [ -z "$CONNECTION_UUID" ]; then
    exit 0
fi

# Check if timesyncd is running
TIMESYNCD_STATE="$(systemctl show --property ActiveState systemd-timesyncd.service)"

# Create the drop-in directory if it does not exist
if [ ! -d "$CONF_DIR" ]; then
    mkdir -p "$CONF_DIR"
fi

case "$ACTION" in
    up|vpn-up)
        NTP_SERVERS=""

        # Collect NTP servers from DHCP (IPv4 and IPv6)
        if [ -n "$DHCP4_NTP_SERVERS" ]; then
            NTP_SERVERS="$NTP_SERVERS $DHCP4_NTP_SERVERS"
        fi
        if [ -n "$DHCP6_NTP_SERVERS" ]; then
            NTP_SERVERS="$NTP_SERVERS $DHCP6_NTP_SERVERS"
        fi

        # Write a connection-specific timesyncd config
        {
            echo "# Generated by $(basename "$0") for $INTERFACE ($CONNECTION_UUID)"
            echo "[Time]"
            echo "NTP=${NTP_SERVERS}"
        } > "${CONF_DIR}/${CONNECTION_UUID}.conf"

        # Restart timesyncd to pick up the new servers
        if [ "$TIMESYNCD_STATE" = "ActiveState=active" ]; then
            systemctl restart systemd-timesyncd
        fi
        ;;

    down|vpn-down)
        # Remove the connection-specific config when the connection drops
        rm -f "${CONF_DIR}/${CONNECTION_UUID}.conf"

        if [ "$TIMESYNCD_STATE" = "ActiveState=active" ]; then
            systemctl restart systemd-timesyncd
        fi
        ;;
esac

Make it executable, owned by root, and readable only by root:

sudo chmod 0744 /etc/NetworkManager/dispatcher.d/10-update-timesyncd
sudo chown root:root /etc/NetworkManager/dispatcher.d/10-update-timesyncd

Restart NetworkManager to register the dispatcher:

sudo systemctl restart NetworkManager

Now when a connection comes up, if the DHCP server pushes NTP server addresses (as your router should, once the server section of this series is complete), they will be automatically applied to systemd-timesyncd for the duration of that connection.

Connecting to your internal NTP server

Once the server section of this series is complete and an NTP server is running on your network, update /etc/systemd/timesyncd.conf.d/local.conf to set it as the primary server:

[Time]
NTP=ntp.yourdomain.net
FallbackNTP=time.cloudflare.com 0.uk.pool.ntp.org

Alternatively, configure your DHCP server on each site to push the NTP server address automatically. If your router’s DHCP is configured correctly, the dispatcher script above will pick it up without any manual configuration on the desktop.

Verifying time synchronisation

Check the current sync status at any time:

timedatectl timesync-status

The important fields are:

  • Server: the NTP server currently in use
  • Stratum: how many hops from a reference clock (lower is better; your internal server will be stratum 2 or 3)
  • Offset: how far the local clock is from the server (should be in the low milliseconds)
  • Delay: round-trip time to the server

A full time status including timezone and hardware clock:

timedatectl status

A note on NTS

Network Time Security (NTS) is an authenticated extension to NTP that prevents certain attacks on the time synchronisation process, including man-in-the-middle attacks that could push your clock in a specific direction. systemd-timesyncd does not currently support NTS. Chrony does.

If you want NTS-authenticated time synchronisation on the desktop, install chrony:

sudo apt install chrony
sudo systemctl disable --now systemd-timesyncd

Configure /etc/chrony/chrony.conf:

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

# Internal server (once built)
# server ntp.yourdomain.net iburst

# Allow stepping the clock on the first three updates
makestep 1.0 3

# Sync hardware clock
rtcsync

# Log directory
logdir /var/log/chrony

Start and enable chrony:

sudo systemctl enable --now chrony

Verify NTS is working:

chronyc sources -v

Sources using NTS show * in the authenticated column. The chronyc tracking command shows the current offset and synchronisation state.

For most desktop setups, systemd-timesyncd with the dispatcher script above is sufficient. Chrony with NTS is worth considering if your threat model includes attacks on time synchronisation, or if you are setting up the desktop as a reference for other devices.

The time zone matters as much as synchronisation. Verify it is set correctly, particularly if you are logging across multiple systems: timedatectl set-timezone Europe/London