Set up the time zone and time synchronization on a Linux server
A wrong server clock never shows up as a clock problem: it shows up as a rejected certificate, as a package source apt refuses, or as a cron job at the wrong hour. How to set the time zone and the time service correctly, and how to prove that synchronization is running.
Nobody pays any attention to a server clock as long as it is right. When it goes wrong, the clock never speaks up. What speaks up instead is a rejected certificate, a package source that apt refuses, a cron job at the wrong hour and a log file that no longer lines up with any other.
This article goes deeper into step 5 of the checklist for a new root server. If you have worked through the two commands there, you have done what is strictly required. This article is about everything beyond it: choosing the time service, the hardware clock, UTC versus local time, proving that synchronization works and the error messages that go with it.
Everything here applies to Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS and Ubuntu 22.04 LTS. The commands are written for use as root. If you work as a regular user, put sudo in front of every command.
What a wrong clock actually breaks
The common thread: none of these messages mentions the clock.
- TLS certificates. Every certificate has a validity window. If the system time falls before it, curl reports
curl: (60) SSL certificate problem: certificate is not yet valid, if it falls after it,certificate has expired. Every outgoing call is affected. - apt. Release files carry an issue date and an expiry date. If the clock runs behind, you get
Release file for ... is not valid yet, if it runs well ahead,is expired. The server then stops receiving security updates without a single service going down. - The logs. Two machines that are five minutes apart can no longer be read against each other, and that is exactly when you need it: during a break-in or an outage.
- Cron jobs and systemd timers. cron works in the system time zone. If you change the zone later, you shift every job. If the clock jumps, jobs run twice or not at all. Covered in detail in setting up a cron job.
- Backups. Almost every backup names its folders after the date and deletes by age. A clock that jumps back one day overwrites yesterday's backup.
- Time-based one-time passwords. TOTP works in 30-second windows, usually with a tolerance of one window on each side. If the server clock drifts further than that, every correct code is rejected. This is the one item on this list that actually locks you out.
Before you change anything: a way back and an inventory
The way back
Changing the time zone does not cut an open SSH session, and switching synchronization on is harmless as well. Two things in this article are not: a clock that jumps a long way, and switching the time service. A large jump backwards makes valid certificates temporarily invalid and throws TOTP logins out of their window. Switching the time service removes the old one before the new one is running, so if the installation breaks off in between, the server has no time synchronization at all, and that only becomes visible days later.
KVM root servers and dedicated servers from KernelHost have no IPMI and no iDRAC. The access path that works without any network service inside the guest is the VNC console in the customer panel. It hangs off the virtualization layer, or off the port itself, and a wrong clock inside the guest does not affect it. Log in there once beforehand and check that you know the root password.
The current state in four commands
Write down what applies right now. Without that note you will not be able to tell later whether a deviation is new:
timedatectl
readlink -f /etc/localtime
date -u
systemctl is-active systemd-timesyncd chrony
Of the seven lines that timedatectl prints, three matter. Time zone names the zone together with its abbreviation and offset, for example Europe/Vienna (CEST, +0200). System clock synchronized tells you whether the clock has ever been synchronized. NTP service has three states that are regularly mixed up:
| Line | Meaning | What to do |
NTP service: active | A time service is installed and running. | Nothing, but verify it anyway. |
NTP service: inactive | A time service is installed but not running. | Start the service, look for the cause in the journal. |
NTP service: n/a | No time service is installed at all. | Install timesyncd or chrony. |
In the third case timedatectl show -p CanNTP --value returns no, and every attempt to switch synchronization on ends with Failed to set ntp: NTP not supported.
Time zone and time synchronization are two different things
The kernel keeps exactly one counter: seconds since 1 January 1970, counted in UTC. That counter is the system time, and it knows nothing about time zones, because a zone is purely a matter of presentation. Two things follow from this. Changing the time zone does not move a single instant, date -u returns the same output before and after. And synchronizing the clock changes the counter, not the presentation, so a wrongly set zone stays wrong. Two tasks, two checks.
Step 1: set the time zone
Zone names come from the IANA database and almost always read Continent/City, in English spelling. Look the name up instead of guessing it:
timedatectl list-timezones | grep -i vienna
timedatectl set-timezone Europe/Vienna
Verifying the result:
timedatectl show -p Timezone --value
readlink -f /etc/localtime
date +%Z
What you expect is Europe/Vienna, the path /usr/share/zoneinfo/Europe/Vienna and an abbreviation that matches the season, CEST in summer and CET in winter.
Three pitfalls
The file /etc/timezone is not authoritative. Debian 13 no longer ships it, so a cat /etc/timezone ends with No such file or directory there, and tzdata does not bring it back either. On the other three systems it still exists, but on all of them the only thing that decides the zone is the symlink /etc/localtime.
Without tzdata there are no zones. Slim images, Ubuntu ones in particular, ship without the zone database. Then even a correctly spelled name fails with Failed to set time zone: Invalid or not installed time zone 'Europe/Vienna', and timedatectl list-timezones prints a single line. After the fix there have to be several hundred:
DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata
timedatectl list-timezones | wc -l
Running services remember the old zone. cron, databases and application servers read the time zone at startup and keep logging in the old zone after a change until they are restarted.
In environments without a running systemd there is no timedatectl. The classic route leads to the same result:
ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
dpkg-reconfigure -f noninteractive tzdata
readlink -f /etc/localtime
UTC or local time: a deliberate decision
Many operators run their servers on UTC, and there are good reasons for that. UTC has no daylight saving time, so there is no hour that occurs twice and none that is missing: on the night of the changeover, 02:30 local time exists twice in autumn and not at all in spring, which affects every job in that window. Against it stands the fact that people read the logs, and that a maintenance window of "Sundays 03:00" means local time. The rule that works: if you run several servers or several locations, take UTC, if you run a single server by the office clock, take local time. The only wrong answer is not knowing which one you have.
timedatectl set-timezone Etc/UTC
For a single look you do not have to touch the system time zone. The TZ variable is enough and applies only to that one call:
TZ=Europe/Vienna date
TZ=Europe/Vienna journalctl -u nginx --since "today"
Step 2: choose the time service
Debian and Ubuntu offer three candidates that solve the same task at very different scope.
| Service | What it can do | When it fits |
systemd-timesyncd | Pure SNTP client, queries one server at a time and moves on when that one fails | The normal case for a single server |
chrony | Full NTP client and server, combines several sources, ships a diagnostic tool with chronyc, supports NTS | When you want to prove accuracy or track down errors |
ntpsec | Successor of the reference implementation ntpd | Existing systems and special cases |
All three declare the same role to the package system: Provides: time-daemon and at the same time Conflicts: time-daemon. If you ask for two of them in one command, you get a refusal:
The following packages have unmet dependencies:
chrony : Conflicts: time-daemon
systemd-timesyncd : Conflicts: time-daemon
E: Unable to correct problems, you have held broken packages.
If you install chrony while timesyncd is running, do not skip over the line The following packages will be REMOVED: systemd-timesyncd. That is intended, because two processes on the same clock are worse than none. Check right afterwards that the new service is running.
Two package names from older guides are history: ntp and ntpdate. On Debian 12 and both Ubuntu versions they are nothing but transitional packages pointing at ntpsec, and on Debian 13 apt-cache policy ntp simply reports Candidate: (none).
Option A: systemd-timesyncd
On all four systems the systemd package recommends systemd-timesyncd | time-daemon. A normal installation therefore brings the service along, a minimal image installed without recommends does not.
DEBIAN_FRONTEND=noninteractive apt-get install -y systemd-timesyncd
systemctl enable --now systemd-timesyncd
The shipped /etc/systemd/timesyncd.conf contains nothing but commented-out defaults. Your own values belong in a drop-in file, and its directory does not exist out of the box:
mkdir -p /etc/systemd/timesyncd.conf.d
cat > /etc/systemd/timesyncd.conf.d/10-kernelhost.conf <<'EOF'
[Time]
NTP=0.at.pool.ntp.org 1.at.pool.ntp.org 2.at.pool.ntp.org
FallbackNTP=0.pool.ntp.org 1.pool.ntp.org
EOF
systemctl restart systemd-timesyncd
NTP= holds the servers that get queried. FallbackNTP= only comes into play when none of them answers, and out of the box that is the Debian pool on Debian and ntp.ubuntu.com on Ubuntu.
Verifying the result:
systemd-analyze cat-config systemd/timesyncd.conf
systemctl is-active systemd-timesyncd
timedatectl timesync-status
The first command shows the assembled configuration, and your drop-in file has to appear in it with its full path. The third one names the server in use, the polling interval and the offset measured last. If it answers with Failed to query server: Connection timed out instead, timesyncd is not running. That message takes 25 seconds.
Option B: chrony
apt-get install -y chrony
systemctl enable --now chrony
On all four distributions the unit is called chrony.service and additionally carries the alias chronyd.service. The shipped /etc/chrony/chrony.conf already comes with usable defaults: Debian queries pool 2.debian.pool.ntp.org iburst, Ubuntu queries ntp.ubuntu.com plus three entries from the Ubuntu pool. Do not edit that file. Through confdir /etc/chrony/conf.d it pulls in a directory that package updates leave untouched:
cat > /etc/chrony/conf.d/10-kernelhost.conf <<'EOF'
pool 0.at.pool.ntp.org iburst
EOF
systemctl restart chrony
Verifying the result with chronyc tracking, this is what it looks like on a cleanly running server:
Reference ID : A29FC801 (time.cloudflare.com)
Stratum : 4
Ref time (UTC) : Thu Sep 03 17:53:29 2026
System time : 0.000135970 seconds fast of NTP time
Last offset : +0.000042723 seconds
RMS offset : 0.000086483 seconds
Frequency : 13.967 ppm fast
Residual freq : +0.002 ppm
Skew : 0.073 ppm
Root delay : 0.008282715 seconds
Root dispersion : 0.000613841 seconds
Update interval : 1038.4 seconds
Leap status : Normal
Three lines are enough in daily use. System time is the current deviation, here roughly 136 microseconds. Leap status has to read Normal, and if it says Not synchronised, chrony does not have a usable source yet. Stratum tells you how far you are from a reference clock, 2 to 4 is normal. The second look goes to the sources themselves, with chronyc -n sources:
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^- 217.175.198.239 3 10 377 748 +114us[ +153us] +/- 19ms
^- 46.102.157.67 2 10 377 558 +151us[ +192us] +/- 28ms
^* 162.159.200.1 3 10 377 335 -251us[ -208us] +/- 4463us
^+ 152.53.132.244 2 9 377 231 +335us[ +335us] +/- 5626us
The two characters on the far left are the actual finding. ^* marks the source the clock is set by, ^+ one that is taken into account, ^- one that is not combined, ^? an unreachable one and ^x a contradictory one. The Reach column holds an octal value covering the last eight queries: 377 means all eight were answered, 0 means none arrived.
From version 4 onwards chrony supports Network Time Security, that is authenticated time over TLS. This requires two things that are easy to overlook: the ca-certificates package and an outgoing TCP port 4460. Without the root certificates the sync fails with Error in the certificate verification. The certificate is NOT trusted. The certificate issuer is unknown.
apt-get install -y ca-certificates
cat > /etc/chrony/conf.d/20-nts.conf <<'EOF'
server time.cloudflare.com iburst nts
EOF
systemctl restart chrony
chronyc authdata
The hardware clock
Next to the system time there is a second clock: the hardware clock, RTC for short. It supplies the first time value at boot, long before any time service is running. On a KVM root server that is the clock provided by the virtualization layer, and timedatectl shows it in the RTC time line. Exactly one setting matters, the last line of the output:
timedatectl set-local-rtc 0
timedatectl | grep "RTC in local TZ"
If it says yes, the hardware clock is read as local time. That is a concession to machines that also boot Windows, and it is pointless on a server. The damage shows up twice a year: at the daylight saving changeover, a hardware clock kept in local time is ambiguous for an hour, and a reboot inside that window can bring the server up with a time that is off by one hour.
In the other direction: through the rtcsync default, chrony regularly writes the corrected time back into the hardware clock, and that line is present in the shipped chrony.conf of all four distributions. systemd-timesyncd has no such option. By hand it works with hwclock --systohc. On Debian 12, Debian 13 and Ubuntu 24.04 the command comes from the util-linux-extra package; if the shell reports hwclock: command not found, install it. On Ubuntu 22.04 it belongs to util-linux.
Step 3: prove that synchronization is running
The fact that a command ran without an error is not proof. These five checks together are proof:
- The overall state.
timedatectlshowsSystem clock synchronized: yesandNTP service: active. Both lines together, not one of them. - The service itself.
systemctl is-enabled chronyandsystemctl is-active chronyanswer withenabledandactive, and the same applies to timesyncd.enabledon its own only means that it would run at the next boot. - A source is really being reached. With chrony,
chronyc -n sourceshas to hold at least one line marked^*and theReachcolumn has to have moved away from0. With timesyncd,timedatectl timesync-statusnames a specific server. - The offset is small.
chronyc trackingshould show microseconds or milliseconds atSystem time. Whole seconds mean that the correction is still under way or that something is stuck. - It survives a reboot. Reboot the server once, then repeat checks 1 to 4. This is the only check that answers the question conclusively.
The line you must not trust on its own: System clock synchronized: yes reflects a flag in the kernel that was set by whichever process last adjusted the clock. It does not disappear when the time service crashes. A server can therefore show this line and still have been running for hours without any synchronization.
Services that strictly need a correct clock at startup are ordered with After=time-sync.target. For that target to be reached only after the clock has been synchronized, you also need a waiting unit: systemd-time-wait-sync.service with timesyncd, chrony-wait.service with chrony. Debian 12, Debian 13 and Ubuntu 24.04 ship the latter, Ubuntu 22.04 does not. How a unit of your own is put together is covered in creating a systemd service.
Firewall and network
NTP goes outbound over UDP port 123, and NTS adds TCP 4460. In the UFW default configuration outgoing traffic is allowed, so there is nothing to do. If you have set ufw default deny outgoing, you have to open the port, otherwise the clock stops being corrected without any service writing an error message:
ufw allow out 123/udp comment 'NTP'
The rest of the firewall topic is covered in setting up the UFW firewall. In the opposite direction: a time service that answers requests from the internet is an amplifier for reflection attacks. Out of the box, neither chrony nor systemd-timesyncd answers anyone, and only an allow line in the chrony configuration turns the client into a server. Set it scoped to your own network only. The upstream filtering in the maincubes datacenter in Frankfurt am Main catches traffic like that, but the best amplifier is still the one that does not exist.
When the clock is far off
Under normal conditions time services do not correct by jumping. They correct by speeding the clock up or slowing it down by a tiny amount. That is why a deviation of one minute does not disappear within a second, and that is exactly right: a jump backwards makes timestamps occur twice.
For freshly booted systems the shipped chrony.conf of all four distributions holds the line makestep 1 3: it permits a real jump when the offset is above one second, and only for the first three synchronizations. That is exactly the case of a virtual machine cloned from an image or restored from a snapshot. If that is not enough, force the jump and wait for the result:
chronyc makestep
chronyc waitsync 10
Measuring without changing anything works with the -Q switch: it queries the configured sources, reports the deviation and exits without touching the clock. The interesting line then reads something like System clock wrong by 0.000363 seconds (ignored):
chronyd -Q -f /etc/chrony/chrony.conf
What you should not do is set the clock by hand with date -s while a time service is running. timedatectl set-time refuses that explicitly with Failed to set time: Automatic time synchronization is enabled, because otherwise two instances would be turning the same clock at once.
Common errors and how to fix them
Failed to set time zone: Invalid or not installed time zone 'Europe/Wien': the zone name does not exist. The IANA database uses English city names, so it is Europe/Vienna, Europe/Zurich and Europe/Prague. If the same message comes up for a correctly spelled name, the zone database is missing: the counter-check is timedatectl list-timezones | wc -l, and if a single line is all that is left, install tzdata.
Failed to set ntp: NTP not supported: no time service is installed at all, and timedatectl show -p CanNTP --value then returns no. The set-ntp switch only controls services that have registered themselves under /usr/lib/systemd/ntp-units.d/: timesyncd with 80-systemd-timesync.list, chrony with 50-chrony.list.
timedatectl set-ntp true runs without an error, yet NTP service stays inactive: the command only reports that it has instructed the unit, not that the unit is running. The cause is in the journal, for example with journalctl -u systemd-timesyncd -n 20.
506 Cannot talk to daemon: chronyc cannot reach the service because chronyd is not running. systemctl status chrony gives you the reason. The message is the same for every chronyc subcommand.
chrony.service - chrony, an NTP client/server was skipped because of an unmet condition check (ConditionCapability=CAP_SYS_TIME).: the system is not allowed to set the clock at all. On container-based VPS (LXC, OpenVZ) this is the normal case, because the host system dictates the time there. On a KVM root server the guest is allowed to set its own clock, so there the message is a real finding. From the timesyncd side the same case looks like this: systemd-timesyncd.service - Network Time Synchronization was skipped because of an unmet condition check (ConditionVirtualization=!container).
Release file for ... is not valid yet during apt update: the clock is running behind. Fix the time first, then run apt update again. The other way round, is expired points to a clock that runs ahead, and less often to an outdated mirror.
Failed to query server: Connection timed out during timedatectl timesync-status: timesyncd is not running, or chrony has replaced it. If chrony is the active service, this command stays without an answer permanently, and that is not an error. The right tool is then chronyc tracking.
Distribution differences at a glance
| Item | Debian 13 | Debian 12 | Ubuntu 24.04 | Ubuntu 22.04 |
| chrony version | 4.6.1 | 4.3 | 4.5 | 4.2 |
Source in chrony.conf | Debian pool | Debian pool | ntp.ubuntu.com plus Ubuntu pool | ntp.ubuntu.com plus Ubuntu pool |
/etc/timezone | no longer present | present | present | present |
hwclock from package | util-linux-extra | util-linux-extra | util-linux-extra | util-linux |
chrony-wait.service | yes | yes | yes | no |
ntp and ntpdate | no longer available | transitional packages | transitional packages | transitional packages |
What is the same everywhere: the time zone depends solely on the symlink /etc/localtime, and one time service rules out every other.
The short version
For a freshly provisioned root server that is meant to run on local time and gets by with the default service:
DEBIAN_FRONTEND=noninteractive apt-get install -y systemd-timesyncd tzdata
timedatectl set-timezone Europe/Vienna
timedatectl set-local-rtc 0
systemctl enable --now systemd-timesyncd
timedatectl
What you expect at the end is Time zone: Europe/Vienna, System clock synchronized: yes, NTP service: active and RTC in local TZ: no. If those four lines still read that way after a reboot, the clock of this server is taken care of.
Frequently asked questions
What breaks when the clock of a server is wrong?
Does the time of the server change when I switch the time zone?
Why does timedatectl report "Failed to set time zone: Invalid or not installed time zone"?
systemd-timesyncd or chrony: which time service should I use?
timedatectl set-ntp fails with "Failed to set ntp: NTP not supported". What is missing?
chrony does not start on my VPS and reports an unmet condition. Is that a real problem?
How do I prove that time synchronization is really running?
Should the server run on UTC or on local time?
2026 KernelHost GmbH. All rights reserved. This guide is protected by copyright. Republishing it on other websites, in whole, in part or in edited form, is not permitted without our written consent. Quoting with a source credit and a link is expressly welcome.

