Deploy 2 internal NTP servers
Point the switches and PLCs to internal stratum 2 servers. 2 servers deliver redundancy when 1 becomes inoperable.
Accurate DNS records depend on accurate time. When a device’s clock drifts, log timestamps become unreliable and certificate validation is unsuccessful. NTP solves this.
NTP (Network Time Protocol) synchronizes clocks across the network using a stratum hierarchy. Stratum 0 is a reference clock (GPS receiver, atomic clock). Stratum 1 is a server directly connected to a stratum 0 source. Each additional hop adds 1 stratum level.
Stratum 16 means the server is unsynchronized and its time is unreliable. NTP clients reject stratum 16 sources. In a plant network, deploy at least 2 stratum 2 servers (synchronized to public stratum 1 servers or a local GPS receiver) and point the switches and PLCs to those internal servers.
NTP achieves millisecond-level accuracy over a WAN and sub-millisecond on a LAN. For most OT applications (logging, certificate validation, SCADA timestamps), NTP is sufficient.
PTP (Precision Time Protocol, IEEE 1588) achieves sub-microsecond accuracy by using hardware timestamping in network interface cards and switches. PROFINET IRT requires PTP because its cycle times (250 microseconds to 1 millisecond) demand clock synchronization tighter than NTP delivers. IEC 61850 Sampled Values for power grid protection also requires PTP.
| Feature | NTP | PTP (IEEE 1588) |
|---|---|---|
| Accuracy | 1 to 10 ms (LAN) | < 1 microsecond |
| Hardware support | None required | Requires PTP-capable NICs and switches |
| Use case | Logging, certificates, SCADA | PROFINET IRT, IEC 61850, motion control |
| Standard | RFC 5905 | IEEE 1588-2019 |
The following script queries an NTP server and reports the offset between the local clock and the server. The script helps identify devices with clock drift.
import ntplibfrom datetime import datetime, timezone
def check_ntp(server: str) -> dict: client = ntplib.NTPClient() response = client.request(server, version=3) return { "server": server, "stratum": response.stratum, "offset_ms": response.offset * 1000, "delay_ms": response.delay * 1000, "ref_time": datetime.fromtimestamp(response.ref_time, tz=timezone.utc), }
for server in ["192.168.1.1", "pool.ntp.org"]: try: stats = check_ntp(server) print(f"Server: {stats['server']} Stratum: {stats['stratum']} " f"Offset: {stats['offset_ms']:.3f} ms " f"Delay: {stats['delay_ms']:.3f} ms") if abs(stats["offset_ms"]) > 100: print(f" WARNING: offset > 100 ms, check NTP configuration") except ntplib.NTPException as e: print(f"Server: {server} ERROR: {e}")An offset greater than 100 ms indicates a configuration issue. An offset greater than 1 second causes syslog timestamps to be unreliable for incident correlation.
Deploy 2 internal NTP servers
Point the switches and PLCs to internal stratum 2 servers. 2 servers deliver redundancy when 1 becomes inoperable.
NTP for logging, PTP for real-time
NTP delivers millisecond accuracy for logs and certificates. PTP delivers sub-microsecond accuracy for PROFINET IRT and IEC 61850.
Stratum 16 means unsynchronized
NTP clients reject stratum 16 sources. When a device reports stratum 16, its time is unreliable.
With addresses assigned, names resolved, and clocks synchronized, the remaining question is how to monitor the health of the network. The next page covers SNMP, the protocol that polls device statistics and receives event notifications.