How to Set Up Chrony as a Local NTP Server Using Docker

chrony

In a local network where you want to keep your devices synchronized with accurate time, running a lightweight and efficient NTP server is essential. Chrony, a modern alternative to ntpd, is a great choice and in this guide, I’ll show you how to set it up inside a Docker container that fetches time from global sources and distributes it across your LAN.


🚀 Why Chrony?

Chrony is:

  • More accurate than ntpd in many conditions (especially with intermittent connectivity)
  • Lightweight and easy to configure
  • Ideal for both clients and servers

🐳 What You’ll Set Up

  • A Docker container running Chrony
  • Configured to sync with global NTP servers
  • Act as a time server for your LAN
  • With optional logging and control access

🧱 Step 1: Create a Dockerfile for Chrony

Start by creating a simple Dockerfile to build a minimal Chrony container.

# Dockerfile
FROM debian:stable-slim

RUN apt-get update && \
    apt-get install -y chrony && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY chrony.conf /etc/chrony/chrony.conf

EXPOSE 123/udp

CMD ["chronyd", "-d", "-f", "/etc/chrony/chrony.conf"]

⚙️ Step 2: Create the chrony.conf

Here’s a sample chrony.conf tailored for local server use and syncing with global time sources:

# chrony.conf

# Time sources (use pool.ntp.org or your regional servers)
server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
server 2.pool.ntp.org iburst

# Allow all clients on your LAN (edit this according to your subnet)
allow 192.168.1.0/24

# Local stratum fallback if Internet is down
local stratum 10

# Drift file to track clock error over time
driftfile /var/lib/chrony/chrony.drift

# Log tracking data
log tracking measurements statistics

# Log files location
logdir /var/log/chrony

# Optional: control access
cmdport 0  # Use 0 to disable remote control; use 323 if needed

Replace 192.168.1.0/24 with your actual LAN subnet.


🧪 Step 3: Build and Run the Docker Container

docker build -t chrony-server .

Now run the container with:

docker run -d \
  --name chrony \
  --restart unless-stopped \
  --network host \
  --cap-add=NET_BIND_SERVICE \
  chrony-server

✅ Explanation:

  • --network host allows the container to bind directly to port 123/UDP
  • --cap-add=NET_BIND_SERVICE is required to bind to low-numbered ports like 123

🔎 Step 4: Test Your NTP Server

From a client machine on your LAN:

ntpdate -q <chrony-server-ip>

or

chronyc sources -a

You should see that the time is being served and synchronized.


📌 Optional: Run as a Local Time Authority

If you want to run fully offline, or ensure internal time continuity even without internet:

  1. Remove the server lines from chrony.conf
  2. Set: local stratum 8
  3. Start the server with a stable internal clock source

This makes your Chrony instance a local time authority for your network.


🔐 Firewall Notes

Make sure UDP port 123 is allowed inbound from your LAN on your Docker host:

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

Or for iptables:

iptables -A INPUT -p udp -s 192.168.1.0/24 --dport 123 -j ACCEPT

📎 Conclusion

With this setup, you’ve created a portable, containerized NTP server using Chrony that:

  • Syncs with global servers
  • Serves accurate time to all local devices
  • Works even if your external internet connection drops

Perfect for homelabs, IoT networks, or offline environments.

Post Comment

You May Have Missed