How to synchronize two Raspberry Pi 5s using LinuxPTP¶
The Raspberry Pi 5 supports hardware timestamping of network packets transmitted and received by its Ethernet interface.
This timestamping is handled by the PTP Hardware Clock (PHC), a separate clock dedicated to the network interface.
This clock is exposed by the network driver to the Linux kernel, and shows up as /dev/ptp0.
PTP (Precision Time Protocol) is a standardized protocol for synchronizing PHCs over Ethernet.
LinuxPTP provides the ptp4l application to do this synchronization.
In addition to synchronizing PHCs, one can synchronize the system clock to the PHC or vice versa using the phc2sys utility. In this guide, we will take you through the steps of using LinuxPTP to synchronize the system clocks of two Raspberry Pi 5s.
Raspberry Pi 5 clock topology¶
Prerequisites¶
2x Raspberry Pi 5 (with shell access)
1x Ethernet Cable
Install LinuxPTP¶
LinuxPTP is available on Ubuntu as either a deb or a snap package. You can use either package to work through this guide.
Install the deb package:
sudo apt install linuxptp
Install the snap:
sudo snap install linuxptp
Add aliases for the snap’s apps:
sudo snap alias linuxptp.hwstamp-ctl hwstamp_ctl
sudo snap alias linuxptp.nsm nsm
sudo snap alias linuxptp.phc-ctl phc_ctl
sudo snap alias linuxptp.phc2sys phc2sys
sudo snap alias linuxptp.pmc pmc
sudo snap alias linuxptp.ptp4l ptp4l
sudo snap alias linuxptp.timemaster timemaster
sudo snap alias linuxptp.ts2phc ts2phc
sudo snap alias linuxptp.tz2alt tz2alt
Set up your network¶
LinuxPTP uses either layer 2 or layer 3 networking. In this case, layer 3, IP networking is used to handle communication between the two Raspberry Pis which are connected directly to each other. You can use a switch between the two devices, but only if it supports the PTP protocol.
Note
In the rest of this guide, the two devices are referred to as Pi A and Pi B.
Assign static IP addresses to the two Pis, so they can communicate using the direct link. You can assign 10.0.0.1 to Pi A, and 10.0.0.2 to Pi B.
On Ubuntu Desktop, Ethernet is managed by Network Manager. You’ll need to remove the interface from Network Manager so you can manually specify the address. To do this, Run:
sudo nmcli dev set eth0 managed no
Assign each device a static IP and bring up the interfaces.
On Pi A:
sudo ip addr add 10.0.0.1/24 dev eth0
sudo ip link set dev eth0 up
On Pi B:
sudo ip addr add 10.0.0.2/24 dev eth0
sudo ip link set dev eth0 up
On each device, ping the other to make sure layer 3 IP networking is functional.
On Pi A:
ubuntu@pi-a:~$ ping 10.0.0.2PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.275 ms64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.224 ms64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.222 ms^C--- 10.0.0.2 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 2046msrtt min/avg/max/mdev = 0.222/0.240/0.275/0.024 msOn Pi B:
ubuntu@pi-b:~$ ping 10.0.0.1PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.235 ms64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.216 ms64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.225 ms^C--- 10.0.0.1 ping statistics ---3 packets transmitted, 3 received, 0% packet loss, time 2039msrtt min/avg/max/mdev = 0.216/0.225/0.235/0.007 msStart PTP synchronization¶
PTP uses a server-client architecture. When a client or multiple clients synchronize against a server, they collectively form the time domain. By default, PTP uses the best master clock algorithm (BMCA) to choose which device should be the server for the time domain. However, in the demonstration below, Pi A is manually configured to be the server and Pi B the client.
Start server¶
Start the PTP server on Pi A:
sudo ptp4l -i eth0 --verbose 1 --use_syslog 0 --step_threshold 1 --hwts_filter full --neighborPropDelayThresh 17000 --serverOnly 1
In this case, the default configurations of ptp4l are unchanged, except for these overrides:
-i eth0specifies the Ethernet interface to use.--verbose 1prints logs to stdout.--use_syslog 0prevents logs from being written to the syslog.--step_threshold 1tellsptp4lto step the clock when large time jumps occur. This is optional, but useful during testing.--hwts_filter fulltells the network driver to always timestamp network packets.--neighborPropDelayThresh 17000increases LinuxPTP’s threshold so that it’s above the constant path delay between the two Raspberry Pi 5s (measured at around 16.9 microseconds).--serverOnly 1tellsptp4lto only run in server mode.
Wait for the server to print out assuming the grand master role before starting the client.
For a detailed description of all configuration options, refer to the ptp4l man page .
Start client¶
On Pi B, start the client:
sudo ptp4l -i eth0 --verbose 1 --use_syslog 0 --step_threshold 1 --hwts_filter full --neighborPropDelayThresh 17000 --clientOnly 1
The configurations for the client are identical to the server’s, except for the last argument which changes to --clientOnly 1 to force ptp4l to always run in client mode.
Many logs will be printed on the client device once it’s running. A few of these are discussed below.
If the client found the server on the network, it will print:
port 1 (eth0): new foreign master 2ccf67.fffe.1cbba1-1selected best master clock 2ccf67.fffe.1cbba1You will also see messages about the internal state transitions of ptp4l:
ptp4l[24493.428]: port 1 (eth0): INITIALIZING to LISTENING on INIT_COMPLETE...ptp4l[24498.921]: port 1 (eth0): LISTENING to UNCALIBRATED on RS_SLAVE...ptp4l[24502.922]: port 1 (eth0): UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTEDCrucially, you will also see logs of the time offsets between the client and the server:
ptp4l[24500.921]: master offset 21663 s0 freq +7336 path delay 16925ptp4l[24501.921]: master offset 21594 s1 freq +7267 path delay 16925ptp4l[24502.922]: master offset -1 s2 freq +7266 path delay 16925ptp4l[24503.922]: master offset 4 s2 freq +7270 path delay 16922ptp4l[24504.922]: master offset 2 s2 freq +7270 path delay 16922ptp4l[24505.922]: master offset 0 s2 freq +7268 path delay 16920ptp4l[24506.922]: master offset -18 s2 freq +7250 path delay 16917ptp4l[24507.922]: master offset 27 s2 freq +7290 path delay 16909The time offset will likely start at a very high number, jump rapidly to a smaller number, and then settle on a number around 0±100. Two systems are generally considered synchronized with PTP when their clocks differ by less than 100 nanoseconds. It may take tens of seconds before the offset settles in this range.
Synchronize system clock¶
On Pi A, start phc2sys, synchronizing from the system clock to eth0’s PHC:
sudo phc2sys -s CLOCK_REALTIME -c eth0 -O 0 --step_threshold 1 -m -q
-sspecifies that the source is the system clock, a.k.aCLOCK_REALTIME.-cspecifies the clock that needs to be synchronized. In this case it is eithereth0, or its device node/dev/ptp0.-O 0tells the synchronizer that it should target a zero second offset between the two clocks. You can add an offset here, for example to account for leap seconds.--step_threshold 1(optional) tellsphc2systo step the clock when large time jumps occur.-menables printing log messages to stdout.-qdisables printing to the syslog.
For a detail description of all available configurations, refer to the phc2sys man page.
Note
phc2sys has many features to automatically synchronize between clocks such as selecting the best source to synchronize from, and automatically handling leap seconds and daylight savings time.
The arguments we use here disable all these features, and use constant offsets.
This is to show a minimal setup of what this utility does.
After running the phc2sys command, the output should show the difference between eth0’s clock and the system clock, and over time it should decrease to the range 0±100.
You will also notice time jumps being reported by ptp4l on Pi B.
This is because synchronization is being done from A’s system clock, to A’s PHC, to B’s PHC.
Therefore, any clock adjustments made by phc2sys on Pi A are propagated to Pi B.
To synchronize Pi B’s system clock from its PHC, run this command:
sudo phc2sys -s eth0 -c CLOCK_REALTIME -O 0 --step_threshold 1 -m -q
The only difference between this command and the phc2sys command on Pi A is the source clock and destination clock are swapped around.
After running the command, the terminal will print the offset statistics.
Verify time synchronization¶
With the setup above, Pi A’s system clock is synchronized to PI A’s PHC which is then synchronized over the network to Pi B’s PHC. Finally, Pi B’s PHC is synchronized to Pi B’s system clock.
Therefore, Pi A’s and Pi B’s system clocks should be synchronized.
You can verify this by running the date command on both devices and checking if their clocks report the same time.
If the devices are set up in different time zones, they will still report the same time, offset by the number of hours between the two time zones.
On Pi A:
ubuntu@pi-a:~$ dateMon Apr 7 14:42:36 UTC 2025On Pi B:
ubuntu@pi-b:~$ dateMon Apr 7 16:42:36 SAST 2025If Pi A’s system clock is incorrect, you can manually adjust it:
ubuntu@pi-a:~$ sudo date --set="2025-01-01 00:00:00"Wed Jan 1 00:00:00 UTC 2025Large time jumps will take longer to synchronize.
However, if you use --step_threshold 1, it should only take a few seconds for all the systems in the PTP time domain to be synchronized again.
You can also manually synchronize the time from the internet using the ntpdate tool from the chrony package:
ubuntu@pi-a:~$ sudo ntpdate pool.ntp.org2025-04-07 14:51:36.570167 (+0000) +8347851.831618 +/- 0.050685 pool.ntp.org 196.10.98.182 s1 no-leapCLOCK: time stepped by 8347851.831618CLOCK: time changed from 2025-01-01 to 2025-04-07Alternatively, you can use Chrony or ntpd to continuously synchronize your system clock from the internet, and then use PTP to distribute the time and synchronize via Ethernet.
You can also follow our guide Combine NTP and PTP for redundant time synchronization to use a combination of PTP and NTP for redundancy.