Idle SSH Sessions: Disconnect or Keep Them Alive on Linux

Deepak Prasad
Tested on RHEL 10.2 (Coughlan) — vm1.lab.example (192.168.56.116) as SSH server; vm2.lab.example (192.168.56.220, connects from 192.168.56.156) as SSH client
Package openssh-server 9.9p1-25.el10_2 and openssh-clients 9.9p1-25.el10_2 on both lab hosts
Applies to RHEL, Rocky Linux, AlmaLinux, Fedora, Ubuntu, Debian, and other Linux systems with OpenSSH
Privilege sudo or root on the SSH server for sshd_config; normal user on the SSH client for ~/.ssh/config
Scope Server-side ClientAliveInterval, ClientAliveCountMax, and TCPKeepAlive; client-side ServerAliveInterval and ServerAliveCountMax; reading effective values with sshd -T and ssh -G. Does not replace terminating a hung interactive SSH session or firewall idle rules on middleboxes.
Related guides ssh command
List active SSH connections
OpenSSH authentication methods
SSH multiplexing
Test SSH connection

“Idle SSH” can mean two different things: the user has stopped interacting with the shell, or the connection has stopped carrying network traffic long enough for a NAT or firewall to expire it. OpenSSH uses different settings for these cases. Server admins and remote users often want opposite outcomes: drop ghost sessions on the server, or keep a quiet laptop session alive through a home router.

OpenSSH splits the work cleanly. sshd settings on the server decide when to probe or drop a client. ssh client settings on the workstation that runs ssh decide whether the client sends its own keepalive traffic. This guide tests both sides on vm1.lab.example (server) and vm2.lab.example (client).


Server settings vs client settings

Misplacing these directives is the most common mistake on this topic. Edit the SSH server when you control sshd; edit the SSH client when your session dies on the path between your laptop and the server.

Goal Where Directives File
Detect and drop unresponsive clients SSH server ClientAliveInterval, ClientAliveCountMax, optionally TCPKeepAlive /etc/ssh/sshd_config or /etc/ssh/sshd_config.d/*.conf
Keep a quiet session alive through NAT or firewalls SSH client ServerAliveInterval, ServerAliveCountMax /etc/ssh/ssh_config, /etc/ssh/ssh_config.d/*.conf, or ~/.ssh/config
Close a quiet shell channel when its traffic stops (OpenSSH 9+) SSH server ChannelTimeout session=… /etc/ssh/sshd_config drop-in

All procedural steps below name which lab host runs the command. Unless noted, server steps run on vm1.lab.example and client steps run on vm2.lab.example.


What the keepalive directives do

From the OpenSSH manuals:

  • ClientAliveInterval (server): after this many seconds with no data from the client, sshd sends an encrypted keepalive request. Default 0 disables those probes.
  • ClientAliveCountMax (server): how many consecutive probes may go unanswered before sshd closes the session. 0 disables termination from this mechanism — it does not force a faster disconnect.
  • ServerAliveInterval (client): the ssh client sends its own keepalive traffic when the session is quiet. Use this when a middlebox drops idle TCP flows even though both sides are healthy.
  • ServerAliveCountMax (client): how many unanswered client keepalive cycles occur before ssh gives up.
  • TCPKeepAlive (server): enables or disables OS-level TCP keepalive probes. Separate from ClientAlive*; useful for detecting some dead peers but not a substitute for encrypted SSH keepalives.

For a keyboard-idle but otherwise healthy session, the OpenSSH client normally answers server ClientAlive probes automatically. OpenSSH documents the configured disconnect delay as approximately ClientAliveInterval × ClientAliveCountMax after the connection becomes unresponsive; observed wall-clock time can vary depending on when the client last sent data and on scheduling.

If your goal is to close an SSH shell because its channel has no traffic, recent OpenSSH versions also provide ChannelTimeout. That measures inactivity on an SSH channel, not merely keyboard silence in every case — activity on the relevant channel resets its timeout, and it is separate from connection-level keepalive detection with ClientAlive*. See ChannelTimeout in sshd_config(5).


Read effective SSH server values

Before you change timeouts, read what sshd actually runs. Drop-in files under /etc/ssh/sshd_config.d/ override the main file.

On the server (vm1.lab.example), print the merged configuration:

bash
sudo sshd -T | grep -iE 'clientalive|tcpkeepalive|channeltimeout'

Sample output on a fresh RHEL 10 install before this lab adds overrides:

output
clientaliveinterval 0
clientalivecountmax 3
tcpkeepalive yes
channeltimeout none

clientaliveinterval 0 means the server is not actively probing idle clients yet. tcpkeepalive yes is the RHEL default at the TCP layer.


Drop unresponsive clients on the server

When you want sshd to close sessions whose client stops answering keepalive probes — crashed laptop, suspended process, or one-way network failure — set ClientAliveInterval and a non-zero ClientAliveCountMax on the server.

Create a drop-in on vm1.lab.example (adjust seconds for production; short values are easier to test):

text
# /etc/ssh/sshd_config.d/99-client-alive.conf
ClientAliveInterval 15
ClientAliveCountMax 1

Validate syntax and reload sshd without dropping existing sessions:

bash
sudo sshd -t

That command exits silently when the file parses cleanly.

Reload the running daemon:

bash
sudo systemctl reload sshd

Confirm the new values are active:

bash
sudo sshd -T | grep -i clientalive

Sample output:

output
clientaliveinterval 15
clientalivecountmax 1

OpenSSH documents the timeout as approximately ClientAliveInterval × ClientAliveCountMax. With 15 and 1, expect roughly 15 seconds after the connection becomes unresponsive — not keyboard-idle, but a client that stops answering keepalive probes. Observed timing in a test can vary depending on when the connection last exchanged data.

A responsive client stays connected while idle

From vm2.lab.example, start a quiet remote command — no typing, but the client still answers server probes:

bash
ssh root@192.168.56.116 sleep 120

That session stayed up for the full two minutes in this lab even with ClientAliveInterval 15. A user who stops typing is not necessarily an unresponsive SSH client.

If the client stops responding because its process, host, or network path fails, sshd eventually disconnects it according to ClientAliveInterval and ClientAliveCountMax. Use list active SSH connections (who, w, or ss -tnp sport = :22) on the server while you test policy changes.


Keep idle sessions alive from the client

When the problem is a NAT router, VPN concentrator, or firewall that forgets quiet TCP flows, configure keepalives on the SSH client — the machine where you run ssh.

On vm2.lab.example, scope the settings to one server in ~/.ssh/config:

text
Host vm1.lab.example 192.168.56.116
    ServerAliveInterval 60
    ServerAliveCountMax 3

Ask OpenSSH to print the merged client options for that host:

bash
ssh -G vm1.lab.example | grep -i alive

Sample output:

output
serveraliveinterval 60
serveralivecountmax 3
tcpkeepalive yes

After 60 seconds without receiving data from the server, the client sends an encrypted keepalive request. After three failed rounds it exits — long enough to survive most home-router idle timers without hammering the server every few seconds.

This does not change sshd on the remote host. It only affects connections initiated from hosts that use this client configuration.


TCPKeepAlive on the server

TCPKeepAlive in /etc/ssh/sshd_config toggles whether the kernel sends TCP-level keepalive probes on SSH connections. On this lab host the merged server config already showed tcpkeepalive yes in the earlier sshd -T step.

TCPKeepAlive is enabled by default in this tested configuration. It uses kernel TCP keepalives and is separate from the encrypted ClientAlive* / ServerAlive* mechanisms. You normally do not need to change it just to configure the SSH keepalive behavior covered here.


Troubleshooting

Symptom Likely cause Fix
Keyboard-idle users stay connected Expected behavior with ClientAlive*; healthy clients answer probes Use ChannelTimeout or shell-level idle timeout if you need inactivity-based logout
Dead or unresponsive clients stay connected ClientAliveInterval 0, ClientAliveCountMax 0, or probes disabled Set a positive ClientAliveInterval and a positive ClientAliveCountMax on the server, then reload sshd
Sessions still die on a quiet laptop NAT or firewall idle timeout Add ServerAliveInterval on the client ssh configuration
Changed sshd_config but old timing persists Typo or missing reload Run sudo sshd -t, then sudo systemctl reload sshd, then re-check with sudo sshd -T
Directive seems ignored Edited the wrong side ClientAlive* on server; ServerAlive* on client
sshd -t fails after edit Invalid keyword or unit in drop-in Fix the drop-in file; do not reload until sshd -t succeeds

References


Summary

Idle SSH timeouts depend on which side of the connection you configure. On the server (vm1.lab.example in this lab), ClientAliveInterval and a positive ClientAliveCountMax make sshd probe the client and drop sessions that stop answering — but a normal OpenSSH client still answers those probes when you simply stop typing. OpenSSH describes that mechanism as detecting unresponsive connections; ClientAliveCountMax 0 disables termination from it. For channel-level shell inactivity, see ChannelTimeout in the upstream manual.

On the client (vm2.lab.example), ServerAliveInterval and ServerAliveCountMax keep quiet TCP flows alive through NAT and stateful firewalls. TCPKeepAlive is a separate kernel-level option on the server and is not the same knob as the encrypted keepalive settings in this walkthrough.

Always verify merged settings with sudo sshd -T on the server and ssh -G host on the client before you assume a timeout change took effect. For stuck interactive sessions that ignore keepalives, see kill or disconnect a hung SSH session instead of tuning idle timers alone.


Frequently Asked Questions

1. Where do I configure SSH idle disconnect — server or client?

ClientAliveInterval and ClientAliveCountMax belong in sshd_config on the SSH server. ServerAliveInterval and ServerAliveCountMax belong in ssh_config or ~/.ssh/config on the machine that runs the ssh command. The old advice to edit the remote host as the client node is backwards.

2. Does ClientAliveInterval disconnect a user who stops typing?

Not by itself. When ClientAliveInterval is set, sshd sends encrypted keepalive probes and a normal OpenSSH client answers them even if you are idle at the keyboard. ClientAlive settings mainly drop clients that stop responding — crashed laptop, broken network path, or hung client process.

3. What is the difference between ClientAlive and TCPKeepAlive?

ClientAlive messages travel inside the encrypted SSH channel and are not spoofable. TCPKeepAlive is an OS-level TCP option that can detect some dead peers but is spoofable and behaves differently through NAT. OpenSSH treats them as separate mechanisms.

4. How do I keep SSH from dropping when I walk away behind a home router?

Raise ServerAliveInterval and ServerAliveCountMax in ~/.ssh/config on the workstation that initiates SSH. That makes the client send periodic traffic so middleboxes do not forget the flow. Pair it with Server-side ClientAlive only when you also want the server to detect dead clients.

5. What does ClientAliveCountMax 0 do?

Setting ClientAliveCountMax to zero disables termination based on unanswered client-alive probes. It does not make sshd disconnect idle users faster. Use a positive ClientAliveCountMax when you want sshd to close sessions after unanswered probes.
Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on OpenStack, KVM, Proxmox, and VMware.

  • Debian
  • Ubuntu
  • Linux
  • Red Hat Enterprise Linux
  • Shell Script
  • System Administration