Local NTP Time Server

Self-Hosting Mar 28, 2021

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

NTP 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.

CyberHost Example

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.

  1. Head to Firewall -> NAT -> Port Forward -> Add

  2. Select the Interface LAN

  3. Protocol TCP/UDP

  4. Invert Match

  5. Select LAN Address

  6. Destination Port Range - NTP(123)

  7. Redirect target IP - PI Hole IP (Probably 192.168.x.x)

  8. 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
allow rule.


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.

Borat Thumbs