Back to blog

Why SSH Feels Laggy on Long Links — and How Predictive Echo Fixes It

July 14, 2026 5 min read Kubexer Team
SSHLatencyTerminalPerformance

Open an SSH session to a server on another continent and start typing. The characters do not appear as you press them — they appear a beat later, in a small ragged burst. Hold down a key and the repeats arrive in clumps. Delete a word and the backspaces crawl. Nothing is broken, the connection is not dropping packets, and yet typing feels like wading.

This is not a bug in your terminal or your network. It is the direct, unavoidable consequence of how a remote shell works — and it is fixable, but not by making the network faster.

Why remote typing is slow

In a local shell, the terminal draws the character you typed immediately. In an SSH session it does not, and it must not. The character you press is sent to the server; the server decides what to display and sends it back; only then does your terminal draw it.

That indirection is not gratuitous. The remote program is entitled to decide what your keystroke means:

  • A password prompt echoes nothing at all.
  • A shell with tab completion may echo an entire filename in response to one keypress.
  • An editor in a modal state may treat d as a command rather than a letter.
  • A pager may echo nothing and scroll instead.

The terminal cannot know which of these applies. So it waits. Every visible character costs one full network round-trip, and the delay you feel is your round-trip time — 5 ms on the same LAN, 60 ms across a country, 200 ms or more to a distant region or through a congested VPN. At 200 ms, typing a forty-character command means eight seconds of accumulated waiting spread across your fingers.

Nagle's algorithm makes it worse

There is a second, less obvious contributor. TCP by default applies Nagle's algorithm, which deliberately delays sending small packets so it can coalesce them into fuller ones. It is a sensible optimisation for bulk transfer and precisely the wrong behaviour for interactive typing, where every packet is one keystroke and latency matters far more than efficiency. Kubexer Terminal sets TCP_NODELAY on the SSH socket to disable it, so keystrokes leave immediately instead of being held back for a few tens of milliseconds.

That is worth doing, but it only removes the artificial delay. The round-trip itself remains.

Predictive local echo

The technique that actually solves this was popularised by mosh and later adopted by iTerm2: predict what the server is going to echo, draw it immediately, and correct if you were wrong.

When you type a plain printable character into what appears to be an ordinary shell prompt, the overwhelmingly likely outcome is that the server echoes exactly that character back. So the terminal draws it right away — marked internally as a prediction — and when the real response arrives it reconciles. If the server echoed what was predicted, the prediction is confirmed and nothing visibly changes. If it echoed something else, the prediction is rolled back and the authoritative output is drawn instead.

The result is that typing feels instantaneous, because in the common case it is instantaneous, while correctness is preserved because the server always has the final say.

When it engages, and when it stays out of the way

A predictor that is always on would be a liability. Kubexer Terminal's implementation is conservative about when it acts:

  • Latency-gated. It measures the actual echo round-trip and keeps a smoothed estimate (an exponential moving average). Predictions engage only once measured latency exceeds 25 ms. On a local shell or a nearby host, nothing changes — there is no delay to hide, so there is no reason to take the risk.
  • Suppressed where echo is not literal. If the server is not echoing input — a password prompt is the obvious case — predicting would draw characters that should never be visible. The predictor detects this and stops. It also stands down in the alternate screen buffer, which is what full-screen applications like editors and pagers use, because there a keystroke is a command rather than a character.
  • Self-correcting with backoff. A wrong prediction is rolled back. Repeated wrong predictions mean the model of the session is unreliable, so the predictor disables itself and re-arms only after a cooling-off period — currently around eight seconds. It measures latency continuously even while disabled, so it can re-engage when conditions warrant.
  • Bounded by a staleness floor. A prediction that is never confirmed is discarded after a timeout that scales with measured latency and never drops below a safety floor, so a slow host cannot leave phantom characters on screen.

What it does not do

Predictive echo is a rendering optimisation, and it is worth being precise about its limits:

  • It does not make commands execute faster. Only the display of your own typing is accelerated; output still arrives when the server sends it.
  • It does not reduce round-trips or change what is sent over the wire.
  • It is not a substitute for a session-resumption protocol. Unlike mosh, it does not survive a network change or a suspended laptop — it makes an established SSH session feel better, not survive disconnection.

What it does do is remove the single most constant irritation of working on remote machines. On a 5 ms link you will never notice it, because it never turns on. On a 250 ms link to a machine in another region, it is the difference between typing and waiting.

Related reading

Getting connected quickly matters as much as typing quickly once you are there — see host key verification and TOFU for how Kubexer Terminal handles connection trust. Kubexer Terminal is available for macOS, Windows, and Linux.