You leave an SSH session idle for a few minutes, come back, and the terminal is frozen. Eventually it prints client_loop: send disconnect: Broken pipe or Connection reset by peer. The usual cause isn’t the server. A router, firewall or NAT device between you and the server silently dropped the idle connection. Keepalive messages keep it alive and detect dead connections quickly.

Why idle connections die

Home routers, corporate firewalls, mobile networks and cloud NAT gateways track connections, and forget idle ones after a timeout. That can be as short as 5 minutes, or less on some mobile networks. Once forgotten, packets from either side are dropped. SSH doesn’t know, so the session looks alive but no keystrokes get through.

Sending a small encrypted message every so often keeps the state alive and lets SSH notice a dead link.

On your computer, in ~/.ssh/config:

Host *
    ServerAliveInterval 30
    ServerAliveCountMax 4
  • Every 30 seconds of inactivity, the client sends a keepalive through the encrypted channel.
  • If 4 in a row get no answer (2 minutes), the client disconnects cleanly instead of hanging forever.

For one connection: ssh -o ServerAliveInterval=30 user@host.

Windows OpenSSH uses C:\Users\you\.ssh\config with the same syntax. PuTTY: Connection → “Seconds between keepalives” = 30.

This fixes most problems, and only affects you.

Server side: ClientAliveInterval

To keep sessions alive for all users, and let the server clean up dead sessions, set this in /etc/ssh/sshd_config (or a drop-in in /etc/ssh/sshd_config.d/):

ClientAliveInterval 60
ClientAliveCountMax 3

The server checks every 60 seconds and drops the session after 3 missed replies. Apply it:

sudo sshd -t && sudo systemctl reload ssh     # 'sshd' on RHEL

Using it as an idle timeout (security)

The same settings can disconnect idle users. ClientAliveCountMax 0 used to kill sessions after one interval of idleness on older OpenSSH, but its behaviour changed in OpenSSH 8.2+. For a real idle logout, use the shell:

# /etc/profile.d/timeout.sh
TMOUT=900          # log out shells idle for 15 minutes
readonly TMOUT
export TMOUT

Or, in recent OpenSSH (9.2+), ChannelTimeout and UnusedConnectionTimeout. Compliance standards often require an idle timeout for admin sessions. See Linux server hardening.

TCPKeepAlive

TCPKeepAlive yes (the default) sends TCP-level keepalive probes. These are unencrypted, can be spoofed, and the OS defaults are often 2 hours, far too slow to beat NAT timeouts. ServerAlive/ClientAlive messages are the reliable choice. Leave TCPKeepAlive at its default.

Long-running jobs: don’t rely on the connection

Keepalives prevent idle drops, but they can’t save a session when your laptop sleeps, Wi-Fi changes or the network really goes down. For upgrades, imports, migrations and builds, run them inside tmux or screen:

tmux new -s upgrade
sudo apt full-upgrade
# connection drops → reconnect:
tmux attach -t upgrade

See tmux and nohup and background jobs.

On unreliable or mobile connections, mosh survives network changes and sleep. See mosh.

Other causes of SSH disconnects

Symptom Cause
Disconnects only during heavy output or transfers MTU or path problems, flaky Wi-Fi, packet loss. Test with mtr
Connection closed by remote host right after login Server-side: shell exits, ForceCommand, a full disk, or Fail2Ban banning you
Sessions killed after a fixed time, idle or not A corporate firewall’s maximum session length, or TMOUT in the shell
Broken pipe after the laptop wakes Expected. The network changed. Use tmux or mosh
Every connection drops at the same moment Server reboot, sshd restart, or a network outage. Check last -x and journalctl -u ssh

Quick reference

Where Setting Suggested
Client ~/.ssh/config ServerAliveInterval 30
Client ServerAliveCountMax 4
Server sshd_config ClientAliveInterval 60
Server ClientAliveCountMax 3
Shell idle logout TMOUT 900
Long jobs tmux / screen —

Related: SSH config file and SSH connection refused.

  • SSH
  • Linux
  • Troubleshooting