Disclaimer: This is massively overkill and you will look like a NERD!
Why on earth would you want a local time server?
Almost all the devices on your network will be contacting an NTP (Network Time Protocol) server to ensure they have the correct time. Typically they will query from every 5 mins to a few hours. When looking through my DNS logs there's is a query going off to time.google.com / time.apple.com / *.pool.ntp.org on average every minute.
What we can do is set up a local NTP server, to reduce all of these outgoing requests.
What is NTP?
NTP or Network Time Protocol is a networking protocol to allow for computers to synchronise their times over the internet.
The protocol works by sending timestamps with each request, an algorithm is then used to calculate the time whilst minimising the effect of network latency.
Fun fact: NTP uses a 64-bit timestamp consisting of a 32-bit part part for seconds and a 32-bit part for fractions of a second. This means NTP has an overflow issue where it rolls over every 136 years. As NTP uses Epoch time (Starting 1 January 1900) the first rollover occurs in 2036. Let's wait to see the havoc this will cause! Year 2038 Problem Wikipedia
What is Stratum?
NTP Servers operate in a hierarchical structure.
A Stratum 0 server is a high-precision timekeeping device, typically this is a GPS receiver however it could be an Atomic Clock. Stratum 0 devices are connected directly to a Stratum 1 server which then operates the NTP server.
A Stratum 2 server typically queries multiple different Stratum 1 servers to synchronise with the time, this improves stability.
Stratum 3 servers synchronise with stratum 2 server, this goes on to stratum 15.
Query a NTP Server
You can query an NTP server using the following command on Linux:
ntpdate -q time.apple.com
Stratum 1 - time.apple.com
Stratum 2 - clock.nyc.he.net
Stratum 3 - time.cloudflare.com
Lists of Public NTP Servers
Setting up the NTP Docker
Now to the fun bit!
In this tutorial, we will be deploying an NTP server using a Docker Image of chrony.
Chrony is a lightweight NTP server that has been designed for unstable environments such as a Virtual Machine or Docker container.
Chrony Setup Docs: https://github.com/cturra/docker-ntp
The example docker-compose provided in the docs synchronise with time.cloudflare.com. This is a Stratum 3 NTP server (making your's a Stratum 4).
You can use the example below which uses a range of Stratum 1 servers and a pre-build docker image.
Start the container with:
sudo docker-compose up -d
Hijacking outgoing NTP requests
Hijacking the NTP requests can be done in two different ways.
DNS - If you have Adguard Home or a pi-Hole running you can set DNS records for the popular time servers to the Local IP address of your NTP server.
Router - The better solution would be to catch all requests on port 123 (NTP) and redirect them to your NTP server.
In the example below, we will be hijacking the requests using a pfSense router.
Head to Firewall -> NAT -> Port Forward -> Add
Select the Interface LAN
Select LAN Address
Destination Port Range - NTP(123)
Redirect target IP - PI Hole IP (Probably 192.168.x.x)
Redirect target port - NTP(123)
Currently, the NTP requests your NTP server uses to synchronise are also getting forwarded to its self. To stop this, we'll create an
Make sure this is placed above the hijacking rule, as rules at the top have higher priority.
We now have a decent understanding of the NTP protocol, the Stratum hierarchy and a local NTP server handling all our requests.