SSH Broken Pipe — How to Fix It

Last updated May 24, 2026

TL;DR

Broken pipe (or client_loop: send disconnect: Broken pipe) means the SSH connection died — usually because something in the middle silently dropped it while you weren't typing.

Three causes cover almost every case:

  • A NAT or stateful firewall dropped your idle TCP connection (default timeouts are 5–15 min)
  • Your Wi-Fi reconnected / VPN flipped, killing the underlying socket
  • The server-side sshd decided your session timed out

Fix it with ServerAliveInterval 60 in ~/.ssh/config — your client sends a tiny keepalive once a minute, the connection stays "active" from every firewall's perspective, problem gone.

What you're seeing

$ ssh [email protected]
... (you're working, you walk away for lunch, you come back) ...
client_loop: send disconnect: Broken pipe
$

Variants:

  • packet_write_wait: Connection to X port 22: Broken pipe
  • Write failed: Broken pipe
  • Connection to X closed by remote host. (related — usually a server-side kill rather than NAT drop)

All point at the same underlying thing: a TCP connection that no longer works.

What's causing this error

A long-lived SSH session is a TCP connection that sits idle for minutes at a time. Anything in the path that holds connection state — your home router, a corporate firewall, your VPN, the cloud provider's load balancer — will drop "stale" connections to reclaim memory. Default timeouts are usually 5–15 minutes of total idleness.

When that happens, the next time you press a key, your client tries to write to a socket that's silently dead. The kernel raises EPIPE, which becomes "Broken pipe" in the error output.

The four sources, in rough order of likelihood:

  1. NAT or firewall idle timeout. Most common. The connection table entry expired.
  2. Your local network changed. Wi-Fi sleep, switching between Wi-Fi and Ethernet, VPN reconnect — the source IP changed, the existing TCP socket is now orphaned.
  3. Server-side ClientAliveInterval/ClientAliveCountMax explicitly disconnects idle sessions.
  4. Network blip. Brief packet loss caused the connection to give up; rare on stable links but common over flaky cellular or distant hops.

How to fix it

Step 1: Add ServerAliveInterval to ~/.ssh/config

This is the fix in 90% of cases. Edit ~/.ssh/config:

Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

What this does: every 60 seconds of idle time, your client sends a tiny encrypted keepalive to the server. The connection stays "active" from every NAT/firewall's perspective. ServerAliveCountMax 3 means "give up after 3 missed keepalives" — so if the connection truly is dead, you find out after ~3 minutes instead of waiting indefinitely.

This applies to every host. If you only want it for specific hosts:

Host work.example.com
    ServerAliveInterval 60
    ServerAliveCountMax 3

Step 2: For sessions you can't afford to lose, use a multiplexer

ServerAliveInterval prevents drops. But for truly long-running work — a multi-hour build, an SSH session you started before lunch — you want the work to survive any disconnect, including ones you can't prevent.

Two options:

# tmux (recommended): start it on the server, detach safely.
$ ssh user@server
$ tmux new -s work
# Work happens. If the SSH session drops, the tmux session keeps running.
# Reconnect and re-attach:
$ ssh user@server
$ tmux attach -t work

Alternative: mosh — a UDP-based SSH replacement that survives IP changes and reconnects automatically. Worth it if you frequently switch networks.

Step 3: Server-side fix (only if you control the server)

If users keep getting kicked, you can tell sshd to send keepalives from its side:

# /etc/ssh/sshd_config
ClientAliveInterval 60
ClientAliveCountMax 3

Restart sshd:

sudo systemctl restart ssh

Now the server keeps the connection warm even for clients that don't set their own keepalive.

Common edge cases

SituationWhat's actually wrong
Drops at exactly 5 / 10 / 15 minutes idleNAT or firewall timeout — fix with ServerAliveInterval
Drops every time you switch Wi-Fi networksSource IP changed; TCP can't recover. Use mosh or accept it.
Drops only on corporate Wi-FiCorporate firewall has very aggressive idle timeouts. ServerAliveInterval 30 (more aggressive) usually solves it
Drops randomly mid-typingGenuine network instability — try mosh, or check if your link has packet loss (mtr <host>)
Connection to X closed by remote host insteadServer-side disconnect — check sshd_config ClientAliveInterval on the server, or session policy that's killing idle users
Keepalive enabled but still drops over VPNVPN MTU mismatch — keepalives are tiny so they pass, but real traffic fragments. Lower MTU with Tunnel/TCPKeepAlive directives
Stays connected, but commands hangServer is alive but a process inside the session is stuck (network call timing out) — not the same problem, ignore broken pipe advice

Related errors