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/ttyUSB0normally corresponds to/run/chrony.ttyUSB0.sockfor PPS./dev/ttyACM0normally corresponds to/run/chrony.ttyACM0.sockfor 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/pps0for 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
- Chrony FAQ: configuring chronyd with GPSD
- Chrony configuration examples
- Chrony configuration reference
- GPSD documentation
- TICC-DASH installation
- TICC-DASH on GitHub