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.

By Rowan Hale · Published

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.1 over IPv4 and 2606:4700:4700::1111 over IPv6;
  • Quad9's, 9.9.9.9 over IPv4 and 2620:fe::fe over 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.

Table 1. Where in the minute the round trip changed most, from a wired computer on a home Starlink connection to four public DNS resolvers, in two one-hour sittings, October 2026. Each figure gives the value at the four seconds, then the value at other seconds. A reading with no pattern shows no seconds and no figures: by the test's rule, they aren't a finding.
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.

Table 2. Spread of the round trip around each target's own median, in whole ms, for each sitting and target. October 2026.
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.

The path the pings took, and the part both routes share From a wired computer on the home network, five echo requests a second to each of four targets, for one hour, through the home network and router, the satellite link and Starlink's network, up to where the two routes part: the part of the path the two share. Then each route crosses its operator's network to its nearest site: Cloudflare's, 1.1.1.1 and 2606:4700:4700::1111; Quad9's, 9.9.9.9 and 2620:fe::fe. computer a wired computer on the home network: five echo requests a second to each of four targets, for one hour the part of the path the two share router the home network and router satellite the satellite link Starlink Starlink's network, up to where the two routes part Cloudflare its nearest site: 1.1.1.1, 2606:4700:4700::1111 Quad9 its nearest site: 9.9.9.9, 2620:fe::fe The path the pings took, and the part both routes share From a wired computer on the home network, five echo requests a second to each of four targets, for one hour, through the home network and router, the satellite link and Starlink's network, up to where the two routes part: the part of the path the two share. Then each route crosses its operator's network to its nearest site: Cloudflare's, 1.1.1.1 and 2606:4700:4700::1111; Quad9's, 9.9.9.9 and 2620:fe::fe. computer a wired computer on the home network: five echo requests a second to each of four targets, for one hour the part of the path the two share router the home network and router satellite the satellite link Starlink Starlink's network, up to where the two routes part Cloudflare its nearest site: 1.1.1.1, 2606:4700:4700::1111 Quad9 its nearest site: 9.9.9.9, 2620:fe::fe
From a wired computer on the home network to each operator's nearest site, October 2026. 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 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.

  1. 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 yes when it is:

    scrolls sideways
    timedatectl show -p NTPSynchronized --value
  2. Then 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 &
    wait

    Each ping line 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, and wait returns once all four have stopped. What the options do, from ping(8) (iputils 20250605, checked October 2026):

    • -D prints a timestamp before each line, "unix time + microseconds as in gettimeofday".
    • -O reports 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.
    • -n gives "numeric output only", with no name lookups.
    • -i 0.2 sends a ping every 0.2 seconds. "Only super-user may set interval to values less than 2 ms", so this needs no root.
    • -w 3600 stops ping after 3,600 seconds, one hour.
  3. 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 columns second, sent, lost and change_ms, then a last line with the four seconds that had the largest changes.

    The script, latency_fold.py
    scrolls 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]))
  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.txt

    On 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.

  5. 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.py doesn't print those two figures. Its change_ms column 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.

Unanswered, of pings sent at 12, 27, 42 or 57 s past the minute, all four targets, both sittings
109 of 9,567Table 3 ·
Unanswered, of pings sent at other seconds, all four targets, both sittings
792 of 133,895Table 3 ·

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.

Table 3. Pings sent and lost over both sittings together, by the second past the minute in which each was sent, from a wired computer on a home Starlink connection. October 2026.
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.

See the homelab verdict from all five tests.

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

  1. Making Sense of Constellations: Methodologies for Understanding Starlink's Scheduling Algorithms, Tanveer, Puchol, Singh, Bianchi and Nithyanand, arXiv preprint 2307.00402 v1, accessed
  2. A Multifaceted Look at Starlink Performance, Mohan, Ferguson, Cech, Bose, Renatin, Marina and Ott, ACM Web Conference 2024 (arXiv 2310.09242 v2), accessed
  3. A Multifaceted Look at Starlink Performance, slides, IRTF Open, IETF 123, accessed
  4. Improving Starlink's Latency, Starlink, accessed
  5. ping(8), iputils 20250605, man7.org, accessed
  6. Does Starlink have a referral program?, Starlink, accessed
  7. Referral Program Terms (US), section 4.1, Starlink, accessed

Retest log

  1. 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.