All articles
Infrastructure·April 27, 2026·6 min read

PTP vs NTP: Time Sync for Solana HFT Bot Servers

Why PTP time sync beats NTP for trading bot servers: sub-microsecond clock discipline for honest latency attribution across colocated Solana HFT fleets.

Two servers in the same rack, both running your Solana arb bot, report Jito bundle land times that differ by 40 microseconds — and you have no idea whether that gap is real network jitter or just two clocks disagreeing. That ambiguity is the entire problem. When your latency budget for reaching the current leader is measured in tens of microseconds, a clock that drifts by more than that turns every timing measurement into noise. NTP will not save you here. PTP might.

What NTP actually gives you, and why it isn't enough

NTP (chrony or ntpd) disciplines your clock over UDP against a remote or local stratum-1 source. On a decent LAN with a nearby GPS-backed server, chrony holds you to roughly 100 microseconds to low single-digit milliseconds of offset. Over the open internet it's worse — 1 to 10 ms is normal, and asymmetric routing quietly biases the offset because NTP assumes the request and reply paths take equal time.

For most infrastructure that's fine. For a colocated Solana bot fleet it is not, for one specific reason: slot timing and latency attribution are cross-server problems. Solana produces a slot every ~400 ms, and the leader schedule rotates every 4 slots (~1.6 s per leader). Your feed handler, your strategy box, and your TPU forwarder may all live on different machines. When you log "saw the account update at T1, sent the transaction at T2, it landed in slot N," those timestamps are only comparable if the clocks agree to well under the differences you care about. A 500 microsecond NTP offset between two boxes makes a 200 microsecond internal latency measurement meaningless — the sign can even flip.

Chrony can do better than its internet reputation suggests when you feed it a hardware clock and keep the poll interval tight. But it fundamentally rides the same software/UDP timestamping path, and that path has jitter from kernel scheduling, interrupt coalescing, and NIC queueing that no amount of tuning fully removes.

Where PTP wins: hardware timestamps

PTP (IEEE 1588, run via linuxptp's ptp4l) exists to close that gap. The trick is hardware timestamping: a PTP-capable NIC stamps the packet with its own clock (the PHC, PTP Hardware Clock, exposed as /dev/ptp0) at the moment it hits the wire, not when the kernel gets around to it. That removes almost all of the software-stack jitter from the measurement.

Run PTP over a network where every switch is a boundary or transparent clock, feed the grandmaster from GPS/PPS, and sub-microsecond offset across the fleet is routine. Even with one or two non-PTP-aware switches in the path you'll typically hold single-digit microseconds — an order of magnitude better than good NTP, and the residual error is dominated by path asymmetry you can characterize and correct rather than random kernel jitter.

The mechanism that makes this work is the same two-part discipline you eventually need anyway:

  • ptp4l synchronizes the NIC's PHC to the grandmaster over the wire.
  • phc2sys copies the disciplined PHC into the system clock (CLOCK_REALTIME) so your application's clock_gettime calls actually benefit.

Skip phc2sys and you've synced a clock nothing reads. This is the single most common misconfiguration I see.

A minimal working setup

Assume eth0 is a PTP-capable NIC (Intel X710, E810, most Mellanox ConnectX-5+). Confirm hardware timestamping first:

ethtool -T eth0 | grep -A3 "Hardware Transmit"
# You want SOF_TIMESTAMPING_TX_HARDWARE and RX_HARDWARE listed.
# If you only see SOFTWARE flags, this NIC won't give you the win.

Client config (/etc/ptp4l.conf), running as a PTP slave in a boundary-clock domain:

[global]
domainNumber        24
tx_timestamp_timeout 50
logSyncInterval     -3      # 8 syncs/sec
delay_mechanism     E2E
network_transport   UDPv4
[eth0]

Then wire both daemons together:

ptp4l  -f /etc/ptp4l.conf -i eth0 --step_threshold=1 &
phc2sys -s eth0 -c CLOCK_REALTIME -w -O 0 &

The -O 0 matters: PTP runs on TAI, which is currently 37 seconds ahead of UTC. phc2sys handles the offset, but if you get it wrong every timestamp is off by exactly 37 s and you'll waste an afternoon. Watch the offset converge:

# ptp4l master offset in nanoseconds — should settle inside ±1000
journalctl -u ptp4l -f | grep "master offset"

Once it's steady, log phc2sys offset to your metrics stack. If it wanders past a few microseconds during trading hours, treat that like any other alert — it directly corrupts your latency attribution. Building that kind of clock-health monitoring into a fleet is exactly the sort of thing worth handing to a proper infrastructure and market-data setup rather than bolting on later.

Why this changes how you read your own numbers

Here's the concrete payoff. With disciplined PHCs across the fleet you can do honest latency decomposition: the wire time between your feed box and your execution box becomes a real, signed number instead of a guess. You can finally separate "the market data was slow to arrive" from "my strategy was slow to react" from "the transaction was slow to reach the leader." Before PTP those three are entangled inside your clock error.

It also sharpens the picture when you're deciding how to submit. Whether you're doing dual-path submission across sendTransaction and Jito bundles or pushing packets straight at the leader with a direct TPU/QUIC forwarder, the only way to know which path actually landed first — across two different sending servers — is a shared, trustworthy clock. Same story for evaluating whether your staked-connection RPC path is buying you the priority you paid for: the measurement is cross-server, so the clock has to be right. Feeding those clean per-slot timings into a real-time execution dashboard is where the work pays off, because now the microsecond columns mean something.

Gotchas that will bite you

  • Virtualization eats hardware timestamps. In most cloud VMs the PHC isn't passed through, so ptp4l silently falls back to software timestamping and you're back to NTP-class accuracy. This is why HFT clock work lives on bare metal in a colo.
  • NIC bonding breaks the PHC binding. ptp4l disciplines one physical PHC. LACP bonds and active/passive failover confuse which clock is authoritative. Pin PTP to a single physical interface.
  • A software switch in the path adds jitter you can't see. One dumb, non-PTP switch between you and the grandmaster injects queueing delay that PTP can't compensate for without transparent-clock support. Know your switch fabric.
  • CPU frequency scaling and deep C-states add scheduling latency that shows up as phc2sys jitter. Pin the daemons, disable C-states on the timing cores.
  • TAI vs UTC, again — the most common 37-second faceplant in the book.

None of this is exotic once it's running, but a fleet-wide time layer is a standing thing that drifts, alerts, and occasionally needs a NIC firmware bump — the kind of quiet dependency worth keeping under an ongoing infrastructure support arrangement so a silent phc2sys failure doesn't quietly poison a week of latency data.

If you're building or colocating a Solana market-making stack where cross-server slot timing decides whether your fills are real, getting the clock layer right first is the cheapest edge you'll buy all year.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#PTP#time-sync#Solana HFT#low-latency#colocation