NTP Client
This role installs and configures Chrony as an NTP (Network Time Protocol) client for accurate time synchronization.
NTP Client Role
Overview
This role installs and configures Chrony as an NTP (Network Time Protocol) client for accurate time synchronization. It installs the chrony package, configures it to sync with the OPNsense firewall as the local NTP server, enables RTC (Real-Time Clock) synchronization, configures clock stepping for large time offsets, and ensures the chronyd service is enabled and started.
Purpose
- Time Synchronization: Keep system clocks accurate and synchronized
- Local NTP Server: Sync from OPNsense firewall (not public internet)
- Clock Drift Management: Track and compensate for hardware clock drift
- Large Offset Handling: Automatically step clock for large time differences
- RTC Sync: Synchronize hardware real-time clock with system time
- Client-Only Mode: Doesn’t listen for incoming NTP requests
- Logging: Track synchronization status and issues
Requirements
- Ansible 2.9 or higher
- Linux system (RedHat or Debian-based)
- Network connectivity to NTP server (OPNsense)
- Proper sudo/root permissions
- OPNsense firewall configured as NTP server
What is NTP?
NTP (Network Time Protocol) is a protocol for synchronizing computer clocks:
- Provides accurate time from reference clocks
- Compensates for network latency
- Adjusts for clock drift
- Essential for logs, authentication, certificates, distributed systems
- Uses UDP port 123
Chrony is a modern NTP implementation that:
- Performs better than traditional ntpd
- Handles intermittent network connections
- Faster synchronization
- Better accuracy
- Lower resource usage
Role Variables
Optional Variables
| Variable | Default | Description |
|---|---|---|
ntp_client_chrony_package | chrony | Package name to install |
ntp_client_driftfile | /var/lib/chrony/drift | Drift file location |
ntp_client_drift_seconds | 1 | Drift measurement interval |
ntp_client_drift_limit | 3 | Maximum drift before stepping |
ntp_client_port | 0 | NTP listen port (0 = client-only) |
ntp_client_log_path | /var/log/chrony | Log directory |
ntp_client_config_path | OS-specific | Config directory path |
ntp_client_config_file_name | chrony.conf | Config file name |
Variable Details
ntp_client_driftfile
File where Chrony stores clock drift information.
Default: /var/lib/chrony/drift
Purpose: Helps Chrony quickly adjust after restart by remembering previous drift rate.
ntp_client_drift_seconds and ntp_client_drift_limit
Controls the makestep directive for handling large time offsets.
Default:
ntp_client_drift_seconds: 1 # Step if offset > 1 second
ntp_client_drift_limit: 3 # Only for first 3 sync attempts
Meaning: If clock is off by more than 1 second, step (jump) the time immediately, but only during the first 3 synchronization attempts. After that, slew (gradually adjust) the time.
Why this matters:
- Stepping: Instant time change (can break running services)
- Slewing: Gradual adjustment (slower but safer)
- First 3 attempts: Allows quick correction on boot
- After 3 attempts: Prevents sudden jumps during operation
ntp_client_port
NTP listening port.
Default: 0 (client-only mode)
Values:
0: Client only (doesn’t serve time to others)123: Server mode (accepts requests from other clients)
This role configures client-only mode - doesn’t serve NTP to other systems.
ntp_client_config_path
OS-specific configuration directory.
Default:
ntp_client_config_path:
debian: /etc/chrony
redhat: /etc
Results:
- Debian/Ubuntu:
/etc/chrony/chrony.conf - RHEL/CentOS:
/etc/chrony.conf
Dependencies
This role has no dependencies on other Ansible roles, but requires:
- OPNsense firewall configured as NTP server
- Network connectivity to OPNsense on VLAN10
- OPNsense IP available in inventory (
hostvars['opnsense']['ip_vlan10'])
Example Playbook
Basic Usage
---
- name: Configure NTP Client
hosts: all
become: true
roles:
- ntp_client
Custom NTP Server
---
- name: Configure NTP with Custom Server
hosts: all
become: true
vars:
# Override template to use different server
# (requires modifying template)
roles:
- ntp_client
With Custom Drift Settings
---
- name: Configure NTP with Custom Drift
hosts: all
become: true
vars:
ntp_client_drift_seconds: 0.5 # Step if offset > 0.5 seconds
ntp_client_drift_limit: 5 # Allow stepping for first 5 syncs
roles:
- ntp_client
What This Role Does
-
Installs chrony package
- Package name:
chrony - Includes chronyd daemon and chronyc client tool
- Package name:
-
Deploys chrony.conf from template:
- Sets NTP server to OPNsense IP (VLAN10)
- Configures drift file location
- Enables RTC synchronization
- Sets makestep parameters
- Configures log directory
- Sets port to 0 (client-only)
-
Creates backup of existing config (if any)
-
Restarts chronyd service (via handler):
- Applies new configuration
- Ensures service is enabled (starts on boot)
Chrony Configuration
Generated Configuration File
Location:
- Debian:
/etc/chrony/chrony.conf - RedHat:
/etc/chrony.conf
Content:
# Use asus-opnsense as NTP server
server 192.168.x.x iburst
# Driftfile used to help synchronisation
driftfile /var/lib/chrony/drift
# RTC synchronisation
rtcsync
# Allow chrony to step up the time directly if it's offset by at least one second for the first 3 synchronisation attempts
makestep 1 3
# Where logs are stored
logdir /var/log/chrony
# Listen port : 0 for clients, 123 for servers
port 0
Configuration Directives Explained
server: NTP server to synchronize with
server 192.168.x.x iburst
iburst: Send burst of packets at startup for faster sync
driftfile: Store clock drift rate
driftfile /var/lib/chrony/drift
- Remembers how fast/slow hardware clock runs
- Helps maintain accuracy across reboots
rtcsync: Sync hardware clock every 11 minutes
rtcsync
- Keeps RTC (BIOS clock) synchronized
- Important for dual-boot systems
makestep: Handle large time offsets
makestep 1 3
- First parameter: Threshold in seconds (1)
- Second parameter: Number of updates to allow stepping (3)
- After 3 updates, only slew (gradual adjustment)
logdir: Log file location
logdir /var/log/chrony
- Stores measurements, statistics, tracking logs
port: Listening port
port 0
0: Don’t listen (client-only)123: Standard NTP port (server mode)
NTP Server Configuration
OPNsense as NTP Server
The role configures clients to sync from OPNsense firewall:
server {{ hostvars['opnsense']['ip_vlan10'] }} iburst
Why OPNsense?
- Central network device (always on)
- Already synced with upstream NTP servers
- Low latency (local network)
- Single source of truth for network time
- Reduces external NTP traffic
OPNsense NTP Configuration (manual setup required):
- Log into OPNsense
- Services → NTP (ntpd)
- Enable NTP server
- Configure upstream servers (e.g., pool.ntp.org)
- Allow NTP access from internal networks
Alternative: Public NTP Servers
To use public servers instead (requires template modification):
server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
server 2.pool.ntp.org iburst
server 3.pool.ntp.org iburst
Service Management
Chronyd Service
# Check status
systemctl status chronyd
# Start service
systemctl start chronyd
# Stop service
systemctl stop chronyd
# Restart service
systemctl restart chronyd
# Enable on boot
systemctl enable chronyd
# View logs
journalctl -u chronyd -f
Chrony Client Commands
# Check sync status
chronyc tracking
# Show time sources
chronyc sources
# Show detailed source stats
chronyc sourcestats
# Force sync (for testing)
chronyc makestep
# Check NTP activity
chronyc activity
Verifying Time Synchronization
Check Tracking Status
$ chronyc tracking
Reference ID : C0A80A01 (192.168.x.x)
Stratum : 3
Ref time (UTC) : Wed Jan 08 10:30:45 2026
System time : 0.000123456 seconds fast of NTP time
Last offset : +0.000098765 seconds
RMS offset : 0.000234567 seconds
Frequency : 5.432 ppm slow
Residual freq : +0.001 ppm
Skew : 0.123 ppm
Root delay : 0.001234567 seconds
Root dispersion : 0.000123456 seconds
Update interval : 64.5 seconds
Leap status : Normal
Key fields:
- Reference ID: NTP server IP (OPNsense)
- Stratum: Distance from reference clock (lower is better)
- System time: Offset from NTP time
- Last offset: Most recent time adjustment
- Frequency: Clock drift rate
Check Time Sources
$ chronyc sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* 192.168.x.x 2 6 377 34 +12us[ +25us] +/- 15ms
Symbols:
^: Server*: Currently selected source+: Acceptable source?: Connectivity issuesx: False ticker (large offset)
Fields:
- Reach: Reachability (377 = all recent polls successful)
- LastRx: Seconds since last response
- Last sample: Offset from source
Check System Time
# Show system time
date
# Compare with NTP time
chronyc tracking | grep "System time"
# Check hardware clock
hwclock --show
Clock Drift Explanation
What is Clock Drift?
Hardware clocks aren’t perfect - they run slightly fast or slow:
- Temperature changes affect crystal oscillators
- Aging components drift over time
- Typical drift: 1-50 ppm (parts per million)
- 10 ppm = 0.864 seconds per day
How Chrony Handles Drift
- Measures drift by comparing to NTP server
- Stores in drift file:
/var/lib/chrony/drift - Applies correction continuously
- Updates RTC every 11 minutes (rtcsync)
Example drift file:
5.432
Meaning: Clock runs 5.432 ppm slow (needs speeding up)
Makestep Behavior
Stepping vs Slewing
Stepping: Instant time change
- Pros: Fast correction
- Cons: Can break applications, logs, databases
- Used for: Large offsets at startup
Slewing: Gradual adjustment
- Pros: Safe for running applications
- Cons: Slow correction (max 0.5ms per second)
- Used for: Small offsets during normal operation
Role configuration:
makestep 1 3
- Step if offset > 1 second
- Only during first 3 sync attempts
- After that, always slew
When Does Stepping Occur?
- System boot: Clock likely wrong, step immediately
- After suspend: Time jumped, step on first sync
- Large manual change: If someone sets wrong time
After 3 successful syncs, Chrony assumes clock is roughly correct and only slews.
Firewall Configuration
OPNsense firewall needs to allow NTP:
# OPNsense firewall rule
Protocol: UDP
Source: Internal networks (VLAN10, VLAN12, etc.)
Destination: OPNsense (this firewall)
Port: 123
Action: Allow
Clients also need firewall rules (if firewall is active):
# Allow NTP client (outgoing to OPNsense)
firewall-cmd --permanent --add-service=ntp
firewall-cmd --reload
Security Considerations
- Client-Only Mode: Port 0 prevents serving time to others
- Single NTP Source: OPNsense only (reduces attack surface)
- No Authentication: Not configured (consider NTP authentication for high-security)
- Local Network: NTP traffic stays within network (not internet)
- Firewall Protection: OPNsense filters NTP traffic
Tags
This role does not define any tags. Use playbook-level tags if needed:
- hosts: all
roles:
- ntp_client
tags:
- ntp
- time
- chrony
- sync
Notes
- Role uses Chrony (not ntpd)
- Syncs from OPNsense firewall (not public NTP servers)
- Client-only mode (port 0)
- RTC synchronization enabled
- Makestep allows quick boot-time correction
- Configuration backed up before changes
- Service automatically enabled on boot
- Works on both RedHat and Debian-based systems
Troubleshooting
Time not synchronizing
Check chronyd status:
systemctl status chronyd
# If not running:
systemctl start chronyd
systemctl enable chronyd
Check sources:
chronyc sources
# Look for:
# ^* (currently synced)
# ^? (no response - connectivity issue)
Check connectivity to OPNsense:
# Ping OPNsense
ping 192.168.x.x
# Test NTP port
nc -u -v 192.168.x.x 123
“No suitable source found” error
Cause: Can’t reach NTP server
Solution:
# Check OPNsense IP
grep "^server" /etc/chrony/chrony.conf
# Verify OPNsense NTP service running
# (check on OPNsense: Services → NTP)
# Check firewall rules allow NTP
firewall-cmd --list-services | grep ntp
Time jumps after role execution
Expected behavior: If system time was wrong, role allows stepping during first 3 syncs.
Check offset:
chronyc tracking | grep "System time"
If large offset:
- First sync: May step immediately
- Subsequent syncs: Gradual adjustment
Clock drift too high
Check drift file:
cat /var/lib/chrony/drift
# Typical values: -50 to +50 ppm
# High values (>100): Hardware issue or incorrect NTP server
Solutions:
- Wait for drift measurement to stabilize (hours/days)
- Check hardware (temperature, failing RTC battery)
- Verify NTP server is accurate
Logs showing “No suitable source”
Check logs:
journalctl -u chronyd -n 50
# Look for:
# "No suitable source" - can't reach any servers
# "Selected source" - successfully synced
Solutions:
- Verify OPNsense IP correct in config
- Check network connectivity
- Verify OPNsense NTP service running
- Check firewall rules
Testing After Role Execution
Verify Chrony Installed
# Check package
rpm -q chrony # RedHat
dpkg -l chrony # Debian
# Check version
chronyd -v
Verify Configuration
# Check config file exists
ls -l /etc/chrony/chrony.conf # Debian
ls -l /etc/chrony.conf # RedHat
# View configuration
cat /etc/chrony/chrony.conf
# Should show OPNsense as server
Verify Service Running
# Check service active
systemctl is-active chronyd
# Should output: active
# Check service enabled
systemctl is-enabled chronyd
# Should output: enabled
Verify Time Synchronization
# Check tracking
chronyc tracking
# Should show:
# Reference ID: OPNsense IP
# System time: Small offset (< 1ms)
# Check sources
chronyc sources
# Should show:
# ^* 192.168.x.x (star means synced)
Verify RTC Sync
# Check system time
date
# Check hardware clock
hwclock --show
# Should be very close (within seconds)
Performance Considerations
- CPU Usage: Minimal (<0.1% CPU)
- Memory Usage: ~2-4 MB RAM
- Network Traffic: Very low (~1 KB every 64 seconds)
- Disk I/O: Minimal (drift file updates occasionally)
Best Practices
- Use local NTP server (OPNsense) not public servers
- Enable RTC sync (already configured by role)
- Allow stepping at boot (makestep directive)
- Monitor sync status regularly
- Check logs for synchronization issues
- Verify drift file updates over time
- Test after timezone changes
- Document NTP server (OPNsense in this case)
- Keep chronyd running (critical service)
- Verify accuracy periodically with external source
Why Time Synchronization Matters
Critical for:
- Logging: Correlate events across systems
- Authentication: Kerberos, LDAP time-sensitive
- SSL/TLS: Certificate validity checks
- Databases: Transaction ordering, replication
- Monitoring: Accurate metrics timestamps
- Compliance: Audit trails require accurate time
- Distributed Systems: Consensus, ordering
What Happens Without NTP?
- System clocks drift apart (seconds to minutes per day)
- Log timestamps inconsistent
- Authentication failures (time-based tokens)
- Certificate validation errors
- Monitoring alerts with wrong times
- Database replication issues
Related Roles
This role is often used with:
- System setup roles: Configure time before other services
- Logging roles: Accurate timestamps for log aggregation
- Monitoring roles: Precise metrics collection times
License
MIT
Author
Created for homelab infrastructure management.