At a glance
- Client:
ServerAliveInterval 60in~/.ssh/config, off by default. - Server:
ClientAliveInterval 60in/etc/ssh/sshd_config, off by default. TCPKeepAliveis on by default, but Linux waits 2 hours before its first probe.- When the connection dies anyway, only tmux, screen or nohup keep the job running.
Why idle SSH sessions drop
Firewalls and NAT routers often drop connections that carry no traffic for a while. An idle SSH session sends nothing, so a box between you and the server can cut it silently, and your terminal freezes until TCP gives up.
When you press a key and ssh writes to the dead connection, it fails, often with client_loop: send disconnect: Broken pipe. That message shows up after an idle drop or a network change.
Client side: ServerAliveInterval
Fix it on your own machine with ServerAliveInterval. It defaults to 0 (off) and sets the seconds of silence after which ssh sends a message through the encrypted channel asking the server to respond. ServerAliveCountMax defaults to 3 and sets how many unanswered messages ssh allows before it disconnects.
With 60 and 3, ssh notices a dead server after about 180 seconds, prints Timeout, server HOST not responding. and exits with status 255. Set it for every host at the end of ~/.ssh/config, because ssh uses the first value it finds and more specific Host blocks belong above it:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3For one session, pass it on the command line with ssh -o ServerAliveInterval=60 user@host. ssh -G host | grep -i alive prints the values ssh will actually use; we tested with the OpenSSH 10.0 packages in Debian 13. Debian's client uses 300 instead of 0 when BatchMode yes is set, a Debian-specific change.
Server side: ClientAliveInterval
If you run the server, ClientAliveInterval (default 0, off) and ClientAliveCountMax (default 3) do the same from the other end in /etc/ssh/sshd_config. ClientAliveCountMax 0 disables the disconnect, so the server keeps sending messages without ever dropping the client.
# After adding "ClientAliveInterval 60" and "ClientAliveCountMax 3" to /etc/ssh/sshd_config
sudo sshd -t
sudo sshd -T | grep -i clientalive
sudo systemctl reload sshsshd -t checks the file, sshd -T shows the effective values and the reload applies them to new connections. On Fedora and RHEL the unit is called sshd.
| Setting | Where | Default | What it does |
|---|---|---|---|
ServerAliveInterval | Client, ~/.ssh/config | 0 (off) | Seconds of silence before ssh asks the server to respond |
ServerAliveCountMax | Client, ~/.ssh/config | 3 | Unanswered messages before ssh disconnects |
ClientAliveInterval | Server, /etc/ssh/sshd_config | 0 (off) | Seconds of silence before sshd asks the client to respond |
ClientAliveCountMax | Server, /etc/ssh/sshd_config | 3 | Unanswered messages before sshd disconnects; 0 never disconnects |
TCPKeepAlive | Client and server | on | TCP keepalive packets outside the encrypted channel |
ConnectTimeout | Client, ~/.ssh/config | none | Limits connection setup only, not an open session |
TCPKeepAlive and ConnectTimeout are different things
TCPKeepAlive is on by default on both sides and uses TCP-level keepalive packets. They travel outside the encrypted channel, so they can be spoofed, and Linux waits 7,200 seconds, two hours, before sending the first one (net.ipv4.tcp_keepalive_time). A firewall with a shorter idle timeout drops the connection first, so ServerAliveInterval and ClientAliveInterval are the settings that help.
ConnectTimeout (default none, so the system TCP timeout applies) only limits how long ssh waits to set up a connection and finish the handshake. ssh: connect to host ... port 22: Connection timed out means the connection never opened: a wrong address, a firewall blocking you or a server that is down, not an idle drop.
Mosh for networks that change under you
If the connection drops because you move between networks, try mosh 1.4.0 (October 2022). It logs in over SSH, starts mosh-server and then talks UDP on a port between 60000 and 61000, so your firewall must allow that range.
Install it on both ends (sudo apt install mosh) and connect with mosh user@host. It roams across Wi-Fi and IP changes, survives laptop sleep and shows your typing right away, but it has no port forwarding or X forwarding.
Keepalives do not protect your job
None of these settings keep a program running when the connection does drop. The session dies, the shell gets SIGHUP and its jobs die with it, so run long jobs in tmux or screen or start them with nohup. The tmux detach and attach guide and the nohup guide cover both.
FAQ
What is a good ServerAliveInterval value?
ServerAliveInterval 60 with the default ServerAliveCountMax 3 sends a message after every 60 seconds of silence and drops a dead connection after about 180 seconds. Pick an interval shorter than your firewall's idle timeout; the PuTTY manual suggests 300 seconds for a firewall that cuts connections after ten minutes.
What does "client_loop: send disconnect: Broken pipe" mean?
ssh tried to write to a connection that had already died, usually after an idle drop or a network change. A client-side ServerAliveInterval keeps idle connections from being cut in the first place.
Should I set keepalives on the client or the server?
Set ServerAliveInterval in ~/.ssh/config on the client to protect your own sessions to any server. Set ClientAliveInterval in /etc/ssh/sshd_config if you run the server and want it for everyone who connects.
Does ServerAliveInterval keep my job running if the connection drops?
No. If the connection drops anyway, the shell gets SIGHUP and its jobs die. Run long jobs inside tmux or screen, or start them with nohup.
How do I set SSH keepalives in PuTTY?
Open the Connection panel and enter a value in "Seconds between keepalives"; 0 turns them off. The PuTTY manual warns that keepalives can make things worse on networks with short breaks in connectivity.
Check on long runs from your phone
Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.
pip install tqdm-liveSources
- ssh_config(5) manual page man.openbsd.org
- sshd_config(5) manual page man.openbsd.org
- OpenSSH client loop source github.com
- Linux kernel docs: tcp_keepalive_time docs.kernel.org
- Mosh home page mosh.org
- PuTTY manual: using keepalives the.earth.li