Deployment guide

Chrony

Videos

Overview: Chrony NTP Time Server on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)

Launch and log in: Chrony NTP Time Server on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)

Deep Dive: Chrony NTP Time Server on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)

Overview

This guide covers the deployment and configuration of Chrony on Ubuntu 24.04 on Azure using SecureAnyCloud Azure Marketplace images. Chrony is a versatile, high-accuracy implementation of the Network Time Protocol (NTP) and the modern default time daemon on most Linux systems.

This image ships Chrony as a self-hosted NTP time server for a network. The chronyd service disciplines the local clock from upstream time sources and serves precise time to other machines on UDP port 123.

Accurate time is foundational infrastructure. TLS certificate validation, Kerberos and Active Directory authentication, distributed databases, log correlation and audit timestamps all break in subtle ways when clocks drift. Chrony keeps this server, and every client that synchronises against it, correct to well under a millisecond. It also recovers from network interruptions and large offsets far better than the legacy ntpd it replaces.

The image runs Chrony 4.5 from the official Ubuntu package under systemd (chrony.service). There is no web interface and no database. /etc/chrony/chrony.conf is the whole configuration, and you administer the server from the command line with chronyc.

Secure by default, not an open amplifier. A naively deployed public NTP server can be abused for reflection and amplification attacks. This image avoids that. The shipped configuration serves NTP only to the local host and the private RFC1918 network ranges, through a documented and scoped allow list rather than allow all. A query arriving from a public internet address is simply dropped.

The chronyc command interface is bound to the local host only, and Chrony has no legacy monlist command at all. An NTP server has no login, so no shared or default credential of any kind ships in the image.

What you get:

  • Chrony 4.5 from the official Ubuntu package, run under systemd as chrony.service
  • An NTP service on 0.0.0.0:123/udp, serving the local host and your private networks
  • On Azure, the Hyper-V host hardware clock, added automatically on first boot as a high-accuracy PTP reference clock, giving sub-millisecond, network-independent synchronisation
  • The public NTP pool configured as a resilient fallback time source
  • A scoped allow list (loopback plus the RFC1918 private ranges), so the server is not an open amplifier until you deliberately widen it to your own subnets
  • The chronyc command and monitoring interface bound to the local host only, with no amplification vectors

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet plus subnet in the target region
  • A subscription to the Chrony listing on Azure Marketplace
  • Network Security Group rules allowing UDP 123 (NTP) from the clients that will synchronise against the server, and TCP 22 for administration

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a departmental or virtual network time server. Chrony is extremely light, so larger sizes are only needed for very large client populations.

1

Deploy from the Azure Portal

Search for Chrony in the Marketplace, select the SecureAnyCloud publisher, and click Create.

Configure the Network Security Group to allow UDP 123 from the clients that will synchronise against the server, and TCP 22 for administration. Keep UDP 123 restricted to the subnets you serve. There is no need to expose it to the public internet for a private-network time server.

2

Deploy from the Azure CLI

RG="time-prod"; LOCATION="eastus"; VM_NAME="chrony1" IMAGE_URN="sac1647283583153:chrony-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:chrony-ubuntu-24-04:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 123 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002

Open UDP 123 only to the client subnets you intend to serve.

3

First boot

On first boot, the image detects the Azure Hyper-V host clock and adds it as a high-accuracy PTP reference. It also resolves this VM's address, writes the informational note /root/chrony-info.txt, and restarts chrony so it serves time within seconds. SSH in as azureuser and read the note:

sudo cat /root/chrony-info.txt

The note records this server's role, the address and port it serves, the detected PTP reference clock, and the path to the configuration. Chrony has no password, so the note holds no secret.

4

Confirm the service is running

chrony.service is active, and chronyd --version reports 4.5. The ss command confirms that chronyd is listening on UDP 123 on all interfaces, ready to answer clients on your allowed networks.

systemctl is-active chrony.service chronyd --version sudo ss -ulnp | grep -E ':123'

The ss output shows chronyd bound to 0.0.0.0:123 and [::]:123, the NTP server port. The command port 323 is bound only to 127.0.0.1 and ::1, so it is never reachable from off the machine.

5

Confirm the clock is synchronised

chronyc tracking reports the server's own synchronisation state. Leap status : Normal means the server is synchronised and safe to serve time. The Reference ID and Stratum identify the upstream source the server is locked to. The System time and RMS offset lines show how tightly the clock is disciplined, typically well under a millisecond.

chronyc tracking

On Azure, the server locks to the Hyper-V host clock (shown as PHC0, stratum 0 to 1) or to a stratum 2 NTP pool server. It reports Leap status : Normal with a sub-millisecond system time offset.

6

Review the time sources

chronyc sources lists every source the server is disciplining from, with a state marker in the first column:

  • * is the currently selected best source.
  • + is a combined source.
  • # marks a local hardware reference clock such as the Azure PHC.
  • ? or - are sources that are not currently selected.

chronyc sourcestats shows the measured offset and skew for each source.

chronyc sources chronyc sourcestats

On Azure, you see the PHC0 hardware reference clock alongside several ntp.ubuntu.com and pool servers, with one source marked * as the current best.

7

Confirm it serves time and is not an open amplifier

This is the security posture that matters for an NTP server. The shipped allow list answers only the local host and the RFC1918 private ranges, so a query from a public source address is dropped.

Read the allow list and confirm that the command port is loopback only. Then use the bundled self-check to prove that the server answers a real NTP client query on port 123.

sudo grep -E '^(allow|cmdport|bindcmdaddress)' /etc/chrony/chrony.conf
sudo ss -ulnp | grep -E ':323' python3 /usr/local/sbin/ntp-selfcheck.py 127.0.0.1

The allow lines are scoped to 127.0.0.1, ::1 and the private ranges, and never to allow all. The command port 323 is bound only to loopback. The self-check sends a real NTP client request to port 123 and prints OK with the returned time. This proves that the server serves NTP while remaining scoped to your private networks.

8

Point your clients at the server

Configure your other machines to use this instance as their NTP server, using its private IP address (shown in the info note from Step 3). Run the examples below on each client, not on this server.

# --- Linux client running chrony ---
# Add a line to the client's /etc/chrony/chrony.conf, then restart chrony: # server <server-ip> iburst # # --- Linux client running systemd-timesyncd --- # Set NTP=<server-ip> in /etc/systemd/timesyncd.conf, then: # sudo systemctl restart systemd-timesyncd # # --- Windows client --- # w32tm /config /manualpeerlist:"<server-ip>" /syncfromflags:manual /update # # Verify from a Linux client that it is using this server: # chronyc sources # the server should appear, selected with a '*'

Replace <server-ip> with this instance's private IP address. The default allow list already covers the RFC1918 private ranges, so clients on your virtual network can synchronise immediately with no change on the server.

9

Widen the allow list to your network

The shipped allow list covers the whole RFC1918 private space. To narrow it to your exact virtual network, or to serve a network outside RFC1918, edit the allow lines in /etc/chrony/chrony.conf and restart the service.

Only add networks you control. Never use allow all, which turns the server into an open amplifier.

# Edit /etc/chrony/chrony.conf and set the allow list to your exact VNet CIDR, e.g.
# allow 10.20.0.0/16
# Remove the broader ranges you do not need, then apply the change:
#
sudo systemctl restart chrony
# Confirm the new scope:
#
sudo grep -E '^allow' /etc/chrony/chrony.conf

Chrony validates the configuration on restart. If there is a syntax error, the service reports it in systemctl status chrony, so you can correct it before clients are affected.

10

Review the configuration and info note

Everything Chrony does is configured in /etc/chrony/chrony.conf, and the per-instance details are in the info note. Read both with sudo.

sudo cat /etc/chrony/chrony.conf
sudo cat /root/chrony-info.txt

The info note is 0600 root:root and holds no secret, because an NTP server has no login. It records the server's role, the address it serves, and the detected PTP reference for convenience.

11

Security notes

  • Restrict the NSG to your use case. Allow UDP 123 only from the client subnets you serve. There is no need to expose a private-network time server to the public internet.
  • Keep the allow list tight. Only add subnets you control to the allow lines. Never use allow all, because that turns the server back into an open amplifier that can be abused for reflection attacks.
  • Leave the command port loopback only. The chronyc command interface is bound to 127.0.0.1 and ::1, so it cannot be reached or reflected from off the machine. Keep it that way, and administer the server over SSH.
  • Prefer the hardware reference on Azure. The Hyper-V host clock (PHC0) is a stratum 0 reference disciplined to Microsoft's time sources, and it is the most accurate option. It is configured automatically on first boot.
  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
12

Support and licensing

Chrony is an open-source project distributed under the GNU General Public License version 2. This SecureAnyCloud image bundles the unmodified official Chrony package from Ubuntu.

SecureAnyCloud provides the packaging, the systemd hardening, the secure-by-default scoped allow configuration, the Azure PTP reference clock automation, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA.

SecureAnyCloud is not affiliated with or endorsed by the Chrony project. Chrony is a mark of its respective owner and is used here only to identify the software.

Deploy on Azure

Find Chrony NTP Time Server on Ubuntu 24.04 LTS on the Azure Marketplace, published by SecureAnyCloud. Deploy it from the Portal or the Azure CLI, as shown above.

Need Help?

Email sales@secureanycloud.com for deployment help, configuration questions, or licensing enquiries.

Deploy on Azure

Launch Chrony 4.5 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure