Keep Server Time Accurate with chrony on an Ubuntu VPS
Ubuntu uses chrony to keep the system clock in sync, and chrony is installed by default as of Ubuntu 25.10. On older releases systemd-timesyncd was the default, and chrony takes one apt command to install.
Where Clock Errors and Time Jumps Show Up
TLS certificates
Every X.509 certificate carries a validity period, from notBefore to notAfter. Path validation checks that this period includes the current time, and "current time" means whatever the server's clock says. A VPS running days behind can reject a certificate issued yesterday as not yet valid. A VPS running far ahead can treat a valid certificate as expired.
Secure time sync has the same dependency. Network Time Security (NTS) is based on TLS, and TLS needs a reasonably correct clock, so an NTS-based sync can fail when the clock is too far off. Ubuntu's default setup handles this with a bootstrap server.
Logs
chronyd tracks the drift and offset of the system clock and adjusts it continuously, so there are no large corrections that could lead to inconsistent logs.
cron
Ubuntu's cron handles local time changes of less than three hours in a special way. The special handling covers only jobs that run at a specific time and jobs with a granularity greater than one hour. cron schedules jobs that run more often than that in the normal way.
- After a forward jump, such as one hour, the covered jobs that would have run in the skipped interval run immediately.
- After a backward jump, cron avoids running the same job twice.
cron treats a change of more than three hours as a correction to the clock or the timezone and uses the new time immediately. On a VPS whose clock is corrected by four hours, neither rule above applies.
The Commands
These are the shell commands used in this guide. The makestep and burst commands change chronyd's behaviour, so they run through sudo chronyc. waitsync is a monitoring command and runs without sudo.
1# install
2sudo apt install chrony
3
4# check status
5timedatectl status
6chronyc -N tracking
7chronyc -N sources
8chronyc sourcestats
9sudo chronyc -N authdata
10
11# after editing sources or config
12sudo chronyc reload sources
13sudo systemctl restart chrony.service
14systemctl status chrony.service
15
16# step a large offset, then wait for sync
17sudo chronyc makestep 0.1 1
18sudo chronyc burst 1/2
19chronyc waitsync 60 0.01
On Ubuntu 25.10 and later, /etc/chrony/sources.d/ubuntu-ntp-pools.sources contains these default sources:
1pool 1.ntp.ubuntu.com iburst maxsources 1 nts prefer
2pool 2.ntp.ubuntu.com iburst maxsources 1 nts prefer
3pool 3.ntp.ubuntu.com iburst maxsources 1 nts prefer
4pool 4.ntp.ubuntu.com iburst maxsources 1 nts prefer
5pool ntp-bootstrap.ubuntu.com iburst maxsources 1 nts certset 1
Install chrony on Ubuntu
sudo apt install chrony installs two binaries. chronyd is the daemon that syncs time over the Network Time Protocol (NTP) and can also serve it to other machines. chronyc is the command-line interface to that daemon. The daemon runs as user and group _chrony.
Some systems were upgraded from a release that used timesyncd. On those, systemd-timesyncd might still be the active time daemon and leave the new chrony service disabled. Run systemctl status chrony.service and confirm that chrony is running. Once chrony is installed, timedatectl steps back and leaves timekeeping to chrony.
chrony serves time to other machines only after you set at least an allow rule. A VPS that only needs its own clock to be right needs no such rule.
Check Sync Status
timedatectl status gives the quick answer. On a synced server it shows System clock synchronized: yes and NTP service: active. For the detail behind that answer, run chronyc -N tracking.
Some chronyc commands need sudo and some do not. chronyc first tries the Unix domain socket at /var/run/chrony/chronyd.sock, and sudo gives it access to that admin socket. Without the socket, chronyc can use only the commands chronyd accepts over the network: activity, manual list, rtcdata, smoothing, sourcename, sources, sourcestats, tracking and waitsync. By default chronyd accepts those only from localhost (127.0.0.1 or ::1), and chronyc uses UDP port 323 for them. Every other command in this guide, including authdata, reload sources and makestep, needs sudo.
Three options change how the output reads:
| Option | Effect |
|---|---|
-n | No reverse lookups of IP addresses, which avoids slow DNS queries |
-N | Each source shown by the hostname or IP address written in the configuration file |
-c | Reports in comma-separated values (CSV), easier for a monitoring script to parse than the aligned table |
Read chronyc tracking
The tracking command shows how well the system clock is performing. On Ubuntu the first lines name the source and its stratum, for example Reference ID : B97DBE7B (2.ntp.ubuntu.com) and Stratum : 3. These fields tell you whether the clock is right:
- Reference ID: the source chronyd is synced to.
7F7F0101with no name or IP address means the server is not synchronised to any external source. - Stratum: how many hops the server is from a computer with an attached reference clock.
- System time: the current offset between the NTP clock and the system clock. chronyd normally corrects it by speeding the clock up or slowing it down, up to the rate set by the
maxslewratedirective. This is called slewing. A large offset takes a very long time to correct this way. - Last offset: a positive value means the local time was ahead of the time sources.
- Frequency: how fast the clock would drift if chronyd stopped correcting it, in parts per million (ppm).
- Update interval: the time between the last two clock updates.
- Leap status:
Normal,Insert second,Delete secondorNot synchronised.
The same output gives an upper bound on the clock's error:
clock_error <= |system_time_offset| + root_dispersion + (0.5 * root_delay)
Root delay is the total network path delay back to the stratum-1 computer, and root dispersion is the dispersion accumulated along that chain. The bound assumes the stratum-1 computer itself is correct.
Read chronyc sources and sourcestats
chronyc -N sources lists the time sources in use. Each row starts with two characters. The first is the mode: ^ means a server, = a peer and # a locally connected reference clock. The second is the selection state:
| Symbol | Meaning |
|---|---|
* | The best source, currently selected for synchronisation |
+ | Another selected source, combined with the best one |
- | Selectable, but not selected now |
x | A falseticker, in chronyd's judgement |
~ | Time with too much variability |
? | Not selectable for another reason, such as being unreachable, not synchronised, or short of measurements |
On a healthy VPS one row starts with ^*, a server that chronyd has selected. If no row has a *, the clock has no selected source.
Poll gives the measurement interval as a power of two in seconds, so 6 means a measurement every 64 seconds. A Reach of 377 means a valid reply came back for each of the last eight transmissions. Any other value means at least one of those eight got no valid reply. In Last sample, a positive offset means the local clock is ahead of the source. The number after +/- is the margin of error, the NTP root distance.
chronyc sourcestats is the companion report. For each source, chronyd estimates the drift rate and current offset by fitting a linear regression through its samples.
Confirm NTS Is Working (Ubuntu 25.10 and Later)
Ubuntu 25.10 and later turn NTS on by default through the Ubuntu NTS pools. NTS support itself arrived in chrony 4.0, which first appeared in Ubuntu 21.04. The pools are defined in /etc/chrony/sources.d/ubuntu-ntp-pools.sources. That file holds four ntp.ubuntu.com pools marked nts prefer and one ntp-bootstrap.ubuntu.com pool. With iburst on each line, chronyd starts with a burst of 4-8 requests, so the first clock update comes sooner.
The bootstrap server lets a system with a large clock offset still adjust its clock securely. It covers the TLS problem described above. For a clock too far off even for that, the nocerttimecheck option exists.
NTS needs two ports. NTS/KE uses its own port, 4460/tcp, and normal NTP still uses 123/udp. If either is blocked on the VPS or at the provider's firewall, chrony cannot adjust the system clock with the default configuration. Open both ports. If you cannot, edit /etc/chrony/sources.d/ubuntu-ntp-pools.sources to use the listed NTP servers instead of the NTS ones.
To check the session, run sudo chronyc -N authdata. Its columns are Name/IP address, Mode, KeyID, Type, KLen, Last, Atmp, NAK, Cook and CLen. A working NTS source shows NTS in Mode and non-zero values for KeyID, Type and KLen. SK in Mode means a symmetric key, and - means authentication is off. A - on a 25.10 server means the source is running plain NTP.
Fix a Large Offset
chronyd slews by default because a step in the system time might have adverse effects on certain applications. Slewing a large offset takes a very long time, so chronyc gives you two ways to step the clock instead.
makestep with no arguments cancels any remaining slew correction and jumps the system clock by that amount. The clock is correct immediately.
With two arguments, makestep sets up automatic stepping, like the makestep directive in chrony.conf. The first argument is the stepping threshold in seconds. The second is the number of future clock updates for which that threshold applies. Pair it with burst to take a new measurement quickly and step if needed. You do not have to wait for chronyd's normal measurement and update cycle. makestep 0.1 1 steps the clock if the next update is off by more than 0.1 seconds. burst 1/2 then makes chronyd measure every source over a short period, asking for one good measurement from each and trying at most two times.
A step of less than three hours falls under the cron rules above. A larger step does not, and cron uses the new time immediately.
The makestep directive in /etc/chrony/chrony.conf controls stepping at start-up. On most systems you want the clock to step only at boot, before programs that expect time to move forward steadily start. makestep 0.1 3 steps the clock if the adjustment is larger than 0.1 seconds, but only in the first three clock updates. Check whether your chrony.conf has a makestep line before you rely on boot-time stepping.
For a one-shot sync, use chronyd -q. To check the time once without setting it, use chronyd -Q.
Change Time Sources
Time sources live in *.sources files under /etc/chrony/sources.d/. After you add or remove sources, run sudo chronyc reload sources. chronyd then re-reads every *.sources file in the directories named by the sourcedir directive.
Defaults such as sourcedir, ntsdumpdir and rtcsync are set in /etc/chrony/chrony.conf. Put your own settings in separate files under /etc/chrony/conf.d/. After changing any config file, restart chrony with sudo systemctl restart chrony.service. Then check it with systemctl status chrony.service.
chrony also uses time sources handed out by DHCP (option 42). If your provider's DHCP gives out time servers you do not want, comment out this line in /etc/chrony/chrony.conf and restart:
1# sourcedir /run/chrony-dhcp
Gate Services on Sync with waitsync
waitsync lets a start-up script hold off until the clock is right. waitsync 60 0.01 waits up to about 10 minutes (60 tries, 10 seconds apart by default) for chronyd to sync to a source with less than 10 milliseconds of correction left. The first argument is the maximum number of tries. When chronyd runs out of tries, waitsync gives up and returns a non-zero error code.
A boot script can run chronyc waitsync 60 0.01 and stop on a non-zero exit. Services that check certificates or write timestamps then do not start on a wrong clock. waitsync is one of the monitoring commands, so it runs without sudo.
References
- Ubuntu Server: chrony client - chrony as default from 25.10, timedatectl status, tracking and sources usage, NTS pools and the default sources file, ports, authdata, config paths, reload, restart, DHCP option 42
- Ubuntu Server: timedatectl and timesyncd - timesyncd as the default before 25.10; timedatectl stepping back for chrony
- Ubuntu Server: About time synchronisation - timesyncd staying active on upgraded systems; chronyd -q and -Q
- Ubuntu Server: Serve NTP with chrony - install command, chronyd and chronyc, continuous adjustment and logs, allow rule, admin socket, _chrony user, NTS in chrony 4.0, sourcestats
- chronyc manual, chrony 4.6.1 - socket and network commands, port 323, -n/-N/-c, tracking and sources fields, error bound, sourcestats, reload sources, authdata modes, makestep, burst, waitsync
- chrony.conf manual, chrony 4.6.1 - iburst, makestep directive
- cron(8), Ubuntu noble - cron handling of time changes under and over three hours, and which jobs the special handling covers
- RFC 5280 - certificate validity period and the current-time check in path validation
Posts in this series
- Linux VPS Performance Tuning: sysctl & swap 2027
- Systemd Service Hardening: Sandbox a Unit (2026)
- Fail2ban on Ubuntu VPS: Stop SSH Brute Force 2026
- UFW Firewall Rules for a Public VPS: 2026 Setup
- How to Harden SSH on an Ubuntu VPS (2026 Guide)
- Automated Backups with Rsync and Cron on Linux 2026
- TLS Certificates with Certbot on an Ubuntu VPS 2026
- Harden Nginx on Ubuntu: Headers, TLS & Limits 2026
- Automatic Security Updates on Ubuntu: 2026 Setup
- Logrotate for Nginx and App Logs on a Linux VPS
- Systemd Timers: Replace Cron Jobs on a Linux VPS
- Manage Linux Users and sudo Permissions on a VPS
- Linux VPS Disk and Inode Cleanup: du, find, lsof
- journald Logs: Retention, Size Limits and Queries
- How to Install Docker on a Linux VPS
- How to Monitor a Linux VPS with Netdata
- Valkey vs Redis: Choosing a Cache for Your Linux VPS
- SQL Workbench/J: Free Multi-Database Client for a Linux VPS
- How to Install MariaDB on an Ubuntu Linux VPS
- How to Set Up WireGuard on a Linux VPS
- How to Add a Swap File on a Linux VPS
- How to Install Portainer and Manage Docker on a Linux VPS
- journald Disk Usage: Why a Short Log Line Costs Tens of KB
- Harden a systemd Service: ProtectSystem and NoNewPrivileges
- Encrypted Linux VPS Backups with restic and a systemd Timer
- Install PostgreSQL on an Ubuntu VPS With Safe Remote Access
- Keep Server Time Accurate with chrony on an Ubuntu VPS