Chrony NTP Time Server on AWS User Guide
Overview
This guide covers the deployment and operation of Chrony on AWS using cloudimg 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 is answering queries within minutes of launch. Chrony serves NTP on UDP port 123 across all interfaces, disciplining the local clock from the Amazon Time Sync Service and the public NTP pool, and is administered 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, and 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, a documented and scoped allow list rather than allow all, so 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, and 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 is included:
-
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; the image ships no baked credential of any kind
-
Unattended security upgrades left enabled so the server keeps receiving patches
Prerequisites
-
Active AWS account, an EC2 key pair, a VPC and subnet in the target region
-
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; a smaller instance also serves comfortably.
Step 1: Launch from AWS Marketplace
Find Chrony on AWS Marketplace, published by cloudimg, and choose Continue to Subscribe, then Continue to Configuration and Continue to Launch. Select the Ubuntu 24.04 delivery option, your instance type (m5.large), your key pair, and a security group that allows TCP 22 for administration plus UDP 123 from the client networks you want to serve.
Step 2: Connecting to your instance
SSH in with your EC2 key pair as the login user for the OS variant you launched.
| OS variant | SSH login user | Example |
|---|---|---|
| Ubuntu 24.04 | ubuntu |
ssh -i your-key.pem ubuntu@<public-ip> |
ssh -i your-key.pem ubuntu@<public-ip>
Step 3: 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 - time synchronisation never waits on first boot. This completes within a minute.
Step 4: Confirm Chrony is synchronised
Confirm 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 tracking
The 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.
Step 5: 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 -v
The Amazon Time Sync Service (169.254.169.123) is selected as the current best source (^*), with the public NTP pool servers as reachable candidates (^-).

Step 6: Confirm the server is answering NTP clients
Prove the server is actually serving time. chronyc serverstats counts the NTP requests it has answered, chronyc clients lists the clients that have queried it, and 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.1
NTP 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.

Step 7: Point your clients at this server
Point your servers, workstations and network devices at this instance as their NTP server, using its private IP, and 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> iburst
On 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.
Step 8: 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 CIDR
Then apply the change:
sudo systemctl restart chrony
Never use allow all - that 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.
Step 9: 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.conf
The 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.

Step 10: Managing the service
sudo systemctl status chrony --no-pager
Reload after a configuration change with 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
This image is published and supported by cloudimg. For assistance, contact cloudimg support through the AWS Marketplace listing. Chrony is distributed under the GPL-2.0 license; cloudimg is not affiliated with or endorsed by the Chrony project.