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
allowlist (loopback plus the RFC1918 private ranges), so the server is not an open amplifier until you deliberately widen it to your own subnets - The
chronyccommand 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.
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.
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 1002Open UDP 123 only to the client subnets you intend to serve.
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.txtThe 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.
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.
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 trackingOn 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.
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 sourcestatsOn Azure, you see the PHC0 hardware reference clock alongside several ntp.ubuntu.com and pool servers, with one source marked * as the current best.
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.1The 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.
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.
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.confChrony 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.
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.txtThe 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.
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
allowlines. Never useallow 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
chronyccommand interface is bound to127.0.0.1and::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.
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
Overview
This guide covers the deployment and operation of Chrony on AWS using SecureAnyCloud AWS Marketplace images. Chrony is a versatile, high-accuracy implementation of the Network Time Protocol (NTP) and the modern default time daemon on most Linux distributions.
The image installs Chrony from the official Ubuntu package and runs it under systemd as the standard chronyd service, so a working, secure NTP time server answers queries within minutes of launch. Chrony serves NTP on UDP port 123 across all interfaces. It disciplines the local clock from the Amazon Time Sync Service and the public NTP pool, and you administer it entirely from the command line with chronyc.
Accurate time is foundational infrastructure. TLS certificate validation, Kerberos and Active Directory authentication, distributed databases, log correlation, audit timestamps and scheduled jobs 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 gracefully from network interruptions and large initial offsets.
Secure by default, not an open amplifier. 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 and monitoring interface is bound to the local host only, so there is no remote runtime control and no command protocol reflection. Chrony has no legacy monlist command at all. There are no shared or default credentials of any kind, since an NTP server has no login.
What you get:
- Chrony 4.5 (official Ubuntu package), run under systemd as the standard
chronydservice (chrony.service) - An NTP time server on UDP port 123, disciplined by the Amazon Time Sync Service (link-local
169.254.169.123) as the preferred high-accuracy source, with the public NTP pool as fallback - A secure-by-default configuration: NTP served only to loopback and the private RFC1918 ranges, a scoped allow list rather than allow all, and the
chronyccommand port bound to loopback only - A per-instance information note rendered on first boot, with no baked credential of any kind in the image
- Unattended security upgrades left enabled, so the server keeps receiving patches
Prerequisites
- An active AWS account, an EC2 key pair, and a VPC and subnet in the target region
- A subscription to the Chrony listing on AWS Marketplace
- A security group allowing TCP 22 (admin) and, from the client subnets you intend to serve, UDP 123 (NTP time service)
Recommended instance type: m5.large (2 vCPU, 8 GB RAM). A time server is very light, so a smaller instance also serves comfortably.
Launch from AWS Marketplace
Find Chrony on AWS Marketplace, published by SecureAnyCloud. Choose Continue to Subscribe, then Continue to Configuration, then Continue to Launch.
Select the Ubuntu 24.04 delivery option, your instance type (m5.large) and your key pair. Choose a security group that allows TCP 22 for administration plus UDP 123 from the client networks you want to serve.
Connecting to your instance
SSH in with your EC2 key pair. For Ubuntu 24.04, the SSH login user is ubuntu, for example: ssh -i your-key.pem ubuntu@<public-ip>
ssh -i your-key.pem ubuntu@<public-ip>First boot
On first boot, the image resolves this instance's address via EC2 instance metadata (IMDSv2) and writes a per-instance information note to /root/chrony-info.txt. Then systemd starts chronyd.
Chrony begins disciplining the clock from the Amazon Time Sync Service and the NTP pool immediately, because time synchronisation never waits on first boot. This completes within a minute.
Confirm Chrony is synchronised
Confirm that the service is active and synchronised to a real time source. chronyc tracking reports the reference source, the stratum, the current offset, and a Leap status of Normal once the clock is disciplined:
sudo systemctl start chrony 2>/dev/null || true systemctl is-active chrony chronyc trackingThe service reports active, the Reference ID shows 169.254.169.123 (the Amazon Time Sync Service), the offset is a few microseconds, and Leap status reads Normal.
Review the upstream time sources
chronyc sources -v lists every source Chrony is disciplining from and marks the currently selected best source with ^*:
chronyc sources -vThe Amazon Time Sync Service (169.254.169.123) is selected as the current best source (^*), with the public NTP pool servers as reachable candidates (^-).
Confirm the server is answering NTP clients
Prove that the server is actually serving time. chronyc serverstats counts the NTP requests it has answered. chronyc clients lists the clients that have queried it. ss shows it listening on UDP 123 across all interfaces.
The bundled self-check sends a real NTP client request to the server on port 123 and validates the reply:
sudo chronyc serverstats
sudo chronyc clients ss -lun 'sport = :123' python3 /usr/local/sbin/ntp-selfcheck.py 127.0.0.1NTP packets received is non-zero, the client list shows the queries served, the server is listening on 0.0.0.0:123 and [::]:123, and the self-check prints a valid NTP timestamp. The server is answering clients.
Point your clients at this server
Point your servers, workstations and network devices at this instance as their NTP server, using its private IP. Open UDP 123 to your client subnets in the instance security group. On a Linux client running chrony, add a line to /etc/chrony/chrony.conf:
server <this-server-private-ip> iburstOn a Linux client running systemd-timesyncd, set NTP=<this-server-private-ip> in /etc/systemd/timesyncd.conf. Windows and network devices take the same server address in their NTP client settings.
Keep the security group rules as tight as your use case allows. Open UDP 123 to specific client subnets, not to 0.0.0.0/0.
Widen the allow list to your own subnet
By default the server answers NTP only for loopback and the private RFC1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). If your clients sit outside those ranges, add an allow line for your exact client CIDR to /etc/chrony/chrony.conf (for example with sudo nano /etc/chrony/chrony.conf), so the allow section reads:
allow 127.0.0.1 allow ::1 allow 10.0.0.0/8 allow 172.16.0.0/12 allow 192.168.0.0/16 allow 198.51.100.0/24
# <-- your client CIDRThen apply the change:
sudo systemctl restart chronyNever use allow all, because it turns the server into an open NTP amplifier. Widen the allow list to specific client networks only, and pair it with a matching UDP 123 security group rule scoped to those same networks.
Secure by default, not an open amplifier
The shipped configuration is scoped to private networks, and the command interface is bound to loopback only. You can review the whole posture at a glance:
chronyd --version grep -E '^(server 169|allow|cmdport|bindcmdaddress)' /etc/chrony/chrony.confThe allow list covers loopback and the RFC1918 ranges with no allow all, and cmdport/bindcmdaddress keep the chronyc command port on loopback only. A query from a public source address matches no allow rule and is dropped, so the server cannot be abused for reflection or amplification even if UDP 123 is exposed by the security group.
Managing the service
sudo systemctl status chrony --no-pagerAfter a configuration change, run sudo systemctl restart chrony. The service is enabled at boot, so the server starts serving time automatically on every reboot. The single source of truth for the configuration is /etc/chrony/chrony.conf.
Support
SecureAnyCloud publishes and supports this image. For help, contact SecureAnyCloud support through the AWS Marketplace listing. Chrony is distributed under the GPL-2.0 licence. SecureAnyCloud is not affiliated with or endorsed by the Chrony project.
Deploy on AWS
Launch Chrony on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com