Starlink ping spikes: latency second by second from home
In one of two one-hour sittings, round trips over IPv4 changed most, and spiked, at 12, 27, 42 and 57 seconds past the minute; in the other sitting, and over IPv6, the test's rule found no such pattern. Round trips were read in whole milliseconds, so a step of a millisecond or two, or one close to their usual variation, can go unseen, and the test covers one connection, behind its home router, in two hours.
Promo: $100 off Starlink with my referral link
If you've never been a Starlink customer and you order an eligible Residential or Roam plan through my link, we both get Starlink's reward for your country. In the US, that is a $100 credit on your second monthly bill, and I get $100 too. Use my referral link
In one sitting, round trips over IPv4 changed most at 12, 27, 42 and 57 seconds past the minute
The test ran in October 2026 from a wired computer on the home network, on the same connection page 1 traced. Every route to the targets left through the wired interface, so no tunnel, exit node or Wi-Fi carried the pings. The pings came from iputils ping: five echo requests a second to each of four targets for one hour, one process per target, all four side by side. That hour ran twice, in two sittings on two days in October 2026. The computer's clock was synchronized before and after each sitting, so its seconds past the minute match real time.
The targets were two public DNS resolvers in each address family:
- Cloudflare's,
1.1.1.1over IPv4 and2606:4700:4700::1111over IPv6; - Quad9's,
9.9.9.9over IPv4 and2620:fe::feover IPv6.
One target in one sitting is one reading, eight in all.
A ping's round-trip time (RTT) is how long its reply takes to come back. Each reply counts at the second past the minute in which its request was sent. For every minute, the change at a second is the median round trip of that second's replies minus the median of the second before, where the median is the middle value. A second's figure is the median size of those changes over the hour, and the four seconds with the largest figures come from the data: no second was assumed in advance.
The rules for each verdict were fixed before the first sitting:
- A pattern needs the four seconds with the largest changes to sit 15 seconds apart, and the median change at those seconds to be at least twice the median change between other neighboring seconds. Four seconds out of 60 fall 15 seconds apart by chance in 15 of 487,635 cases.
- A spike needs p99 minus the median at those seconds to be at least twice what it is at other seconds, or 5 or more pings lost there, at least twice the loss rate elsewhere. p99 is the round trip that 99 in 100 replies came in under.
In the second sitting, both IPv4 resolvers met both rules at the same four seconds: 12, 27, 42 and 57 past the minute, 15 seconds apart. The round trip changed most there, and it spiked there: p99 minus the median was more than twice what it was at other seconds, +46 against +21 ms to Cloudflare and +50 against +22 ms to Quad9. In the first sitting, for all four targets, and over IPv6 in the second, the test's rule found no pattern.
| Sitting | Target | Pattern | Four seconds past the minute | Median change, ms (there; elsewhere) | p90 minus the median, ms (there; elsewhere) | p99 minus the median, ms (there; elsewhere) | Spike |
|---|---|---|---|---|---|---|---|
| First | Cloudflare, IPv4 | no pattern found | |||||
| First | Quad9, IPv4 | no pattern found | |||||
| First | Cloudflare, IPv6 | no pattern found | |||||
| First | Quad9, IPv6 | no pattern found | |||||
| Second | Cloudflare, IPv4 | yes | 12, 27, 42, 57 | 4; 2 | +13; +9 | +46; +21 | yes |
| Second | Quad9, IPv4 | yes | 12, 27, 42, 57 | 3; 2 | +13; +8 | +50; +22 | yes |
| Second | Cloudflare, IPv6 | no pattern found | |||||
| Second | Quad9, IPv6 | no pattern found |
Every figure in Table 1 is in whole ms. The rule compared the median changes before they were rounded, which is how Quad9's 3 and 2 ms meet "at least twice".
Limitation
Round trips were read in whole milliseconds. A step only a little larger than the variation within a second can then fall short of the rule, so a reading with no pattern may hold steps the rule missed, or no steps at those seconds, and the test can't tell which.
A ping is ICMP, and routers can treat ICMP differently from other traffic. The household's other traffic was on the line in both sittings. All of this is one connection, wired, behind its home router, in two hours.
Table 2 gives the spread for every reading: how far the replies came from that target's own median for the sitting. pN is the round trip that N in 100 replies came in under, so p10 minus the median is negative. In every reading, p99 was 22 to 28 ms above the median, and the slowest single reply 144 to 754 ms above it.
| Sitting | Target | p10 | p90 | p99 | p99.9 | Slowest reply |
|---|---|---|---|---|---|---|
| First | Cloudflare, IPv4 | −5 | +9 | +26 | +62 | +403 |
| First | Quad9, IPv4 | −4 | +9 | +26 | +123 | +547 |
| First | Cloudflare, IPv6 | −5 | +9 | +26 | +80 | +402 |
| First | Quad9, IPv6 | −5 | +9 | +28 | +155 | +754 |
| Second | Cloudflare, IPv4 | −5 | +9 | +22 | +53 | +178 |
| Second | Quad9, IPv4 | −4 | +9 | +24 | +61 | +192 |
| Second | Cloudflare, IPv6 | −5 | +9 | +23 | +54 | +144 |
| Second | Quad9, IPv6 | −5 | +9 | +25 | +91 | +431 |
The resolvers answer from their operators' nearest sites, so each round trip also crosses that operator's network. Where both IPv4 resolvers changed at the same seconds, in the second sitting, the changes sit on the part of the path the two share: the home network and router, the satellite link and Starlink's network up to where the two routes part. The test can't separate those parts, and it doesn't show what caused the changes.
Researchers report changes every 15 seconds, at 12, 27, 42 and 57 seconds past the minute
Tanveer et al. measured from four Starlink dishes, one in Western Europe and three in the US (Northeast, Midwest and Northwest), each with a computer connected by Ethernet, to servers at the Starlink points of presence (PoPs) that served those dishes, at millisecond granularity. They report major changes in latency "at the 12th, 27th, 42nd, and 57th second past every minute", seen "from all our measured locations for all periods of time" (arXiv preprint, v1, July 2023, section 3, checked October 2026). They put the changes down to "a global network controller" that gives each dish its satellite, and write that "these allocations are made every 15-seconds, globally" (abstract).
Mohan et al., published at the ACM Web Conference 2024, measured from dishes in Edinburgh and Munich. They found that Starlink "performs network reconfigurations every 15 s", and that the reconfigurations are "globally synchronized and likely independent of satellite handovers" (section 1). Round trips were "comparatively stable within each Starlink reconfiguration interval but potentially changing between intervals", and they still changed between intervals in stretches when the UK dish could be served by only one satellite. Their takeaway: "Hand-offs between satellites are not the cause of these effects." (section 6.2; arXiv v2, February 2024, checked October 2026). The same team presented the work at IRTF Open, IETF 123, in July 2025, with "15-secs reconfigurations are prevalent at all locations" and "15-secs intervals are globally synchronized" on its slides (checked October 2026).
In the second sitting here, over IPv4, both resolvers' changes fell at 12, 27, 42 and 57 seconds past the minute, the seconds Tanveer et al. reported. In the first sitting, and over IPv6 in the second, the test's rule found no pattern. The explanation for the 15-second changes is the researchers', not this page's.
Starlink's own latency figures are built from 15-second averages. Its document Improving Starlink's Latency says Starlink collects measurements from its routers every 15 seconds, and that these "15 second average latencies" give its median and worst-case figures. The PDF has no date in its text; the file was created in March 2024 (checked October 2026). A spike shorter than 15 seconds sits inside one of those averages.
You can run the same test from a computer on your network
The test needs a Linux computer with iputils ping and Python 3, wired to your router.
Check that its clock is synchronized before you start and again when the hour ends, as the test did. On a system with systemd, this prints
yeswhen it is:scrolls sideways timedatectl show -p NTPSynchronized --valueThen start the four pings together, each writing to its own file:
scrolls sideways ping -D -O -n -i 0.2 -w 3600 1.1.1.1 > cloudflare-ipv4.txt & ping -D -O -n -i 0.2 -w 3600 9.9.9.9 > quad9-ipv4.txt & ping -D -O -n -i 0.2 -w 3600 2606:4700:4700::1111 > cloudflare-ipv6.txt & ping -D -O -n -i 0.2 -w 3600 2620:fe::fe > quad9-ipv6.txt & waitEach
pingline is the command the test ran for one target, with only the redirect to a file and the&added.&starts each one in the background so the four run side by side, andwaitreturns once all four have stopped. What the options do, from ping(8) (iputils 20250605, checked October 2026):-Dprints a timestamp before each line, "unix time + microseconds as in gettimeofday".-Oreports a request still waiting for its reply before the next one goes out, so a lost ping leaves a line too. Without it, the loss counts come out as zero.-ngives "numeric output only", with no name lookups.-i 0.2sends a ping every 0.2 seconds. "Only super-user may set interval to values less than 2 ms", so this needs no root.-w 3600stops ping after 3,600 seconds, one hour.
Save this script as
latency_fold.py. It uses only Python 3's standard library and prints no round trip. For each second past the minute, it gives the pings sent, the pings lost and the median change from the second before, to 0.1 ms, in the columnssecond,sent,lostandchange_ms, then a last line with the four seconds that had the largest changes.The script,
latency_fold.pyscrolls sideways #!/usr/bin/env python3 """Fold the output of `ping -D -O -n -i 0.2 <target>` by second past the minute. For each second past the minute, by the time each request was sent on this computer's clock, it prints how many requests were sent, how many never got a reply, and the median size of the change in the round trip from the second before, in ms (round trips read in whole ms). Usage: python3 latency_fold.py < ping.txt """ import re import statistics import sys from collections import defaultdict INTERVAL_US = 200_000 # ping -i 0.2 sent_at, rtt = {}, {} # request number -> send time, round trip (microseconds) for line in sys.stdin: m = re.match(r"\[(\d+)\.(\d{6})\] .* icmp_seq=(\d+) .*time=([\d.]+) ms$", line) if m: # a reply: it was sent one round trip before it was printed seq, us = int(m[3]), int(float(m[4]) + 0.5) * 1000 # in whole ms, as ping prints 100 ms or more rtt[seq], sent_at[seq] = us, int(m[1]) * 10**6 + int(m[2]) - us m = re.match(r"\[(\d+)\.(\d{6})\] no answer yet for icmp_seq=(\d+)", line) if m: # printed as the next request goes out, one interval later sent_at.setdefault(int(m[3]), int(m[1]) * 10**6 + int(m[2]) - INTERVAL_US) by_second = defaultdict(list) # whole second of sending -> round trips (None: no reply) for seq, t in sent_at.items(): by_second[t // 10**6].append(rtt.get(seq)) sent, lost, changes = [0] * 60, [0] * 60, defaultdict(list) for t, trips in by_second.items(): sent[t % 60] += len(trips) lost[t % 60] += trips.count(None) now = [x for x in trips if x is not None] before = [x for x in by_second.get(t - 1, []) if x is not None] if len(now) >= 3 and len(before) >= 3: changes[t % 60].append(abs(statistics.median(now) - statistics.median(before))) change = {s: statistics.median(c) for s, c in changes.items()} print("second sent lost change_ms") for s in range(60): print(f"{s:6} {sent[s]:4} {lost[s]:4} " + (f"{change[s] / 1000:9.1f}" if s in change else " -")) print("largest changes at seconds:", *sorted(sorted(change, key=lambda s: (-change[s], s))[:4]))Once the pings have stopped, run it on each file:
scrolls sideways python3 latency_fold.py < cloudflare-ipv4.txt python3 latency_fold.py < quad9-ipv4.txt python3 latency_fold.py < cloudflare-ipv6.txt python3 latency_fold.py < quad9-ipv6.txtOn the test's own ping output, in both sittings, this script gave the same per-second rows and the same four seconds as the test's own script, for every target.
The last line always names four seconds. If they don't sit 15 seconds apart, the hour showed no pattern by this page's rule, and the four seconds mean nothing on their own. If they do, the rule's other half compares the median of every minute's change at those four seconds with the same median at the other seconds, and
latency_fold.pydoesn't print those two figures. Itschange_mscolumn gives each second's own median instead, so the four seconds' figures against the middle of the other 56 are a close check, not the rule itself, and when they come out near twice the rest, the check can fall on either side of it.
Note
Run the full hour. In neither sitting did the first 10, 20 or 30 minutes show the pattern with the whole hour's seconds, and a full hour here found it in two of the eight readings, so your hour may not show it either.
These are Linux's iputils commands; other systems' ping takes other options, and this page doesn't give them. Over Wi-Fi, the radio link inside your home is part of every round trip as well, and this test didn't cover it.
901 of 143,462 pings went unanswered, more often at 12, 27, 42 and 57 seconds past the minute
Over both sittings and all four targets, 901 of 143,462 pings went unanswered: 417 of 71,733 over IPv4 and 484 of 71,729 over IPv6. Of the 901, 109 were sent at 12, 27, 42 or 57 seconds past the minute, out of 9,567 sent at those seconds. That is 1.9 times the loss rate at other seconds, where 792 of 133,895 went unanswered: 2.1 times over IPv4 and 1.8 times over IPv6. Most lost pings weren't sent at those seconds, though: 109 of the 901, about 1 in 8, were.
These counts use the same four seconds for every target in both sittings, whatever each reading's own verdict was, so they don't rest on a pattern being found.
| Target or family | Pings sent | Lost | Sent at 12, 27, 42 or 57 seconds | Lost there | Sent at other seconds | Lost there |
|---|---|---|---|---|---|---|
| Cloudflare, IPv4 | 35,865 | 221 | 2,390 | 30 | 33,475 | 191 |
| Quad9, IPv4 | 35,868 | 196 | 2,395 | 25 | 33,473 | 171 |
| Cloudflare, IPv6 | 35,864 | 237 | 2,394 | 27 | 33,470 | 210 |
| Quad9, IPv6 | 35,865 | 247 | 2,388 | 27 | 33,477 | 220 |
| IPv4, both targets | 71,733 | 417 | 4,785 | 55 | 66,948 | 362 |
| IPv6, both targets | 71,729 | 484 | 4,782 | 54 | 66,947 | 430 |
| All four | 143,462 | 901 | 9,567 | 109 | 133,895 | 792 |
A ping with no reply may have been lost on the way out, on the way back or at the target. Neither operator's resolver lost several times as many pings as the other's in the same family, so nothing points to a resolver limiting ICMP.
Not tested here
- Games, calls and streams. No application was tested, so this page gives no verdict on any of them.
- Fixes. No setting, product or second connection was tried.
- Wi-Fi and other connections. Both sittings ran over a wired link, on one home connection.
- The cause on this line. The explanation in the research section is the researchers'. This test can't say why the pattern showed in the second sitting and not the first, or over IPv4 and not IPv6, or whether another hour would show it.
- The dish's own statistics, obstruction and weather.
- Round-trip times themselves. Every millisecond figure on this page is a difference, because a round trip to a public resolver's nearest site says where that site is. Starlink publishes figures of its own, and its document is linked in the research section. Starlink's label figure for typical latency is set beside the minimum round trip through a geostationary satellite on the satellite internet comparison.
- Dropouts over days. Two hours of pings can't show how often a connection drops over a week.
Promo: $100 off Starlink with my referral link
If you've never been a Starlink customer and you order an eligible Residential or Roam plan through my link, we both get Starlink's reward for your country. In the US, that is a $100 credit on your second monthly bill, and I get $100 too. Use my referral link
Sources
- Making Sense of Constellations: Methodologies for Understanding Starlink's Scheduling Algorithms, Tanveer, Puchol, Singh, Bianchi and Nithyanand, arXiv preprint 2307.00402 v1, accessed
- A Multifaceted Look at Starlink Performance, Mohan, Ferguson, Cech, Bose, Renatin, Marina and Ott, ACM Web Conference 2024 (arXiv 2310.09242 v2), accessed
- A Multifaceted Look at Starlink Performance, slides, IRTF Open, IETF 123, accessed
- Improving Starlink's Latency, Starlink, accessed
- ping(8), iputils 20250605, man7.org, accessed
- Does Starlink have a referral program?, Starlink, accessed
- Referral Program Terms (US), section 4.1, Starlink, accessed
Retest log
- Test Two one-hour sittings of pings five times a second from a wired computer on the home network to two public DNS resolvers per address family.
Related guides
- Can you host a server on Starlink without a public IPv4 address?
Not directly over IPv4: without a public IPv4 address, connections from the internet arrive at your provider's NAT, and a port forward on your own router doesn't reach it.
- Starlink MTU size: path MTU measured at 1500 bytes
The path MTU from a home Starlink connection was 1500 bytes over both IPv4 and IPv6, on every path tested.
- Is Starlink good for a homelab? A verdict from five tests
On the one home Starlink connection tested, packets of 1500 bytes crossed over IPv4 and IPv6 in October 2026, and round trips over IPv4 spiked at four set seconds each minute in one of two one-hour tests, with 901 of 143,462 pings lost over both; the ways around carrier-grade NAT (CGNAT), such as a tunnel that starts inside your network, aren't tested here.