Accurate system time is the quiet backbone of enterprise IT. Authentication protocols, certificates, distributed logging, and scheduled maintenance all depend on synchronized clocks. This post walks through a production-ready shell script to set the time zone in Linux and NTP timedatectl settings using systemd-timesyncd and timedatectl. If you manage fleets of Linux endpoints—or build policy as an MSP—this is the kind of repeatable automation that keeps incidents out of your pager.
Background
Linux offers multiple ways to manage time: classic ntpd, chrony, and systemd-timesyncd. Many modern distros ship with systemd-timesyncd enabled by default, and timedatectl provides a consistent interface. The script you provided standardizes three tasks IT teams do regularly:
- List and set the system time zone (e.g., America/Los_Angeles)
- Configure one or two NTP servers for synchronization
- Tune NTP polling intervals to match network and compliance requirements
It includes robust validation, safety checks, and idempotent behavior so you can run it repeatedly without creating drift.
The Script
#!/usr/bin/env bash
#
# Description: Set the time zone and NTP timedatectl settings for the local machine.
# By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
# Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
# Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
# Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider.
# Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
# Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
# Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
# EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#
# Preset Parameter: --listTimeZones|-l
# Lists all the time zones available to set.
#
# Preset Parameter: --setTimeZone|-t <arg>
# Specifies the name of the time zone you would like to set, such as 'America/Los_Angeles'.
#
# Preset Parameter: --setSyncServers|-s <arg>
# Specifies up to two NTP servers (comma-separated) to synchronize the time with.
#
# Preset Parameter: --syncIntervalMinimumInSeconds|--min <arg>
# Specifies the minimum interval in seconds for NTP synchronization. The default value for most distributions is 32 seconds.
#
# Preset Parameter: --syncIntervalMaximumInSeconds|--max <arg>
# Specifies the maximum interval in seconds for NTP synchronization. The default value for most distributions is 2048 seconds.
#
# Preset Parameter: --help|-h
# Displays this help menu.
#
# Example: --setTimeZone "America/Los_Angeles" --setSyncServers "0.pool.ntp.org" --syncIntervalMinimumInSeconds "32" --syncIntervalMaximumInSeconds "2048"
#
# The current time zone is already set to 'America/Los_Angeles'.
#
# Analyzing the default timedatectl configuration file at '/etc/systemd/timesyncd.conf'.
# The current primary sync server is not currently set.
# The current fallback sync server is not currently set.
# The current minimum sync interval is not currently set.
# The current maximum sync interval is not currently set.
# An update to the current timedatectl configuration file is necessary.
#
# Creating a new timedatectl configuration file at '/etc/systemd/timesyncd.conf.d/1752702365-ntp.conf'.
# Setting primary sync server to '0.pool.ntp.org'.
# Setting minimum sync interval to '32' seconds.
# Setting maximum sync interval to '2048' seconds.
# Successfully created a new timedatectl configuration file.
# Restarting systemd-timesyncd.
# Successfully updated the NTP configuration.
#
# ### Previous timedatectl Settings ###
# Timezone: America/Los_Angeles
# # /etc/systemd/timesyncd.conf
# [Time]
#
# ### Current timedatectl Settings ###
# Timezone: America/Los_Angeles
# # /etc/systemd/timesyncd.conf
# [Time]
#
# # /etc/systemd/timesyncd.conf.d/1752702365-ntp.conf
# [Time]
# NTP=0.pool.ntp.org
# PollIntervalMinSec=32
# PollIntervalMaxSec=2048
#
# ### Current Time ###
# 07/16/2025 02:46:05 PM
#
# Version: 1.0
# Release Notes: Initial Release
_arg_setTimeZone=
_arg_setSyncServers=
_arg_syncServers=
_arg_syncIntervalMinimumInSeconds=
_arg_syncIntervalMaximumInSeconds=
_arg_listTimeZones="off"
exitCode=0
print_help() {
printf '\n\n%s\n\n' 'Usage: [--listTimeZones|-l] [--setTimeZone|-t <arg>] [--setSyncServers|-s <arg>] [--syncIntervalMinimumInSeconds|--min <arg>] [--syncIntervalMaximumInSeconds|--max <arg>] [--help|-h]'
printf '%s\n' 'Preset Parameter: --listTimeZones|-l'
printf '\t%s\n' "Lists all the time zones available to set."
printf '%s\n' 'Preset Parameter: --setTimeZone|-t <arg>'
printf '\t%s\n' "Specifies the name of the time zone you would like to set, such as 'America/Los_Angeles'."
printf '%s\n' 'Preset Parameter: --setSyncServers|-s <arg>'
printf '\t%s\n' "Specifies up to two NTP servers (comma-separated) to synchronize the time with."
printf '%s\n' 'Preset Parameter: --syncIntervalMinimumInSeconds|--min <arg>'
printf '\t%s\n' "Specifies the minimum interval in seconds for NTP synchronization. The default value for most distributions is 32 seconds."
printf '%s\n' 'Preset Parameter: --syncIntervalMaximumInSeconds|--max <arg>'
printf '\t%s\n' "Specifies the maximum interval in seconds for NTP synchronization. The default value for most distributions is 2048 seconds."
printf '%s\n' 'Preset Parameter: --help|-h'
printf '\t%s\n' "Displays this help menu."
}
die() {
local _ret="${2:-1}"
echo "$1" >&2
test "${_PRINT_HELP:-no}" = yes && print_help >&2
exit "${_ret}"
}
parse_commandline() {
while test $# -gt 0; do
_key="$1"
case "$_key" in
--setTimeZone | --settimezone | -t)
test $# -lt 2 && die "[Error] Missing value for the optional argument '$_key'." 1
_arg_setTimeZone=$2
shift
;;
--setTimeZone=*)
_arg_setTimeZone="${_key##--setTimeZone=}"
;;
--setSyncServers | --setsyncservers | --setSyncServer | --setsyncserver | -s)
test $# -lt 2 && die "[Error] Missing value for the optional argument '$_key'." 1
_arg_setSyncServers=$2
shift
;;
--setSyncServers=*)
_arg_setSyncServers="${_key##--setSyncServers=}"
;;
--syncIntervalMinimumInSeconds | --syncintervalminimum | --syncMinimum | --syncminimum | --min)
test $# -lt 2 && die "[Error] Missing value for the optional argument '$_key'." 1
_arg_syncIntervalMinimumInSeconds=$2
shift
;;
--syncIntervalMinimumInSeconds=*)
_arg_syncIntervalMinimumInSeconds="${_key##--syncIntervalMinimumInSeconds=}"
;;
--syncIntervalMaximumInSeconds | --syncintervalmaximum | --syncMaximum | --syncmaximum | --max)
test $# -lt 2 && die "[Error] Missing value for the optional argument '$_key'." 1
_arg_syncIntervalMaximumInSeconds=$2
shift
;;
--syncIntervalMaximumInSeconds=*)
_arg_syncIntervalMaximumInSeconds="${_key##--syncIntervalMaximumInSeconds=}"
;;
--listTimeZones | --listtimezones | -l)
_arg_listTimeZones="on"
;;
--help | -h)
_PRINT_HELP=yes die
;;
*)
_PRINT_HELP=yes die "[Error] Got an unexpected argument '$1'" 1
;;
esac
shift
done
}
echo " "
parse_commandline "$@"
# If script form variables are used, replace the command line paramters with their value
if [[ -n $listTimeZones && $listTimeZones == "true" ]]; then
_arg_listTimeZones="on"
fi
if [[ -n $setTimeZone ]]; then
_arg_setTimeZone="$setTimeZone"
fi
if [[ -n $setSyncServers ]]; then
_arg_setSyncServers="$setSyncServers"
fi
if [[ -n $syncIntervalMinimumInSeconds ]]; then
_arg_syncIntervalMinimumInSeconds="$syncIntervalMinimumInSeconds"
fi
if [[ -n $syncIntervalMaximumInSeconds ]]; then
_arg_syncIntervalMaximumInSeconds="$syncIntervalMaximumInSeconds"
fi
# Ensure the script is run with root permissions
if [[ $(id -u) -ne 0 ]]; then
_PRINT_HELP=no die "[Error] This script must be run with root permissions. Try running it with sudo or as the system/root user." 1
fi
# Check if the timedatectl command is available
if ! command -v "timedatectl" 1> /dev/null; then
_PRINT_HELP=no die "[Error] This script is designed to configure the time settings in timedatectl, but it was not found.
[Error] You may want to check your distribution's documentation to see the proper way of configuring the system time." 1
fi
if [[ $_arg_listTimeZones == "off" ]]; then
if ! systemctl is-enabled systemd-timesyncd 1> /dev/null; then
_PRINT_HELP=no die "[Error] The service 'systemd-timesyncd' is currently disabled. In order to adjust the timedatectl settings you must have it enabled." 1
fi
fi
# You must provide at least one option
if [[ $_arg_listTimeZones == "off" && -z $_arg_setTimeZone && -z $_arg_setSyncServers && -z $_arg_syncIntervalMinimumInSeconds && -z $_arg_syncIntervalMaximumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] You must provide at least one valid option." 1
fi
# Validate that 'List Time Zones' is not used with other options
if [[ $_arg_listTimeZones == "on" && (-n $_arg_setTimeZone || -n $_arg_setSyncServers || -n $_arg_syncIntervalMinimumInSeconds || -n $_arg_syncIntervalMaximumInSeconds) ]]; then
_PRINT_HELP=yes die "[Error] 'List Time Zones' cannot be used with other options." 1
fi
# Validate the specified time zone
if [[ -n $_arg_setTimeZone ]]; then
_arg_setTimeZone=$(echo "$_arg_setTimeZone" | xargs)
if [[ -z $_arg_setTimeZone ]]; then
_PRINT_HELP=yes die "[Error] You must specify a valid time zone to set." 1
fi
validTimeZones=$(timedatectl list-timezones --no-pager)
if ! echo "$validTimeZones" | grep -q -x "$_arg_setTimeZone"; then
_PRINT_HELP=yes die "[Error] The time zone '$_arg_setTimeZone' is invalid. You must specify a valid time zone to set.
### Valid Time Zones ###
$validTimeZones" 1
fi
fi
# Validate NTP configuration if sync servers or intervals are specified
if [[ -n $_arg_setSyncServers || -n $_arg_syncIntervalInMinutes ]]; then
if ! timedatectl show 1> /dev/null; then
_PRINT_HELP=no die "[Error] Unable to verify if this version of timedatectl is capable of using NTP." 1
fi
if timedatectl show | grep "CanNTP=" | grep "no" 1> /dev/null 2> /dev/null; then
_PRINT_HELP=no die "[Error] This system does not currently have NTP installed and configured with timedatectl. Unable to configure network time synchronization." 1
fi
fi
# Validate and process sync servers
if [[ -n $_arg_setSyncServers ]]; then
_arg_setSyncServers=$(echo "$_arg_setSyncServers" | xargs)
if [[ -z $_arg_setSyncServers ]]; then
_PRINT_HELP=yes die "[Error] You must specify a valid sync server to set." 1
fi
_arg_syncServers=$(echo "$_arg_setSyncServers" | tr "," "\n" | sed "s/^[[:space:]]//" | sed "s/[[:space:]]$//")
serverCount=$(echo "$_arg_syncServers" | wc -l | xargs)
if [[ $serverCount -gt 2 ]]; then
_PRINT_HELP=yes die "[Error] You can only specify up to two valid sync servers." 1
fi
if invalidServers=$(echo "$_arg_syncServers" | grep -e "[^a-zA-Z0-9.:_-]"); then
_PRINT_HELP=yes die "[Error] One or more of the sync servers you specified are invalid. They contain invalid characters; only alphanumeric characters or one of these symbols are valid '.:_-'.
### Invalid Servers ###
$invalidServers" 1
fi
# Check if sync servers are responsive on UDP port 123
while IFS= read -r syncServer; do
if ! timeout 30 bash -c "echo -n '' > /dev/udp/$syncServer/123"; then
_PRINT_HELP=yes die "[Error] The sync server '$syncServer' was not responsive on UDP port 123 (NTP). Unable to set sync server." 1
fi
done <<< "$_arg_syncServers"
# Assign primary and fallback sync servers
primarySyncServer=$(echo "$_arg_syncServers" | head -n 1)
if [[ $serverCount -gt 1 ]]; then
fallbackSyncServer=$(echo "$_arg_syncServers" | tail -n 1)
fi
fi
# Validate and process minimum sync interval
if [[ -n $_arg_syncIntervalMinimumInSeconds ]]; then
_arg_syncIntervalMinimumInSeconds=$(echo "$_arg_syncIntervalMinimumInSeconds" | xargs)
if [[ -z $_arg_syncIntervalMinimumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] You must specify a valid minimum sync interval to set." 1
fi
if [[ $_arg_syncIntervalMinimumInSeconds =~ [^0-9] ]]; then
_PRINT_HELP=yes die "[Error] The sync interval minimum '$_arg_syncIntervalMinimumInSeconds' is invalid. It contains invalid characters; only numeric characters are allowed." 1
fi
if [[ $_arg_syncIntervalMinimumInSeconds -lt 1 || $_arg_syncIntervalMinimumInSeconds -gt 86400 ]]; then
_PRINT_HELP=yes die "[Error] The sync interval minimum '$_arg_syncIntervalMinimumInSeconds' is invalid. The sync interval must be greater than or equal to 1 and less than or equal to 86400." 1
fi
fi
# Validate and process maximum sync interval
if [[ -n $_arg_syncIntervalMaximumInSeconds ]]; then
_arg_syncIntervalMaximumInSeconds=$(echo "$_arg_syncIntervalMaximumInSeconds" | xargs)
if [[ -z $_arg_syncIntervalMaximumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] You must specify a valid maximum sync interval to set." 1
fi
if [[ $_arg_syncIntervalMaximumInSeconds =~ [^0-9] ]]; then
_PRINT_HELP=yes die "[Error] The sync interval maximum '$_arg_syncIntervalMaximumInSeconds' is invalid. It contains invalid characters; only numeric characters are allowed." 1
fi
if [[ $_arg_syncIntervalMaximumInSeconds -lt 1 || $_arg_syncIntervalMaximumInSeconds -gt 86400 ]]; then
_PRINT_HELP=yes die "[Error] The sync interval maximum '$_arg_syncIntervalMaximumInSeconds' is invalid. The sync interval must be greater than or equal to 1 and less than or equal to 86400." 1
fi
fi
# Ensure minimum sync interval is less than maximum sync interval
if [[ -n $_arg_syncIntervalMinimumInSeconds && -n $_arg_syncIntervalMaximumInSeconds && $_arg_syncIntervalMinimumInSeconds -gt $_arg_syncIntervalMaximumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] The maximum sync interval '$_arg_syncIntervalMaximumInSeconds' is less than the minimum sync interval '$_arg_syncIntervalMinimumInSeconds'. The minimum sync interval must be less than the max sync interval." 1
fi
if ! currentConfig=$(systemd-analyze cat-config systemd/timesyncd.conf --no-pager); then
echo "[Error] Failed to retrieve the current time settings." >&2
exitCode=1
fi
previousTimeSettings=$(echo "$currentConfig" | awk '/^# \/.*\.conf/ { print; next } /^\s*#/ { next } { print }')
# List time zones if requested
if [[ $_arg_listTimeZones == "on" ]]; then
if ! currentTimeZone=$(timedatectl show | grep "Timezone=" | sed "s/Timezone=//" | sed "s/^[[:space:]]*//g" | sed "s/[[:space:]]*$//g"); then
_PRINT_HELP=no die "[Error] Failed to retrieve the current timezone." 1
fi
# Display the current time zone.
echo ""
echo "The current timezone is '$currentTimeZone'."
echo ""
echo "### Valid Time Zones ###"
if ! timedatectl list-timezones --no-pager; then
_PRINT_HELP=no die "[Error] Failed to retrieve a list of valid time zones." 1
fi
exit "$exitCode"
fi
# Set the time zone if specified
if [[ -n $_arg_setTimeZone ]]; then
if ! previousTimeZone=$(timedatectl show | grep "Timezone=" | sed "s/Timezone=//" | sed "s/^[[:space:]]*//g" | sed "s/[[:space:]]*$//g"); then
_PRINT_HELP=no die "[Error] Failed to retrieve the current timezone." 1
fi
alreadySetTimeZone="false"
if [[ $previousTimeZone == "$_arg_setTimeZone" ]]; then
echo "The current time zone is already set to '$_arg_setTimeZone'."
alreadySetTimeZone="true"
else
echo "Changing the timezone from '$previousTimeZone' to '$_arg_setTimeZone'."
if ! timedatectl set-timezone "$_arg_setTimeZone"; then
_PRINT_HELP=no die "[Error] Failed to set the time zone to '$_arg_setTimeZone'." 1
fi
fi
currentTimeZone=$(timedatectl show | grep "Timezone=" | sed "s/Timezone=//" | sed "s/^[[:space:]]*//g" | sed "s/[[:space:]]*$//g")
if [[ $alreadySetTimeZone == "false" && $currentTimeZone == "$_arg_setTimeZone" ]]; then
echo "Successfully set the time zone to '$_arg_setTimeZone'."
elif [[ $alreadySetTimeZone == "false" ]]; then
_PRINT_HELP=no die "[Error] Failed to set the time zone to '$_arg_setTimeZone'." 1
fi
echo ""
fi
# Create the configuration directory if it doesn't exist
if [[ ! -d "/etc/systemd/timesyncd.conf.d" && (-n $_arg_syncServers || -n $_arg_syncIntervalMinimumInSeconds || -n $_arg_syncIntervalMaximumInSeconds) ]]; then
if ! mkdir "/etc/systemd/timesyncd.conf.d"; then
_PRINT_HELP=no die "[Error] Failed to create the directory '/etc/systemd/timesyncd.conf.d'." 1
fi
fi
# Check if there are existing configuration files in the timesyncd configuration directory
timeSyncConfigFiles=$(ls /etc/systemd/timesyncd.conf.d/*.conf 2> /dev/null)
# If no configuration files exist and the default configuration file is present, analyze it
if [[ -z $timeSyncConfigFiles && -f "/etc/systemd/timesyncd.conf" && (-n $_arg_syncServers || -n $_arg_syncIntervalMinimumInSeconds || -n $_arg_syncIntervalMaximumInSeconds) ]]; then
echo "Analyzing the default timedatectl configuration file at '/etc/systemd/timesyncd.conf'."
# Check and update primary and fallback sync servers if necessary
if [[ -n $_arg_syncServers ]]; then
currentPrimarySyncServer=$(grep -w -r -H "^NTP" /etc/systemd/timesyncd.conf)
currentPrimarySyncServer=$(echo "$currentPrimarySyncServer" | grep -v '^$')
currentPrimaryServerCount=$(echo "$currentPrimarySyncServer" | awk 'NF' | wc -l | xargs)
currentFallbackSyncServer=$(grep -w -r -H "^FallbackNTP" /etc/systemd/timesyncd.conf)
currentFallbackSyncServer=$(echo "$currentFallbackSyncServer" | grep -v '^$')
currentFallbackServerCount=$(echo "$currentFallbackSyncServer" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate primary or fallback sync servers
if [[ $currentPrimaryServerCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple primary sync servers were found. Please correct this before attempting to adjust the sync servers.
$currentPrimarySyncServer" 1
fi
if [[ $currentFallbackServerCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple fallback sync servers were found. Please correct this before attempting to adjust the sync servers.
$currentPrimarySyncServer" 1
fi
# Check if the primary sync server needs to be updated
if [[ -n $currentPrimarySyncServer ]]; then
if ! echo "$currentPrimarySyncServer" | grep -w "$primarySyncServer" 1> /dev/null 2> /dev/null; then
currentPrimarySyncValue=$(echo "$currentPrimarySyncServer" | awk -F "=" '{print $2}')
echo "The current primary sync server needs to be updated from '$currentPrimarySyncValue' to '$primarySyncServer'."
updateTimeSyncConfig="true"
else
echo "The current primary sync server is already $primarySyncServer."
fi
else
echo "The current primary sync server is not currently set."
updateTimeSyncConfig="true"
fi
# Check if the fallback sync server needs to be updated
if [[ -n $currentFallbackSyncServer ]]; then
if ! echo "$currentFallbackSyncServer" | grep -w "$fallbackSyncServer" 1> /dev/null 2> /dev/null; then
currentFallbackSyncValue=$(echo "$currentFallbackSyncServer" | awk -F "=" '{print $2}')
echo "The current fallback sync server needs to be updated from '$currentFallbackSyncValue' to '$fallbackSyncServer'."
updateTimeSyncConfig="true"
else
echo "The current fallback sync server is already $fallbackSyncServer."
fi
else
echo "The current fallback sync server is not currently set."
updateTimeSyncConfig="true"
fi
fi
# Check and update the minimum sync interval if necessary
if [[ -n $_arg_syncIntervalMinimumInSeconds ]]; then
currentMinimumSyncInterval=$(grep -w -r -H "^PollIntervalMinSec" /etc/systemd/timesyncd.conf)
currentMinimumSyncInterval=$(echo "$currentMinimumSyncInterval" | grep -v '^$')
currentMinimumSyncIntervalCount=$(echo "$currentMinimumSyncInterval" | awk 'NF' | wc -l | xargs)
currentMaximumSyncInterval=$(grep -w -r -H "^PollIntervalMaxSec" /etc/systemd/timesyncd.conf)
currentMaximumSyncInterval=$(echo "$currentMaximumSyncInterval" | grep -v '^$')
currentMaximumSyncIntervalCount=$(echo "$currentMaximumSyncInterval" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate minimum or maximum sync intervals
if [[ $currentMinimumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple minimum sync intervals were found. Please correct this before attempting to adjust the minimum sync interval.
$currentMinimumSyncInterval" 1
fi
if [[ $currentMaximumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple maximum sync intervals were found. Please correct this before attempting to adjust the minimum sync interval.
$currentMaximumSyncInterval" 1
fi
# Validate the relationship between minimum and maximum sync intervals
if [[ -n $currentMaximumSyncInterval && -z $_arg_syncIntervalMaximumInSeconds ]]; then
currentMaxValue=$(echo "$currentMaximumSyncInterval" | awk -F "=" '{ print $2 }')
if [[ currentMaxValue -lt $_arg_syncIntervalMinimumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] The maximum sync interval '$_arg_syncIntervalMaximumInSeconds' is less than the minimum sync interval '$_arg_syncIntervalMinimumInSeconds'. The minimum sync interval must be less than the max sync interval." 1
fi
fi
# Check if the minimum sync interval needs to be updated
if [[ -n $currentMinimumSyncInterval ]]; then
if ! echo "$currentMinimumSyncInterval" | grep -w "$_arg_syncIntervalMinimumInSeconds" 1> /dev/null 2> /dev/null; then
currentMinValue=$(echo "$currentMinimumSyncInterval" | awk -F "=" '{ print $2 }')
echo "The current minimum sync interval needs to be updated from '$currentMinValue' to '$_arg_syncIntervalMinimumInSeconds'."
updateTimeSyncConfig="true"
else
echo "The current minimum sync interval is already $_arg_syncIntervalMinimumInSeconds."
fi
else
echo "The current minimum sync interval is not currently set."
updateTimeSyncConfig="true"
fi
fi
# Check and update the maximum sync interval if necessary
if [[ -n $_arg_syncIntervalMaximumInSeconds ]]; then
currentMaximumSyncInterval=$(grep -w -r -H "^PollIntervalMaxSec" /etc/systemd/timesyncd.conf)
currentMaximumSyncInterval=$(echo "$currentMaximumSyncInterval" | grep -v '^$')
currentMaximumSyncIntervalCount=$(echo "$currentMaximumSyncInterval" | awk 'NF' | wc -l | xargs)
currentMinimumSyncInterval=$(grep -w -r -H "^PollIntervalMinSec" /etc/systemd/timesyncd.conf)
currentMinimumSyncInterval=$(echo "$currentMinimumSyncInterval" | grep -v '^$')
currentMinimumSyncIntervalCount=$(echo "$currentMinimumSyncInterval" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate maximum or minimum sync intervals
if [[ $currentMaximumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple maximum sync intervals were found. Please correct this before attempting to adjust the maximum sync interval.
$currentMaximumSyncInterval" 1
fi
if [[ $currentMinimumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple minimum sync intervals were found. Please correct this before attempting to adjust the maximum sync interval.
$currentMinimumSyncInterval" 1
fi
# Validate the relationship between maximum and minimum sync intervals
if [[ -n $currentMinimumSyncInterval && -z $_arg_syncIntervalMinimumInSeconds ]]; then
currentMinValue=$(echo "$currentMinimumSyncInterval" | awk -F "=" '{ print $2 }')
if [[ currentMinValue -gt $_arg_syncIntervalMaximumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] The maximum sync interval '$_arg_syncIntervalMaximumInSeconds' is less than the minimum sync interval '$_arg_syncIntervalMinimumInSeconds'. The minimum sync interval must be less than the max sync interval." 1
fi
fi
# Check if the maximum sync interval needs to be updated
if [[ -n $currentMaximumSyncInterval ]]; then
if ! echo "$currentMaximumSyncInterval" | grep -w "$_arg_syncIntervalMaximumInSeconds" 1> /dev/null 2> /dev/null; then
currentMaxValue=$(echo "$currentMaximumSyncInterval" | awk -F "=" '{ print $2 }')
echo "The current maximum sync interval needs to be updated from '$currentMaxValue' to '$_arg_syncIntervalMaximumInSeconds'."
updateTimeSyncConfig="true"
else
echo "The current maximum sync interval is already $_arg_syncIntervalMaximumInSeconds."
fi
else
echo "The current maximum sync interval is not currently set."
updateTimeSyncConfig="true"
fi
fi
# Determine if an update to the configuration file is necessary
if [[ $updateTimeSyncConfig == "true" ]]; then
echo "An update to the current timedatectl configuration file is necessary."
else
echo "No update is necessary at this time."
echo ""
echo "### Previous timedatectl Settings ###"
echo "Timezone: $previousTimeZone"
echo "$previousTimeSettings"
echo ""
if ! currentTimeZone=$(timedatectl show | grep "Timezone=" | sed "s/Timezone=//" | sed "s/^[[:space:]]*//g" | sed "s/[[:space:]]*$//g"); then
echo "[Error] Failed to retrieve the current timezone." >&2
exitCode=1
fi
echo "### Current timedatectl Settings ###"
echo "Timezone: $currentTimeZone"
if ! currentConfig=$(systemd-analyze cat-config systemd/timesyncd.conf --no-pager); then
echo "[Error] Failed to retrieve the current time settings." >&2
exitCode=1
fi
echo "$currentConfig" | awk '/^# \/.*\.conf/ { print; next } /^\s*#/ { next } { print }'
echo ""
echo "### Current Time ###"
if ! date "+%x %r"; then
echo "[Error] Failed to retrieve the current time." >&2
exitCode=1
fi
exit "$exitCode"
fi
echo ""
fi
# If no configuration files exist and an update is necessary, create a new configuration file
if [[ -z $timeSyncConfigFiles && $updateTimeSyncConfig == "true" ]]; then
timestamp=$(date "+%s")
ntpFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-ntp.conf"
echo "Creating a new timedatectl configuration file at '$ntpFilePath'."
# Write the new configuration file
exec 3>&1
{
echo "[Time]"
if [[ -n $_arg_syncServers ]]; then
echo "NTP=$primarySyncServer"
echo "Setting primary sync server to '$primarySyncServer'." >&3
if [[ -n $fallbackSyncServer ]]; then
echo "FallbackNTP=$fallbackSyncServer"
echo "Setting fallback sync server to '$fallbackSyncServer'." >&3
else
echo "#FallbackNTP=ntp.ubuntu.com"
fi
else
echo "#NTP="
echo "#FallbackNTP=ntp.ubuntu.com"
fi
echo "#RootDistanceMaxSec=5"
if [[ -n $_arg_syncIntervalMinimumInSeconds ]]; then
echo "PollIntervalMinSec=$_arg_syncIntervalMinimumInSeconds"
echo "Setting minimum sync interval to '$_arg_syncIntervalMinimumInSeconds' seconds." >&3
else
echo "#PollIntervalMinSec=32"
fi
if [[ -n $_arg_syncIntervalMaximumInSeconds ]]; then
echo "PollIntervalMaxSec=$_arg_syncIntervalMaximumInSeconds"
echo "Setting maximum sync interval to '$_arg_syncIntervalMaximumInSeconds' seconds." >&3
else
echo "#PollIntervalMaxSec=2048"
fi
echo "#ConnectionRetrySec=30"
echo "#SaveIntervalSec=60"
} > "$ntpFilePath"
# Validate the creation of the new configuration file
if [[ ! -s $ntpFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$ntpFilePath'. Unable to set sync servers or configure the sync interval." 1
else
echo "Successfully created a new timedatectl configuration file."
# Enable NTP if it is currently disabled
if timedatectl show | grep -w "NTP" | grep "no" 1> /dev/null; then
echo "Turning on NTP."
if ! timedatectl set-ntp true; then
_PRINT_HELP=no die "[Error] Failed to turn on NTP." 1
fi
fi
# Restart the systemd-timesyncd service to apply changes
echo "Restarting systemd-timesyncd."
if ! systemctl daemon-reload; then
_PRINT_HELP=no die "[Error] Failed to run 'systemctl daemon-reload'." 1
fi
if ! systemctl restart systemd-timesyncd; then
_PRINT_HELP=no die "[Error] Failed to restart 'systemd-timesyncd'." 1
fi
echo "Successfully updated the NTP configuration."
echo ""
echo "### Previous timedatectl Settings ###"
echo "Timezone: $previousTimeZone"
echo "$previousTimeSettings"
echo ""
if ! currentTimeZone=$(timedatectl show | grep "Timezone=" | sed "s/Timezone=//" | sed "s/^[[:space:]]*//g" | sed "s/[[:space:]]*$//g"); then
echo "[Error] Failed to retrieve the current timezone." >&2
exitCode=1
fi
echo "### Current timedatectl Settings ###"
echo "Timezone: $currentTimeZone"
if ! currentConfig=$(systemd-analyze cat-config systemd/timesyncd.conf --no-pager); then
echo "[Error] Failed to retrieve the current time settings." >&2
exitCode=1
fi
echo "$currentConfig" | awk '/^# \/.*\.conf/ { print; next } /^\s*#/ { next } { print }'
echo ""
echo "### Current Time ###"
if ! date "+%x %r"; then
echo "[Error] Failed to retrieve the current time." >&2
exitCode=1
fi
exit "$exitCode"
fi
fi
# Check if a primary sync server is specified
if [[ -n $primarySyncServer ]]; then
# Retrieve the current primary sync server configuration
currentPrimarySyncServer=$(grep -w -r -H "^NTP" /etc/systemd/timesyncd.conf.d/*.conf)
currentPrimarySyncServer=$(echo "$currentPrimarySyncServer" | grep -v '^$')
currentPrimaryServerCount=$(echo "$currentPrimarySyncServer" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate primary sync server entries
if [[ $currentPrimaryServerCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple primary sync servers were found. Please correct this before attempting to adjust the sync servers.
$currentPrimarySyncServer" 1
fi
# Update the primary sync server if necessary
if [[ -n $currentPrimarySyncServer ]]; then
if ! echo "$currentPrimarySyncServer" | grep -w "$primarySyncServer" 1> /dev/null 2> /dev/null; then
currentPrimaryValue=$(echo "$currentPrimarySyncServer" | awk -F "=" '{ print $2 }')
echo "Updating the primary sync server from '$currentPrimaryValue' to '$primarySyncServer'."
filePath=${currentPrimarySyncServer//:*/}
timestamp=$(date "+%s")
updatedFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-ntp.conf"
# Save the updated configuration file
echo "Saving the updated configuration file to '$updatedFilePath'."
sed "s/^NTP=.*/NTP=$primarySyncServer/g" "$filePath" > "$updatedFilePath"
# Validate the creation of the updated file
if [[ ! -s $updatedFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$updatedFilePath'. This is needed to adjust the sync servers." 1
fi
# Backup the old configuration file
echo "Backing up the old configuration file to '$filePath.old'."
if ! mv "$filePath" "$filePath.old"; then
_PRINT_HELP=no die "[Error] Failed to remove the old config file at '$filePath'. This is needed to adjust the sync servers." 1
fi
echo "Successfully updated the primary sync server."
else
echo "The primary NTP server is already set to '$primarySyncServer'."
fi
fi
# Create a new configuration file if no primary sync server exists
if [[ -z $currentPrimarySyncServer ]]; then
echo "Setting the primary sync server to '$primarySyncServer'."
timestamp=$(date "+%s")
ntpPrimaryFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-ntp-primary-server.conf"
echo "Creating configuration file at '$ntpPrimaryFilePath'."
# Write the new configuration file
{
echo "[Time]"
echo "NTP=$primarySyncServer"
echo "#FallbackNTP=ntp.ubuntu.com"
echo "#RootDistanceMaxSec=5"
echo "#PollIntervalMinSec=32"
echo "#PollIntervalMaxSec=2048"
echo "#ConnectionRetrySec=30"
echo "#SaveIntervalSec=60"
} > "$ntpPrimaryFilePath"
# Validate the creation of the new file
if [[ ! -s $ntpPrimaryFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$ntpPrimaryFilePath'. This is needed to adjust the sync servers." 1
fi
echo "Successfully set the primary sync server."
fi
# Update the list of configuration files and mark the need to restart timedatectl
timeSyncConfigFiles=$(ls /etc/systemd/timesyncd.conf.d/*.conf 2> /dev/null)
needToRestartTimedatectl="yes"
echo ""
sleep 1
fi
# Check if a fallback sync server is specified
if [[ -n $fallbackSyncServer ]]; then
# Retrieve the current fallback sync server configuration
currentFallbackSyncServer=$(grep -w -r -H "^FallbackNTP" /etc/systemd/timesyncd.conf.d/*.conf)
currentFallbackSyncServer=$(echo "$currentFallbackSyncServer" | grep -v '^$')
currentFallbackServerCount=$(echo "$currentFallbackSyncServer" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate fallback sync server entries
if [[ $currentFallbackServerCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple fallback sync servers were found. Please correct this before attempting to adjust the sync servers.
$currentPrimarySyncServer" 1
fi
# Update the fallback sync server if necessary
if [[ -n $currentFallbackSyncServer ]]; then
if ! echo "$currentFallbackSyncServer" | grep -w "$fallbackSyncServer" 1> /dev/null 2> /dev/null; then
currentFallbackValue=$(echo "$currentFallbackSyncServer" | awk -F "=" '{ print $2 }')
echo "Updating the fallback sync server from '$currentFallbackValue' to '$fallbackSyncServer'."
filePath=${currentFallbackSyncServer//:*/}
timestamp=$(date "+%s")
updatedFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-ntp.conf"
# Save the updated configuration file
echo "Saving the updated configuration file to '$updatedFilePath'."
sed "s/^FallbackNTP=.*/FallbackNTP=$fallbackSyncServer/g" "$filePath" > "$updatedFilePath"
# Validate the creation of the updated file
if [[ ! -s $updatedFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$updatedFilePath'. This is needed to adjust the sync servers." 1
fi
# Backup the old configuration file
echo "Backing up the old configuration file to '$filePath.old'."
if ! mv "$filePath" "$filePath.old"; then
_PRINT_HELP=no die "[Error] Failed to remove the old config file at '$filePath'. This is needed to adjust the sync servers." 1
fi
echo "Successfully updated the fallback server."
else
echo "The fallback NTP server is already set to '$fallbackSyncServer'."
fi
fi
# Create a new configuration file if no fallback sync server exists
if [[ -z $currentFallbackSyncServer ]]; then
echo "Setting the fallback sync server to '$fallbackSyncServer'."
timestamp=$(date "+%s")
ntpFallbackFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-ntp-fallback-server.conf"
echo "Creating new configuration file at '$ntpFallbackFilePath'."
{
echo "[Time]"
echo "#NTP="
echo "FallbackNTP=$fallbackSyncServer"
echo "#RootDistanceMaxSec=5"
echo "#PollIntervalMinSec=32"
echo "#PollIntervalMaxSec=2048"
echo "#ConnectionRetrySec=30"
echo "#SaveIntervalSec=60"
} > "$ntpFallbackFilePath"
# Validate the creation of the new file
if [[ ! -s $ntpFallbackFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$ntpFallbackFilePath'. This is needed to adjust the sync servers." 1
fi
echo "Successfully set the fallback sync server."
fi
# Update the list of configuration files and mark the need to restart timedatectl
timeSyncConfigFiles=$(ls /etc/systemd/timesyncd.conf.d/*.conf 2> /dev/null)
needToRestartTimedatectl="yes"
echo ""
sleep 1
fi
# Check if a minimum sync interval is specified
if [[ -n $_arg_syncIntervalMinimumInSeconds ]]; then
# Retrieve the current minimum and maximum sync interval configurations
currentMinimumSyncInterval=$(grep -w -r -H "^PollIntervalMinSec" /etc/systemd/timesyncd.conf.d/*.conf)
currentMinimumSyncInterval=$(echo "$currentMinimumSyncInterval" | grep -v '^$')
currentMinimumSyncIntervalCount=$(echo "$currentMinimumSyncInterval" | awk 'NF' | wc -l | xargs)
currentMaximumSyncInterval=$(grep -w -r -H "^PollIntervalMaxSec" /etc/systemd/timesyncd.conf.d/*.conf)
currentMaximumSyncInterval=$(echo "$currentMaximumSyncInterval" | grep -v '^$')
currentMaximumSyncIntervalCount=$(echo "$currentMaximumSyncInterval" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate minimum or maximum sync interval entries
if [[ $currentMinimumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple minimum sync intervals were found. Please correct this before attempting to adjust the minimum sync interval.
$currentMinimumSyncInterval" 1
fi
if [[ $currentMaximumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple maximum sync intervals were found. Please correct this before attempting to adjust the minimum sync interval.
$currentMaximumSyncInterval" 1
fi
# Validate the relationship between minimum and maximum sync intervals
if [[ -n $currentMaximumSyncInterval && -z $_arg_syncIntervalMaximumInSeconds ]]; then
currentMaxValue=$(echo "$currentMaximumSyncInterval" | awk -F "=" '{ print $2 }')
if [[ currentMaxValue -lt $_arg_syncIntervalMinimumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] The maximum sync interval '$_arg_syncIntervalMaximumInSeconds' is less than the minimum sync interval '$_arg_syncIntervalMinimumInSeconds'. The minimum sync interval must be less than the max sync interval." 1
fi
fi
# Update the minimum sync interval if necessary
if [[ -n $currentMinimumSyncInterval ]]; then
if ! echo "$currentMinimumSyncInterval" | grep -w "$_arg_syncIntervalMinimumInSeconds" 1> /dev/null 2> /dev/null; then
currentSyncValue=$(echo "$_arg_syncIntervalMinimumInSeconds" | awk -F "=" '{ print $2 }')
echo "Updating the minimum sync interval from '$currentSyncValue' to '$_arg_syncIntervalMinimumInSeconds' seconds."
filePath=${currentMinimumSyncInterval//:*/}
timestamp=$(date "+%s")
updatedFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-minimum-syncinterval.conf"
# Save the updated configuration file
echo "Saving the updated configuration file to '$updatedFilePath'."
sed "s/^PollIntervalMinSec=.*/PollIntervalMinSec=$_arg_syncIntervalMinimumInSeconds/g" "$filePath" > "$updatedFilePath"
# Validate the creation of the updated file
if [[ ! -s $updatedFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$updatedFilePath'. This is needed to adjust the minimum sync interval." 1
fi
# Backup the old configuration file
echo "Backing up the old configuration file to '$filePath.old'."
if ! mv "$filePath" "$filePath.old"; then
_PRINT_HELP=no die "[Error] Failed to remove the old config file at '$filePath'. This is needed to adjust the minimum sync interval." 1
fi
echo "Successfully updated the minimum sync interval."
else
echo "The current minimum sync interval is already set to '$_arg_syncIntervalMinimumInSeconds' seconds."
fi
fi
# Create a new configuration file if no minimum sync interval exists
if [[ -z $currentMinimumSyncInterval ]]; then
echo "Setting the minimum sync interval to '$_arg_syncIntervalMinimumInSeconds'."
timestamp=$(date "+%s")
minIntervalFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-minimum-syncinterval.conf"
echo "Creating the configuration file at '$minIntervalFilePath'."
{
echo "[Time]"
echo "#NTP="
echo "#FallbackNTP=ntp.ubuntu.com"
echo "#RootDistanceMaxSec=5"
echo "PollIntervalMinSec=$_arg_syncIntervalMinimumInSeconds"
echo "#PollIntervalMaxSec=2048"
echo "#ConnectionRetrySec=30"
echo "#SaveIntervalSec=60"
} > "$minIntervalFilePath"
# Validate the creation of the new file
if [[ ! -s $minIntervalFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$minIntervalFilePath'. This is needed to adjust the minimum sync interval." 1
fi
echo "Successfully set the minimum sync interval."
fi
# Update the list of configuration files and mark the need to restart timedatectl
timeSyncConfigFiles=$(ls /etc/systemd/timesyncd.conf.d/*.conf 2> /dev/null)
needToRestartTimedatectl="yes"
echo ""
sleep 1
fi
# Check if the maximum sync interval argument is provided
if [[ -n $_arg_syncIntervalMaximumInSeconds ]]; then
# Retrieve the current maximum sync interval configuration
currentMaximumSyncInterval=$(grep -w -r -H "^PollIntervalMaxSec" /etc/systemd/timesyncd.conf.d/*.conf)
currentMaximumSyncInterval=$(echo "$currentMaximumSyncInterval" | grep -v '^$')
currentMaximumSyncIntervalCount=$(echo "$currentMaximumSyncInterval" | awk 'NF' | wc -l | xargs)
# Retrieve the current minimum sync interval configuration
currentMinimumSyncInterval=$(grep -w -r -H "^PollIntervalMinSec" /etc/systemd/timesyncd.conf.d/*.conf)
currentMinimumSyncInterval=$(echo "$currentMinimumSyncInterval" | grep -v '^$')
currentMinimumSyncIntervalCount=$(echo "$currentMinimumSyncInterval" | awk 'NF' | wc -l | xargs)
# Ensure there are no duplicate maximum sync interval entries
if [[ $currentMaximumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple maximum sync intervals were found. Please correct this before attempting to adjust the maximum sync interval.
$currentMaximumSyncInterval" 1
fi
# Ensure there are no duplicate minimum sync interval entries
if [[ $currentMinimumSyncIntervalCount -gt 1 ]]; then
_PRINT_HELP=no die "[Error] Multiple minimum sync intervals were found. Please correct this before attempting to adjust the maximum sync interval.
$currentMinimumSyncInterval" 1
fi
# Validate the relationship between minimum and maximum sync intervals
if [[ -n $currentMinimumSyncInterval && -z $_arg_syncIntervalMinimumInSeconds ]]; then
currentMinValue=$(echo "$currentMinimumSyncInterval" | awk -F "=" '{ print $2 }')
if [[ currentMinValue -gt $_arg_syncIntervalMaximumInSeconds ]]; then
_PRINT_HELP=yes die "[Error] The maximum sync interval '$_arg_syncIntervalMaximumInSeconds' is less than the minimum sync interval '$_arg_syncIntervalMinimumInSeconds'. The minimum sync interval must be less than the max sync interval." 1
fi
fi
# Update the maximum sync interval if necessary
if [[ -n $currentMaximumSyncInterval ]]; then
if ! echo "$currentMaximumSyncInterval" | grep -w "$_arg_syncIntervalMaximumInSeconds" 1> /dev/null 2> /dev/null; then
currentSyncValue=$(echo "$_arg_syncIntervalMaximumInSeconds" | awk -F "=" '{ print $2 }')
echo "Updating the maximum sync interval from '$currentSyncValue' to '$_arg_syncIntervalMaximumInSeconds' seconds."
filePath=${currentMaximumSyncInterval//:*/}
timestamp=$(date "+%s")
updatedFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-maximum-syncinterval.conf"
# Save the updated configuration file
echo "Saving the updated configuration file to '$updatedFilePath'."
sed "s/^PollIntervalMaxSec=.*/PollIntervalMaxSec=$_arg_syncIntervalMaximumInSeconds/g" "$filePath" > "$updatedFilePath"
# Validate the creation of the updated file
if [[ ! -s $updatedFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$updatedFilePath'. This is needed to adjust the maximum sync interval." 1
fi
# Backup the old configuration file
echo "Backing up the old configuration file to '$filePath.old'."
if ! mv "$filePath" "$filePath.old"; then
_PRINT_HELP=no die "[Error] Failed to remove the old config file at '$filePath'. This is needed to adjust the maximum sync interval." 1
fi
echo "Successfully updated the maximum sync interval."
else
echo "The current maximum sync interval is already set to '$_arg_syncIntervalMaximumInSeconds' seconds."
fi
fi
# Create a new configuration file if no maximum sync interval exists
if [[ -z $currentMaximumSyncInterval ]]; then
echo "Setting the maximum sync interval to '$_arg_syncIntervalMaximumInSeconds'."
timestamp=$(date "+%s")
maxIntervalFilePath="/etc/systemd/timesyncd.conf.d/$timestamp-maximum-syncinterval.conf"
echo "Creating the configuration file at '$maxIntervalFilePath'."
{
echo "[Time]"
echo "#NTP="
echo "#FallbackNTP=ntp.ubuntu.com"
echo "#RootDistanceMaxSec=5"
echo "#PollIntervalMinSec=32"
echo "PollIntervalMaxSec=$_arg_syncIntervalMaximumInSeconds"
echo "#ConnectionRetrySec=30"
echo "#SaveIntervalSec=60"
} > "$maxIntervalFilePath"
# Validate the creation of the new file
if [[ ! -s $maxIntervalFilePath ]]; then
_PRINT_HELP=no die "[Error] Failed to create the file '$maxIntervalFilePath'. This is needed to adjust the maximum sync interval." 1
fi
echo "Successfully set the maximum sync interval."
fi
# Update the list of configuration files and mark the need to restart timedatectl
timeSyncConfigFiles=$(ls /etc/systemd/timesyncd.conf.d/*.conf 2> /dev/null)
needToRestartTimedatectl="yes"
echo ""
sleep 1
fi
# Restart timedatectl if necessary
if [[ $needToRestartTimedatectl == "yes" ]]; then
# Enable NTP if it is currently disabled
if timedatectl show | grep -w "NTP" | grep "no" 1> /dev/null; then
echo "Turning on NTP."
if ! timedatectl set-ntp true; then
_PRINT_HELP=no die "[Error] Failed to turn on NTP." 1
fi
fi
# Restart the systemd-timesyncd service to apply changes
if ! systemctl daemon-reload; then
_PRINT_HELP=no die "[Error] Failed to run 'systemctl daemon-reload'." 1
fi
if ! systemctl restart systemd-timesyncd; then
_PRINT_HELP=no die "[Error] Failed to restart 'systemd-timesyncd'." 1
fi
fi
echo "### Previous timedatectl Settings ###"
echo "Timezone: $previousTimeZone"
echo "$previousTimeSettings"
echo ""
if ! currentTimeZone=$(timedatectl show | grep "Timezone=" | sed "s/Timezone=//" | sed "s/^[[:space:]]*//g" | sed "s/[[:space:]]*$//g"); then
echo "[Error] Failed to retrieve the current timezone." >&2
exitCode=1
fi
echo "### Current timedatectl Settings ###"
echo "Timezone: $currentTimeZone"
if ! currentConfig=$(systemd-analyze cat-config systemd/timesyncd.conf --no-pager); then
echo "[Error] Failed to retrieve the current time settings." >&2
exitCode=1
fi
echo "$currentConfig" | awk '/^# \/.*\.conf/ { print; next } /^\s*#/ { next } { print }'
echo ""
echo "### Current Time ###"
if ! date "+%x %r"; then
echo "[Error] Failed to retrieve the current time." >&2
exitCode=1
fi
exit "$exitCode"
Detailed Breakdown
Here’s what the script does, step by step.
- Argument parsing and help
- Supports:
–listTimeZones|-l
–setTimeZone|-t <tz>
–setSyncServers|-s <server[,server]>
–syncIntervalMinimumInSeconds|–min <sec>
–syncIntervalMaximumInSeconds|–max <sec> - Shows a concise usage menu and validates combinations (e.g., you can’t use –listTimeZones with other options).
- Supports:
- Environment and prerequisites
- Requires root (id -u check).
- Requires timedatectl and systemd-timesyncd. If systemd-timesyncd is disabled, it exits with guidance.
- If NTP-related options are used, verifies the host supports NTP via timedatectl show | CanNTP=.
- Input validation
- Time zone: validates against timedatectl list-timezones.
- NTP servers: accepts up to two, allows only safe characters, and live-checks UDP/123 reachability using a /dev/udp probe with a timeout.
- Intervals: numeric, 1–86400 seconds, and ensures min ≤ max.
- Discovery of current state
- Gathers the effective timesyncd configuration with systemd-analyze cat-config systemd/timesyncd.conf, strips comments, and stores a “previous settings” snapshot for before/after reporting.
- Time zone changes
- If requested, calls timedatectl set-timezone <tz> and confirms the result. If already set, it reports that and moves on.
- Configuration layering
- Uses /etc/systemd/timesyncd.conf.d/ drop-ins instead of editing /etc/systemd/timesyncd.conf in place.
- If the default file exists and differs from desired state, marks that an update is required, then writes a new, timestamped drop-in, e.g., /etc/systemd/timesyncd.conf.d/1752702365-ntp.conf.
- Selective updates
- Updates primary NTP, fallback NTP, PollIntervalMinSec, and PollIntervalMaxSec individually if they exist; otherwise creates specific drop-ins for each.
- When updating, it writes a new file, then backs up the old one as .old, avoiding fragile in-place edits.
- Apply and report
- Ensures set-ntp true if NTP was disabled.
- Runs systemctl daemon-reload && systemctl restart systemd-timesyncd.
- Prints a before/after configuration diff-style view and the current time, so you have immediate confirmation.
Potential Use Cases
Case study: An MSP onboards a 500-node manufacturing client across three plants. Devices were imaged with a generic UTC zone and inconsistent NTP. Kerberos tickets and TLS validation intermittently fail. The engineer pushes this shell script to set the time zone in Linux and NTP timedatectl settings via their RMM with per-site parameters:
- Site A: –setTimeZone “America/Chicago” –setSyncServers “0.pool.ntp.org,1.pool.ntp.org” –min 32 –max 2048
- Site B uses a corporate NTP pair; Site C uses regional pool servers.
Within minutes, authentication failures disappear and SIEM correlation is clean.
Comparisons
- systemd-timesyncd vs chrony: chrony is feature-rich and popular for servers needing advanced controls (slew rates, sources, statistics). For endpoints and many servers, systemd-timesyncd is lightweight and “good enough,” especially when standardized with timedatectl.
- Editing /etc/localtime manually vs timedatectl set-timezone: Direct symlink edits work but are brittle at scale; timedatectl is declarative and auditable.
- Editing /etc/systemd/timesyncd.conf directly vs drop-ins: Drop-ins are safer, survive package updates, and are easier to diff/rollback—this script follows best practice.
Implications for Security and Reliability
Correct time underpins Kerberos, JWT/SSO token validity, certificate verification, and log correlation. Misaligned clocks create false positives in SIEMs, fail compliance checks, and weaken forensic timelines. Standardizing NTP, PollIntervalMinSec, and PollIntervalMaxSec reduces drift and speeds convergence after outages.
Recommendations
- Parameterize by site or tenant: Keep time zones and NTP servers aligned with geography and policy.
- Use internal NTP first: Prefer corporate stratum servers; fall back to pool servers.
- Keep intervals sane: Defaults (32–2048 seconds) are reasonable for most environments; tighten only with a clear need.
- Document and store artifacts: Retain the script’s before/after outputs in your CMDB or ticket.
- Run as root in maintenance windows: Restarting systemd-timesyncd is low risk but still a service change.
- Monitor: After rollout, verify timedatectl show across a sample to confirm NTP=yes and correct peers.
Final Thoughts
For IT teams standardizing Linux estates, this shell script to set the time zone and NTP timedatectl settings in Linuxoffers an auditable, idempotent approach that plays nicely with modern distros. Paired with NinjaOne, you can deploy it as a script policy, feed site-specific parameters through Custom Fields, schedule periodic compliance checks, and alert on drift. The result is predictable time across your fleet—and fewer mysteries in your logs.