chef
tls
+
+
jest
android
lit
spring
eslint
+
neo4j
+
cassandra
css
*
swc
+
+
clickhouse
+
+
nuxt
spring
+
+
+
sse
+
+
fiber
+
+
saml
+
+
+
+
ts
gcp
+
smtp
objc
+
mysql
helm
spring
!=
jenkins
abap
smtp
+
+
+
babel
fastapi
f#
+
+
c#
vault
intellij
json
terraform
+
js
android
react
clickhouse
couchdb
+
+
postgres
+
--
+
+
+
+
micronaut
++
grpc
+
+
+
+
+
haskell
Back to Blog
Setting Up Time Zones and NTP Synchronization in AlmaLinux ⏰
AlmaLinux System Administration NTP

Setting Up Time Zones and NTP Synchronization in AlmaLinux ⏰

Published Aug 19, 2025

Master time management in AlmaLinux with comprehensive time zone configuration and NTP synchronization. Learn to set up Chrony, configure time servers, troubleshoot drift issues, and ensure accurate system time across your infrastructure.

20 min read
0 views
Table of Contents

Accurate time synchronization is crucial for modern IT infrastructure. From log correlation and security auditing to distributed systems coordination and regulatory compliance, proper time management ensures your AlmaLinux systems operate reliably and securely. This comprehensive guide covers everything you need to know about configuring time zones and implementing robust NTP synchronization using Chrony, the modern replacement for ntpd in AlmaLinux.

Understanding Time in Linux

⏱️ System Clock vs Hardware Clock

Linux systems maintain two distinct clocks that work together:

# Hardware Clock (RTC - Real Time Clock)
sudo hwclock --show
sudo hwclock --verbose

# System Clock (Software Clock)
date
timedatectl status

# Compare both clocks
echo "System: $(date)"
echo "Hardware: $(sudo hwclock --show)"

🔄 Clock Synchronization Flow

Internet Time Servers (Stratum 1)

Regional NTP Servers (Stratum 2)

Organization NTP Servers (Stratum 3)

Local Systems (Stratum 4+)

📊 Time Components

# View all time-related information
timedatectl

# Example output breakdown:
               Local time: Mon 2025-08-19 10:30:45 EDT
           Universal time: Mon 2025-08-19 14:30:45 UTC
                 RTC time: Mon 2025-08-19 14:30:45
                Time zone: America/New_York (EDT, -0400)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Time Zone Configuration

🌍 Viewing Available Time Zones

# List all available time zones
timedatectl list-timezones

# Search for specific region
timedatectl list-timezones | grep America
timedatectl list-timezones | grep Europe
timedatectl list-timezones | grep Asia

# Count total time zones
timedatectl list-timezones | wc -l

# View current time zone
timedatectl | grep "Time zone"
ls -l /etc/localtime

🔧 Setting Time Zone

# Set time zone using timedatectl
sudo timedatectl set-timezone America/New_York

# Alternative method using tzdata
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/America/New_York /etc/localtime

# Verify change
timedatectl
date

# Set UTC time zone
sudo timedatectl set-timezone UTC

📝 Time Zone Configuration Files

# Main time zone link
ls -l /etc/localtime

# Time zone data directory
ls /usr/share/zoneinfo/

# Environment variable method
export TZ='America/Los_Angeles'
date

# Make permanent for user
echo 'export TZ="America/Los_Angeles"' >> ~/.bashrc

# System-wide environment
echo 'TZ="America/Los_Angeles"' | sudo tee /etc/environment

🌐 Multiple Time Zone Display

# Create time zone display script
cat << 'EOF' > ~/world-clock.sh
#!/bin/bash

echo "🌍 World Clock - $(date +'%Y-%m-%d')"
echo "================================"

zones=(
    "America/New_York:New York"
    "America/Los_Angeles:Los Angeles"
    "Europe/London:London"
    "Europe/Paris:Paris"
    "Asia/Tokyo:Tokyo"
    "Asia/Shanghai:Shanghai"
    "Australia/Sydney:Sydney"
    "UTC:UTC"
)

for zone_info in "${zones[@]}"; do
    IFS=':' read -r zone city <<< "$zone_info"
    time=$(TZ="$zone" date +'%H:%M:%S %Z')
    printf "%-20s %s\n" "$city:" "$time"
done
EOF

chmod +x ~/world-clock.sh
~/world-clock.sh

Introduction to Chrony

🚀 Why Chrony?

Chrony is the default NTP implementation in AlmaLinux, offering several advantages:

  • Faster synchronization after network reconnections
  • Better accuracy for systems with intermittent network access
  • Lower resource usage compared to ntpd
  • Works well with virtual machines
  • Handles large time corrections gracefully

📋 Chrony vs NTPd Comparison

# Feature comparison
cat << EOF
| Feature               | Chrony | NTPd |
|----------------------|--------|------|
| Startup sync speed   | Fast   | Slow |
| Intermittent network | Good   | Poor |
| Virtual machine      | Good   | Fair |
| Resource usage       | Low    | Med  |
| Large corrections    | Yes    | No   |
| Precision timing     | Good   | Best |
EOF

Installing and Configuring Chrony

📦 Installation

# Check if Chrony is installed
rpm -qa | grep chrony

# Install Chrony
sudo dnf install -y chrony

# Enable and start service
sudo systemctl enable chronyd
sudo systemctl start chronyd

# Verify installation
chronyc --version
systemctl status chronyd

🔧 Basic Configuration

# Main configuration file
sudo nano /etc/chrony.conf

Essential configuration options:

# Use public NTP servers from pool.ntp.org
pool 2.almalinux.pool.ntp.org iburst

# Alternative specific servers
server 0.north-america.pool.ntp.org iburst
server 1.north-america.pool.ntp.org iburst
server 2.north-america.pool.ntp.org iburst
server 3.north-america.pool.ntp.org iburst

# Record rate of system clock drift
driftfile /var/lib/chrony/drift

# Allow system clock step for large corrections
makestep 1.0 3

# Enable kernel RTC synchronization
rtcsync

# Specify directory for log files
logdir /var/log/chrony

# Log tracking, statistics, and measurements
log tracking measurements statistics

# Allow NTP client access from local network
allow 192.168.1.0/24
allow 10.0.0.0/8

# Deny all other access
deny all

# Local stratum when no source available
local stratum 10

# Bind to specific interface
bindaddress 192.168.1.100

# Mail admin on clock errors
mailonchange root@localhost 0.5

🎯 Advanced Configuration Options

# High-precision configuration
sudo nano /etc/chrony.conf

# Add advanced options:
# Increase polling interval for stable sources
server time1.google.com iburst minpoll 6 maxpoll 10

# Hardware timestamping (if supported)
hwtimestamp eth0

# Prefer specific server
server ntp.example.com prefer

# Burst mode for faster initial sync
initstepslew 30 ntp1.example.com ntp2.example.com

# Temperature compensation
tempcomp /sys/class/hwmon/hwmon0/temp1_input 30 0.0 0.0

# Leap second handling
leapsectz right/UTC

# Manual time input allowed
manual

# Key file for authentication
keyfile /etc/chrony.keys

# Command access restrictions
cmdallow 127.0.0.1
cmddeny all

NTP Server Configuration

🖥️ Setting Up NTP Server

# Configure as NTP server
sudo nano /etc/chrony.conf

# Add server configuration:
# Use stratum 1 servers as sources
server time1.google.com iburst
server time2.google.com iburst
server time.cloudflare.com iburst
server time.nist.gov iburst

# Allow client access
allow 192.168.0.0/16
allow 10.0.0.0/8
allow 172.16.0.0/12

# Serve time even if not synchronized
local stratum 10

# Log client accesses
log measurements statistics tracking clientlog

# Client rate limiting
ratelimit interval 2 burst 8
clientloglimit 1048576

# Restart service
sudo systemctl restart chronyd

# Open firewall ports
sudo firewall-cmd --add-service=ntp --permanent
sudo firewall-cmd --reload

📊 Server Monitoring

# Monitor server status
chronyc tracking
chronyc sources -v
chronyc sourcestats
chronyc clients

# Create monitoring script
cat << 'EOF' > /usr/local/bin/ntp-server-monitor.sh
#!/bin/bash

echo "🕐 NTP Server Status Report"
echo "=========================="
echo

echo "📊 Server Statistics:"
chronyc serverstats

echo -e "\n📡 Client Connections:"
chronyc clients | head -20

echo -e "\n⚡ System Performance:"
chronyc tracking | grep -E "System time|Root delay|Root dispersion"

echo -e "\n📈 Source Quality:"
chronyc sourcestats
EOF

chmod +x /usr/local/bin/ntp-server-monitor.sh

NTP Client Setup

💻 Basic Client Configuration

# Configure NTP client
sudo nano /etc/chrony.conf

# Point to internal NTP servers
server ntp1.company.local iburst
server ntp2.company.local iburst
server ntp3.company.local iburst

# Remove public pool servers
# pool 2.almalinux.pool.ntp.org iburst

# Don't serve time to others
deny all

# Quick initial sync
makestep 1.0 3

# Restart service
sudo systemctl restart chronyd

# Verify synchronization
chronyc tracking
chronyc sources

🔄 Client Auto-Discovery

# Configure DHCP-provided NTP servers
sudo nano /etc/dhcp/dhclient.conf

# Add NTP option request
request subnet-mask, broadcast-address, time-offset, routers,
        domain-name, domain-name-servers, domain-search, host-name,
        dhcp6.name-servers, dhcp6.domain-search, dhcp6.fqdn, dhcp6.sntp-servers,
        netbios-name-servers, netbios-scope, interface-mtu,
        rfc3442-classless-static-routes, ntp-servers;

# Process DHCP NTP servers
cat << 'EOF' > /etc/dhcp/dhclient-exit-hooks.d/chrony
#!/bin/bash
if [ "$reason" = "BOUND" ] || [ "$reason" = "RENEW" ] || [ "$reason" = "REBIND" ] || [ "$reason" = "REBOOT" ]; then
    if [ -n "$new_ntp_servers" ]; then
        for server in $new_ntp_servers; do
            echo "server $server iburst" >> /etc/chrony.conf.dhcp
        done
        systemctl restart chronyd
    fi
fi
EOF

chmod +x /etc/dhcp/dhclient-exit-hooks.d/chrony

Time Source Selection

🌐 Public NTP Servers

# Popular public NTP servers
cat << EOF > /etc/chrony.conf.d/public-servers.conf
# Google Public NTP
server time1.google.com iburst
server time2.google.com iburst
server time3.google.com iburst
server time4.google.com iburst

# Cloudflare NTP
server time.cloudflare.com iburst

# NIST servers
server time.nist.gov iburst
server time-a-g.nist.gov iburst

# Pool.ntp.org
pool 0.pool.ntp.org iburst maxsources 4
pool 1.pool.ntp.org iburst maxsources 4
EOF

🏢 Regional Server Selection

# Configure regional pools
cat << EOF > /etc/chrony.conf.d/regional.conf
# North America
pool 0.north-america.pool.ntp.org iburst
pool 1.north-america.pool.ntp.org iburst

# Europe
pool 0.europe.pool.ntp.org iburst
pool 1.europe.pool.ntp.org iburst

# Asia
pool 0.asia.pool.ntp.org iburst
pool 1.asia.pool.ntp.org iburst
EOF

📡 GPS and Hardware Sources

# Configure GPS time source
cat << EOF >> /etc/chrony.conf
# GPS reference clock
refclock SHM 0 refid GPS precision 1e-1
refclock PPS /dev/pps0 refid PPS

# Prefer GPS when available
server time.example.com noselect
EOF

# Configure PTP hardware timestamping
cat << EOF >> /etc/chrony.conf
# PTP hardware timestamping
hwtimestamp eth0 rxfilter all
EOF

Monitoring Time Synchronization

📊 Real-time Monitoring

# Check synchronization status
chronyc tracking

# Monitor in real-time
watch -n 1 chronyc tracking

# Detailed source information
chronyc sources -v
chronyc sourcestats -v

# Check system time offset
chronyc tracking | grep "System time"

📈 Performance Metrics

# Create comprehensive monitoring script
cat << 'EOF' > /usr/local/bin/time-sync-monitor.sh
#!/bin/bash

clear
echo "⏰ Time Synchronization Monitor"
echo "=============================="
echo

# System time accuracy
echo "📊 System Time Accuracy:"
offset=$(chronyc tracking | grep "System time" | awk '{print $4}')
echo "  Offset: ${offset} seconds"

# Root delay and dispersion
echo -e "\n🌐 Network Quality:"
chronyc tracking | grep -E "Root delay|Root dispersion"

# Source statistics
echo -e "\n📡 Time Sources:"
chronyc sources | tail -n +3 | while read line; do
    mode=$(echo "$line" | awk '{print $1}')
    state=$(echo "$line" | awk '{print $2}')
    source=$(echo "$line" | awk '{print $3}')
    offset=$(echo "$line" | awk '{print $9}')
    
    case "$mode$state" in
        "^*") status="✅ Current sync" ;;
        "^+") status="✓ Good source" ;;
        "^-") status="⚠ Not used" ;;
        "^?") status="❌ Unreachable" ;;
        *) status="Unknown" ;;
    esac
    
    printf "  %-30s %-20s Offset: %s\n" "$source" "$status" "$offset"
done

# Drift rate
echo -e "\n⚙️ Clock Drift:"
drift=$(chronyc tracking | grep "Frequency" | awk '{print $3}')
echo "  Drift rate: ${drift} ppm"

# Last update
echo -e "\n🔄 Last Update:"
chronyc tracking | grep "Ref time"
EOF

chmod +x /usr/local/bin/time-sync-monitor.sh

📝 Logging Configuration

# Configure detailed logging
sudo nano /etc/chrony.conf

# Add logging directives:
logdir /var/log/chrony
log measurements statistics tracking
log tempcomp rawmeasurements refclocks

# Rotate logs
cat << EOF | sudo tee /etc/logrotate.d/chrony
/var/log/chrony/*.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    sharedscripts
    postrotate
        /usr/bin/chronyc cyclelogs > /dev/null 2>&1 || true
    endscript
}
EOF

Troubleshooting Time Issues

🔍 Common Problems and Solutions

# Problem: No sources available
chronyc sources
# Solution: Check network connectivity and firewall
sudo firewall-cmd --list-all
ping -c 4 pool.ntp.org

# Problem: Large time offset
chronyc tracking | grep "System time"
# Solution: Force immediate sync
sudo chronyc makestep

# Problem: Sources marked as unreachable
chronyc sources | grep "?"
# Solution: Check DNS and routing
nslookup pool.ntp.org
traceroute pool.ntp.org

# Problem: Clock drift too high
chronyc tracking | grep "Frequency"
# Solution: Calibrate drift file
sudo rm /var/lib/chrony/drift
sudo systemctl restart chronyd

🛠️ Diagnostic Commands

# Comprehensive diagnostics
cat << 'EOF' > /usr/local/bin/ntp-diagnose.sh
#!/bin/bash

echo "🔍 NTP Diagnostics Report"
echo "======================="
echo

# Service status
echo "📌 Service Status:"
systemctl is-active chronyd
systemctl is-enabled chronyd

# Network connectivity
echo -e "\n🌐 Network Tests:"
for server in time.google.com pool.ntp.org time.cloudflare.com; do
    if ping -c 1 -W 1 $server &>/dev/null; then
        echo "  ✅ $server reachable"
    else
        echo "  ❌ $server unreachable"
    fi
done

# Firewall status
echo -e "\n🔥 Firewall Status:"
sudo firewall-cmd --list-services | grep -q ntp && echo "  ✅ NTP service allowed" || echo "  ❌ NTP service not allowed"

# DNS resolution
echo -e "\n📡 DNS Resolution:"
for server in 0.pool.ntp.org 1.pool.ntp.org; do
    if nslookup $server &>/dev/null; then
        echo "  ✅ $server resolves"
    else
        echo "  ❌ $server does not resolve"
    fi
done

# Time source status
echo -e "\n⏰ Time Sources:"
chronyc sources | tail -n +3 | while read line; do
    echo "  $line"
done

# System time accuracy
echo -e "\n📊 System Time:"
chronyc tracking | grep -E "System time|Root delay|Root dispersion|Update interval"
EOF

chmod +x /usr/local/bin/ntp-diagnose.sh

🔄 Recovery Procedures

# Complete reset procedure
sudo systemctl stop chronyd
sudo rm -f /var/lib/chrony/drift
sudo rm -f /var/log/chrony/*
sudo timedatectl set-ntp false
sudo timedatectl set-ntp true
sudo systemctl start chronyd

# Force hardware clock update
sudo hwclock --systohc
sudo hwclock --show

# Manual time set (if needed)
sudo timedatectl set-ntp false
sudo timedatectl set-time "2025-08-19 10:30:00"
sudo timedatectl set-ntp true

Security Considerations

🔒 Access Control

# Restrict NTP access
sudo nano /etc/chrony.conf

# Add access controls:
# Deny all by default
deny all

# Allow specific networks
allow 192.168.1.0/24
allow 10.0.0.0/8

# Rate limiting
ratelimit interval 2 burst 8

# Command restrictions
cmdallow 127.0.0.1
cmddeny all

# Bind to specific interface
bindaddress 192.168.1.100

🔐 Authentication Setup

# Generate authentication keys
cat << EOF | sudo tee /etc/chrony.keys
# Key ID Type Key
1 MD5 HEX:A7B2C3D4E5F6789012345678
2 SHA1 HEX:1234567890ABCDEF1234567890ABCDEF12345678
3 SHA256 HEX:1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF
EOF

sudo chmod 640 /etc/chrony.keys
sudo chown root:chrony /etc/chrony.keys

# Configure key usage
echo "keyfile /etc/chrony.keys" | sudo tee -a /etc/chrony.conf
echo "commandkey 1" | sudo tee -a /etc/chrony.conf

# Use keys for servers
echo "server ntp.example.com key 2" | sudo tee -a /etc/chrony.conf

🛡️ Firewall Configuration

# Configure firewall for NTP
# For NTP server
sudo firewall-cmd --permanent --add-service=ntp
sudo firewall-cmd --permanent --add-port=323/udp

# For NTP client (outbound only)
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -p udp --dport 123 -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p udp --sport 123 -m state --state ESTABLISHED -j ACCEPT

# Reload firewall
sudo firewall-cmd --reload

# Verify rules
sudo firewall-cmd --list-all

High Availability Setup

🔄 Redundant NTP Servers

# Configure multiple NTP servers with preferences
cat << EOF | sudo tee /etc/chrony.conf.d/ha-servers.conf
# Primary servers (Stratum 2)
server ntp1.company.local iburst prefer maxpoll 6
server ntp2.company.local iburst prefer maxpoll 6

# Secondary servers (Stratum 3)
server ntp3.company.local iburst maxpoll 8
server ntp4.company.local iburst maxpoll 8

# Tertiary servers (Public)
pool pool.ntp.org iburst maxsources 2 maxpoll 10
EOF

# Configure failover behavior
echo "minsources 2" | sudo tee -a /etc/chrony.conf
echo "combinelimit 3" | sudo tee -a /etc/chrony.conf

🎯 Load Balancing

# DNS round-robin configuration
# In DNS server, create multiple A records:
# ntp.company.local IN A 192.168.1.10
# ntp.company.local IN A 192.168.1.11
# ntp.company.local IN A 192.168.1.12

# Client configuration
echo "pool ntp.company.local iburst maxsources 3" | sudo tee -a /etc/chrony.conf

📊 Monitoring HA Status

# HA monitoring script
cat << 'EOF' > /usr/local/bin/ntp-ha-monitor.sh
#!/bin/bash

echo "🔄 NTP High Availability Status"
echo "==============================="
echo

# Check number of sources
sources=$(chronyc sources | grep -c "^[^=]")
echo "📡 Active sources: $sources"

if [ "$sources" -lt 2 ]; then
    echo "⚠️  WARNING: Less than 2 sources available!"
fi

# Check source quality
echo -e "\n📊 Source Quality:"
chronyc sources | tail -n +3 | while read line; do
    source=$(echo "$line" | awk '{print $3}')
    stratum=$(echo "$line" | awk '{print $4}')
    reach=$(echo "$line" | awk '{print $6}')
    
    if [ "$reach" = "377" ]; then
        status="✅ Healthy"
    elif [ "$reach" = "0" ]; then
        status="❌ Unreachable"
    else
        status="⚠️  Degraded"
    fi
    
    printf "  %-30s Stratum: %-2s Reach: %-3s %s\n" "$source" "$stratum" "$reach" "$status"
done

# Check for preferred source
echo -e "\n⭐ Preferred Source:"
chronyc sources | grep "^\^*" | awk '{print "  " $3}'
EOF

chmod +x /usr/local/bin/ntp-ha-monitor.sh

Virtual Machine Time Management

🖥️ VM-Specific Configuration

# Detect if running in VM
systemd-detect-virt

# Configure for VMware
if [ "$(systemd-detect-virt)" = "vmware" ]; then
    # Disable VMware time sync
    echo "tools.syncTime = 'FALSE'" >> /etc/vmware-tools/tools.conf
    
    # Use Chrony instead
    echo "refclock PHC /dev/ptp0 poll 3 dpoll -2 offset 0" | sudo tee -a /etc/chrony.conf
fi

# Configure for KVM/QEMU
if [ "$(systemd-detect-virt)" = "kvm" ]; then
    # Enable kvmclock
    echo "refclock PHC /dev/ptp0 poll 2" | sudo tee -a /etc/chrony.conf
fi

# Configure for Hyper-V
if [ "$(systemd-detect-virt)" = "microsoft" ]; then
    # Use Hyper-V time sync
    echo "refclock PHC /dev/ptp0 poll 3 dpoll -2" | sudo tee -a /etc/chrony.conf
fi

⚡ VM Optimization

# Optimize for VMs
cat << EOF | sudo tee -a /etc/chrony.conf
# VM-specific optimizations
makestep 1 3
rtcsync
maxupdateskew 100.0
maxslewrate 1000
EOF

# Faster initial sync for VMs
echo "initstepslew 30 $(grep ^server /etc/chrony.conf | awk '{print $2}' | tr '\n' ' ')" | sudo tee -a /etc/chrony.conf

Compliance and Auditing

📋 Compliance Requirements

# Generate compliance report
cat << 'EOF' > /usr/local/bin/time-compliance-report.sh
#!/bin/bash

echo "📋 Time Synchronization Compliance Report"
echo "========================================="
echo "Generated: $(date)"
echo

# Service status
echo "1. SERVICE CONFIGURATION"
echo "   Service: $(systemctl is-active chronyd)"
echo "   Enabled: $(systemctl is-enabled chronyd)"
echo

# Time accuracy
echo "2. TIME ACCURACY"
offset=$(chronyc tracking | grep "System time" | awk '{print $4}')
echo "   Current offset: ${offset} seconds"
if (( $(echo "$offset < 0.1" | bc -l) )); then
    echo "   Status: ✅ COMPLIANT (< 100ms)"
else
    echo "   Status: ❌ NON-COMPLIANT (> 100ms)"
fi
echo

# Source reliability
echo "3. SOURCE RELIABILITY"
sources=$(chronyc sources | grep -c "^\^")
echo "   Active sources: $sources"
if [ "$sources" -ge 3 ]; then
    echo "   Status: ✅ COMPLIANT (>= 3 sources)"
else
    echo "   Status: ❌ NON-COMPLIANT (< 3 sources)"
fi
echo

# Configuration audit
echo "4. CONFIGURATION AUDIT"
if grep -q "^allow" /etc/chrony.conf; then
    echo "   Access control: ✅ Configured"
else
    echo "   Access control: ❌ Not configured"
fi

if [ -f /etc/chrony.keys ]; then
    echo "   Authentication: ✅ Configured"
else
    echo "   Authentication: ⚠️  Not configured"
fi

# Log availability
echo -e "\n5. AUDIT LOGS"
if [ -d /var/log/chrony ]; then
    echo "   Log directory: ✅ Exists"
    echo "   Log files: $(ls -1 /var/log/chrony | wc -l)"
else
    echo "   Log directory: ❌ Not found"
fi
EOF

chmod +x /usr/local/bin/time-compliance-report.sh

📊 Audit Logging

# Configure audit logging for time changes
sudo auditctl -a always,exit -F arch=b64 -S adjtimex -S settimeofday -S clock_settime -k time-change
sudo auditctl -a always,exit -F arch=b64 -S clock_adjtime -k time-change
sudo auditctl -w /etc/localtime -p wa -k time-change

# View time-related audit events
sudo ausearch -k time-change

# Make rules permanent
echo "-a always,exit -F arch=b64 -S adjtimex -S settimeofday -S clock_settime -k time-change" | sudo tee -a /etc/audit/rules.d/time.rules
echo "-a always,exit -F arch=b64 -S clock_adjtime -k time-change" | sudo tee -a /etc/audit/rules.d/time.rules
echo "-w /etc/localtime -p wa -k time-change" | sudo tee -a /etc/audit/rules.d/time.rules

sudo service auditd restart

Best Practices

✅ Configuration Best Practices

  1. Use multiple time sources (minimum 3, ideally 4-7)
  2. Mix source types (internal + external)
  3. Document time requirements for your applications
  4. Regular monitoring of time accuracy
  5. Implement authentication for critical systems
  6. Keep logs for compliance and troubleshooting
  7. Test failover scenarios regularly
  8. Configure appropriate stratum levels

📋 Maintenance Checklist

# Weekly tasks
- [ ] Check time synchronization status
- [ ] Review source availability
- [ ] Monitor drift rate
- [ ] Check log files for errors

# Monthly tasks
- [ ] Review and update time sources
- [ ] Test failover scenarios
- [ ] Generate compliance reports
- [ ] Update documentation

# Quarterly tasks
- [ ] Audit configuration
- [ ] Review security settings
- [ ] Performance analysis
- [ ] Capacity planning

🔍 Monitoring Script

# Comprehensive monitoring script
cat << 'EOF' > /usr/local/bin/time-health-check.sh
#!/bin/bash

STATUS=0
REPORT=""

# Check service
if systemctl is-active chronyd &>/dev/null; then
    REPORT+="✅ Chronyd service is running\n"
else
    REPORT+="❌ Chronyd service is not running\n"
    STATUS=1
fi

# Check synchronization
if chronyc tracking | grep -q "Leap status.*Normal"; then
    REPORT+="✅ Time synchronized normally\n"
else
    REPORT+="⚠️  Time synchronization issue detected\n"
    STATUS=1
fi

# Check offset
OFFSET=$(chronyc tracking | grep "System time" | awk '{print $4}' | sed 's/[^0-9.]//g')
if (( $(echo "$OFFSET < 1.0" | bc -l) )); then
    REPORT+="✅ Time offset within limits: ${OFFSET}s\n"
else
    REPORT+="❌ Time offset too large: ${OFFSET}s\n"
    STATUS=1
fi

# Check sources
SOURCES=$(chronyc sources | grep -c "^\^")
if [ "$SOURCES" -ge 2 ]; then
    REPORT+="✅ Sufficient time sources: $SOURCES\n"
else
    REPORT+="❌ Insufficient time sources: $SOURCES\n"
    STATUS=1
fi

echo -e "$REPORT"
exit $STATUS
EOF

chmod +x /usr/local/bin/time-health-check.sh

# Add to crontab for regular checks
echo "0 * * * * /usr/local/bin/time-health-check.sh || logger -t time-sync 'Time synchronization issue detected'" | sudo tee -a /etc/crontab

Conclusion

Proper time zone configuration and NTP synchronization are fundamental to maintaining a reliable AlmaLinux infrastructure. Chrony provides a robust, efficient, and flexible solution for time management, suitable for everything from standalone systems to complex distributed environments.

Key takeaways:

  • ⏰ Accurate time is critical for security, logging, and distributed systems
  • 🌍 Proper time zone configuration ensures correct local time display
  • 🚀 Chrony offers superior performance for modern systems, especially VMs
  • 🔒 Security features protect against time-based attacks
  • 📊 Regular monitoring ensures continued accuracy and compliance
  • 🔄 High availability configurations prevent single points of failure

By following this guide, you’ve established a robust time synchronization infrastructure that will maintain accurate time across your AlmaLinux systems, support compliance requirements, and provide the foundation for reliable distributed operations. Remember to regularly monitor your time sources, maintain adequate redundancy, and keep your configuration documented for troubleshooting and auditing purposes.