OpenSSH keepalives

SSH keep alive: stop idle SSH connections from timing out

Add ServerAliveInterval 60 to ~/.ssh/config and ssh sends a keepalive through the encrypted channel after 60 seconds of silence, giving up after 3 missed replies, about 180 seconds. ServerAliveInterval and the server's ClientAliveInterval both default to 0, which means off. Keepalives keep the connection open; they do not keep your job alive if it drops.

4 min readTerminals and remote servers

At a glance

  • Client: ServerAliveInterval 60 in ~/.ssh/config, off by default.
  • Server: ClientAliveInterval 60 in /etc/ssh/sshd_config, off by default.
  • TCPKeepAlive is 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:

Text
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3

For 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.

Bash
# After adding "ClientAliveInterval 60" and "ClientAliveCountMax 3" to /etc/ssh/sshd_config
sudo sshd -t
sudo sshd -T | grep -i clientalive
sudo systemctl reload ssh

sshd -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.

SettingWhereDefaultWhat it does
ServerAliveIntervalClient, ~/.ssh/config0 (off)Seconds of silence before ssh asks the server to respond
ServerAliveCountMaxClient, ~/.ssh/config3Unanswered messages before ssh disconnects
ClientAliveIntervalServer, /etc/ssh/sshd_config0 (off)Seconds of silence before sshd asks the client to respond
ClientAliveCountMaxServer, /etc/ssh/sshd_config3Unanswered messages before sshd disconnects; 0 never disconnects
TCPKeepAliveClient and serveronTCP keepalive packets outside the encrypted channel
ConnectTimeoutClient, ~/.ssh/confignoneLimits 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.

Get a live linkpip install tqdm-live

Sources