Do not begin this section until the checks in part one succeed. At a minimum, cgps -s should display valid UTC data. Where the receiver supports PPS, ppstest should show one pulse per second.

How GPSD supplies time to Chrony

A GPS receiver can provide two different timing sources:

  • Message-based time from the serial data, commonly NMEA. This supplies the complete date and second number but is affected by serial and processing delay.
  • Pulse per second, or PPS. This supplies a precise edge once per second but does not identify which second the pulse represents.

Chrony combines the complete timestamp with the precise pulse. Without PPS, the server can still use message-based GPS time, but the accuracy will normally be measured in milliseconds rather than microseconds.

Current Chrony guidance prefers the GPSD SOCK interface over the older shared-memory interface. The socket method has better security and allows GPSD to pass measurements directly to Chrony.

Find the socket name Chrony needs

The expected GPSD socket name is based on the final serial-device name, not the full persistent path.

For example:

  • /dev/ttyUSB0 normally corresponds to /run/chrony.ttyUSB0.sock for PPS.
  • /dev/ttyACM0 normally corresponds to /run/chrony.ttyACM0.sock for PPS.
  • With GPSD 3.25 or later, message-based timing normally uses a socket containing clk, such as /run/chrony.clk.ttyUSB0.sock.

On systems where /var/run is a link to /run, either path may appear in documentation. Use the path expected by the installed packages.

Confirm the receiver's final device name:

readlink -f /dev/serial/by-id/REPLACE-WITH-THE-ACTUAL-DEVICE

The command should return a path such as:

/dev/ttyUSB0

Back up the Chrony configuration

On Debian and Ubuntu, the main file is normally:

/etc/chrony/chrony.conf

Create a backup before editing it:

sudo cp /etc/chrony/chrony.conf /etc/chrony/chrony.conf.before-gps

Open the file:

sudo nano /etc/chrony/chrony.conf

Keep several Internet sources during the build

Do not remove every existing Internet source during initial testing. External NTP sources give Chrony something with which to compare the GPS receiver and allow the server to maintain time while the receiver is acquiring satellites.

A typical Debian or Ubuntu configuration already includes a pool line. It can remain in place:

pool 2.debian.pool.ntp.org iburst

Use the distribution default or the organisation's approved NTP sources. The exact pool hostname is not important to the GPS integration.

Preferred configuration when GPSD supplies PPS

If GPSD detects and handles the receiver's PPS signal, add a SOCK reference clock using the appropriate device name:

refclock SOCK /run/chrony.ttyUSB0.sock refid GPS poll 2 filter 4

Replace ttyUSB0 with ttyACM0 or the actual basename returned earlier.

This is the simplest configuration because GPSD combines the receiver data and PPS before sending measurements to Chrony.

Alternative configuration using /dev/pps0 directly

Where the Linux kernel exposes /dev/pps0 and Chrony will read it directly, configure separate message and PPS sources:

refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SOCK /run/chrony.clk.ttyUSB0.sock offset 0.5 delay 0.1 refid NMEA noselect

This configuration tells Chrony:

  • Use /dev/pps0 for the precise pulse.
  • Use the GPSD message socket to identify the second.
  • Lock the PPS source to the source named NMEA.
  • Do not select the less accurate message source on its own.

Configuration without PPS

If the receiver does not expose PPS, use message-based GPS timing:

refclock SOCK /run/chrony.clk.ttyUSB0.sock offset 0.5 delay 0.1 refid GPS

This requires GPSD 3.25 or later for the message SOCK interface. Older versions normally use shared memory:

refclock SHM 0 offset 0.5 delay 0.1 refid GPS

Use SOCK where the installed GPSD and Chrony versions support it. Shared memory remains relevant on older distributions but should not be selected simply because an old guide uses it.

Allow local network clients

Chrony does not serve arbitrary clients merely because it is synchronising the local computer. Add an allow directive for each trusted local subnet.

For example:

allow 192.168.1.0/24
allow 192.168.10.0/24

Do not use a broad allow directive unless the server is on a controlled network and that is genuinely intended. There is normally no reason to expose a home-lab NTP server to the Internet.

Recommended supporting directives

Confirm that the file contains appropriate supporting directives:

driftfile /var/lib/chrony/chrony.drift
makestep 0.1 3
rtcsync

On some distributions, the drift filename or directory differs. Keep the existing distribution value rather than replacing it unnecessarily.

The completed relevant section may resemble:

pool 2.debian.pool.ntp.org iburst

refclock SOCK /run/chrony.ttyUSB0.sock refid GPS poll 2 filter 4

allow 192.168.1.0/24
allow 192.168.10.0/24

makestep 0.1 3
rtcsync

This example assumes that GPSD supplies combined GPS and PPS measurements through chrony.ttyUSB0.sock.

Chrony must start before GPSD

Chrony creates the SOCK file when it starts. GPSD must start afterwards so that it can connect to that socket.

Restart both services in that order:

sudo systemctl restart chrony
sudo systemctl restart gpsd

Check both:

systemctl status chrony gpsd --no-pager

After a reboot, confirm that the start order remains correct. If GPSD starts before Chrony and does not reconnect, add an appropriate systemd dependency only after checking the unit files on the installed distribution.

Inspect the current unit definitions with:

systemctl cat chrony
systemctl cat gpsd

A distribution-specific systemd override may be required during the later build verification. Do not copy an override blindly because service names and socket activation vary between releases.

Confirm the socket exists

sudo ls -l /run/chrony*.sock

Look for the socket specified in chrony.conf.

If the expected socket does not exist, check:

  • The device basename in the filename.
  • The Chrony configuration syntax.
  • The owner and permissions of the runtime directory.
  • The Chrony service log.
  • Whether Chrony started before GPSD.
sudo journalctl -u chrony -u gpsd --since "10 minutes ago"

Check the available Chrony sources

chronyc sources -v

A typical source display includes:

  • ^ sources for network NTP servers.
  • # for a local reference clock such as GPS or PPS.
  • * beside the source currently selected.
  • + beside acceptable sources combined with the selected source.
  • ? where a source is unreachable or has insufficient samples.

During startup, the GPS source may initially display ?. Allow time for the receiver, GPSD and Chrony filters to gather enough samples.

Confirm Stratum 1 operation

chronyc tracking

When the GPS reference clock is selected, look for:

  • Reference ID corresponding to the configured refid.
  • Stratum 1.
  • A recent reference time.
  • A small system-time offset.
  • A normal leap status.

The output may resemble:

Reference ID    : 47505300 (GPS)
Stratum         : 1
Ref time (UTC)  : Tue Aug 04 19:42:18 2026
System time     : 0.000001234 seconds slow of NTP time
Leap status     : Normal

Exact values change continuously. The important points are a current reference time, normal leap status and the intended source.

Check source stability

chronyc sourcestats -v

This command shows how stable each source has been over its collected samples.

Do not expect the first output immediately after startup to demonstrate final accuracy. Leave the server running and review it again after the receiver and system clock have settled.

Confirm that Chrony is listening for clients

sudo ss -lunp | grep ':123'

Chrony should be listening on UDP port 123. If it is not, check the allow directives and Chrony service logs.

Open the firewall

Where UFW is enabled, allow NTP only from the trusted subnet:

sudo ufw allow from 192.168.1.0/24 to any port 123 proto udp

Repeat for each authorised subnet.

Check the rules:

sudo ufw status numbered

If another firewall is in use, apply the equivalent restricted UDP 123 rule.

Test the server from Windows

From a Windows computer on an allowed subnet:

w32tm /stripchart /computer:192.168.1.20 /samples:5 /dataonly

Replace the IP address with the Chrony server address.

A successful test confirms that the Windows computer can resolve or reach the server and receive NTP replies. It does not change the Windows Time configuration.

Create a stable internal DNS name

Do not configure approximately 125 devices with the physical Linux hostname. Create a service name such as:

time.example.com

In the environment that prompted this article, the internal name is:

time.sembee.net

Every Windows system, Linux host, hypervisor, switch, firewall, NAS and other device can use that name.

If the GPS server is replaced, only the DNS record needs changing. If the local server is temporarily removed, the service name can be redirected to another internal NTP server or, after testing compatibility, to an external source.

Configure the forest root PDC Emulator

In an Active Directory forest, only the PDC Emulator in the forest root domain should normally use the Chrony server directly.

Configure it with the stable DNS name:

w32tm /config /manualpeerlist:"time.example.com,0x8" /syncfromflags:manual /reliable:yes /update
w32tm /resync /rediscover

Verify:

w32tm /query /source
w32tm /query /status
w32tm /query /configuration

Other domain controllers and domain members should continue to follow the Active Directory hierarchy.

Configure standalone Windows systems

A standalone or workgroup Windows system can use the Chrony server directly:

w32tm /config /manualpeerlist:"time.example.com,0x8" /syncfromflags:manual /update
w32tm /resync

Verify the selected source afterwards.

Install TICC-DASH

TICC-DASH provides a web view of the clients that have contacted Chrony. It is particularly useful for Windows administrators because it gives a quick visual answer to questions such as:

  • Which clients are reaching the server?
  • When was each client last seen?
  • Are requests being dropped?
  • Are both IPv4 and IPv6 clients appearing?

The project's current installer is:

curl -fsSL https://raw.githubusercontent.com/anoniemerd/ticc-dash/main/install_ticc_dash.sh | bash

Before running a script directly from the Internet on a production system, review the script and project repository.

The installer creates the application beneath /opt/ticc-dash, installs a systemd service and starts the interface on TCP port 5000.

Open:

http://SERVER-IP:5000/

Check the service if the page does not load:

sudo systemctl status ticc-dash.service
sudo journalctl -u ticc-dash.service -f

The dashboard uses chronyc clients. Confirm the underlying command manually if the dashboard contains no entries:

sudo chronyc clients

Secure the web interface

TICC-DASH listens on port 5000 by default and does not provide the same access controls as a full management platform.

Keep it on a trusted management network, restrict access with a host or network firewall, or place it behind a reverse proxy with authentication and HTTPS.

Do not expose the interface directly to the Internet.

Monitor the NTP service

The following local checks are useful:

chronyc tracking
chronyc sources -v
chronyc sourcestats -v
chronyc clients

An external monitor should test the service independently. Uptime Kuma 2.5 and later includes an NTP monitor that can query the server and evaluate stratum, offset and root dispersion.

Remember that an external offset measurement is relative to the monitor's own clock. The Uptime Kuma host must therefore have a trustworthy time source of its own.

What to check after a reboot

Restart the complete server and repeat the important checks:

systemctl status chrony gpsd ticc-dash --no-pager
cgps -s
chronyc sources -v
chronyc tracking
chronyc clients

Confirm that:

  • GPSD reopened the correct persistent device.
  • The Chrony socket exists.
  • The GPS source becomes reachable.
  • Chrony returns to Stratum 1 when GPS is selected.
  • The web interface starts automatically.
  • Windows clients can still query the stable DNS name.

Common problems

The GPS source remains marked with a question mark

Check cgps -s, the socket path, service start order and recent logs. Allow enough time for the receiver and Chrony filters to collect samples.

The socket does not exist

Check the exact ttyUSB or ttyACM basename, the Chrony configuration and runtime-directory permissions. Restart Chrony before GPSD.

Chrony sees NMEA but not PPS

Confirm that /dev/pps0 exists and ppstest receives pulses. Some USB receivers advertise PPS in product descriptions but do not expose it through a Linux-compatible USB interface.

Windows stripchart works but normal synchronisation fails

Check Windows peer mode, UDP 123 firewall handling and the effective W32Time configuration. stripchart and normal Windows Time synchronisation do not use the UDP source port identically.

TICC-DASH is empty

Run sudo chronyc clients. If that is also empty, no clients have contacted the server or Chrony is not permitting the client subnet.

The server is Stratum 2 rather than Stratum 1

Chrony may currently be using an Internet source rather than GPS. Check the source markers in chronyc sources -v and determine why the GPS source was not selected.

What the completed build provides

The finished server provides:

  • A local GPS-backed reference independent of Internet connectivity.
  • PPS precision where supported by the receiver and hardware.
  • A standard NTP service for Windows, Linux and network devices.
  • A stable DNS name that can outlive the physical server.
  • A browser view of clients through TICC-DASH.
  • Independent monitoring through an NTP-aware monitoring platform.

The result is more elaborate than simply pointing Windows at the NTP Pool, but it is a useful way for a Windows administrator to understand what sits above the forest root PDC Emulator and how a local Stratum 1 service is built.

Project documentation

Related information