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, and 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