How to Set Up Chrony as a Local NTP Server Using Docker
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:
- Remove the
server
lines fromchrony.conf
- Set:
local stratum 8
- 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