Linux & OS Intermediate

SSH Client Connection Timeout & Keepalive Configuration on CentOS Stream / Rocky Linux

Resolve persistent SSH client connection timeouts on CentOS Stream/Rocky Linux. Learn to configure SSH client keepalives (ServerAliveInterval) to maintain stable connections and prevent disconnects.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve persistent SSH client connection timeouts on CentOS Stream/Rocky Linux. Learn to configure SSH client keepalives (ServerAliveInterval) to maintain stable connections and prevent disconnects.

A stable SSH connection is fundamental for any Systems Administrator or DevOps engineer. Experiencing frequent connection timeouts can severely disrupt workflows, especially during long-running tasks or inactive terminal sessions. This guide provides a highly technical, step-by-step approach to resolve SSH client connection timeouts on CentOS Stream and Rocky Linux environments by leveraging SSH client-side keepalive configurations, alongside considerations for server-side and network factors.

Symptom & Error Signature

Users typically experience a frozen terminal session after a period of inactivity or sometimes even during active work, followed by a sudden disconnection. The exact error messages can vary but generally indicate a loss of connection to the remote host.

Here are common error signatures seen on the client terminal:

Read from remote host 192.168.1.100: Connection timed out

Or, if the server or an intermediate network device actively closes the connection:

ssh_exchange_identification: read: Connection reset by peer
Connection to 192.168.1.100 closed by remote host.

In other cases, the terminal simply hangs indefinitely until a manual intervention (~. or Ctrl+C) forces a close, or the local SSH client eventually times out.

Root Cause Analysis

SSH connection timeouts primarily stem from the lack of traffic on an otherwise idle connection, leading to intermediate network devices (firewalls, routers, load balancers, NAT devices) dropping the connection.

  1. Network Inactivity Timeout: This is the most prevalent cause. Many network devices are configured with aggressive idle session timeouts to conserve resources. If an SSH session remains idle for longer than this threshold, the device drops the connection without notifying either the client or the server. When the user attempts to type again, the TCP connection has already been silently severed.
  2. Default SSH Client Behavior: By default, the OpenSSH client typically does not send periodic keepalive messages to the server. This means an idle SSH session is truly idle from a network perspective, making it susceptible to network device timeouts.
  3. Server-Side SSH Daemon Configuration: The remote server's sshd_config might be configured with ClientAliveInterval and ClientAliveCountMax settings that are too aggressive, causing the server to terminate idle client connections prematurely. While this guide focuses on client-side fixes, it's a critical server-side factor to consider.
  4. Network Instability/Path MTU Issues: Less common for explicit "timeout" messages, but unstable network paths or incorrect Maximum Transmission Unit (MTU) settings can lead to packet loss and connection resets, mimicking timeout scenarios.
  5. Local Firewall Restrictions: Occasionally, an overly restrictive local firewall on the client machine might drop outgoing SSH traffic or incoming responses, although this usually results in connection failures rather than timeouts after establishment.

Step-by-Step Resolution

The most effective and common solution is to configure the SSH client to send periodic "keepalive" messages, ensuring the connection is never truly idle from the perspective of network devices.

1. Understand SSH Keepalive Mechanisms

It's crucial to differentiate between client-side and server-side keepalives:

  • ServerAliveInterval (Client-Side): Configured on the SSH client. The client sends a null packet to the server if no data has been received from the server for the specified interval. This keeps the TCP connection active and prevents network devices from dropping the session.
  • ClientAliveInterval (Server-Side): Configured on the SSH server. The server sends a null packet to the client if no data has been received from the client for the specified interval. This helps the server detect dead client connections.

This guide primarily focuses on ServerAliveInterval to resolve client-initiated timeouts.

2. Configure Client-Side SSH Keepalives Globally (/etc/ssh/ssh_config)

To apply keepalive settings for all users and all SSH connections originating from your CentOS Stream or Rocky Linux client machine, modify the global SSH client configuration file:

  1. Open the global SSH client configuration file with a text editor:

    sudo vi /etc/ssh/ssh_config
    
  2. Locate or add the following lines. If they exist, uncomment them and adjust the values. If not, add them towards the end of the file.

    # Send a null packet to the server every 60 seconds if no data has been received from the server.
    ServerAliveInterval 60
    # Disconnect after 3 consecutive keepalive messages go unanswered.
    ServerAliveCountMax 3
    
    • ServerAliveInterval 60: This instructs the SSH client to send a null packet to the server every 60 seconds if no traffic has been received from the server. This keeps the TCP connection "alive" in the eyes of most firewalls and NAT devices.
    • ServerAliveCountMax 3: This specifies the number of consecutive server alive messages that may be sent without receiving any responses from the server. If this threshold is reached, SSH will disconnect the client, assuming the server is no longer reachable. With ServerAliveInterval 60 and ServerAliveCountMax 3, the client will disconnect after approximately 3 minutes (3 * 60 seconds) of no response from the server.
  3. Save the file and exit the editor. Changes take effect immediately for new SSH connections.

The values 60 and 3 are good starting points. You might need to adjust ServerAliveInterval to a shorter duration (e.g., 30 seconds) if your network devices have very aggressive idle timeouts. However, too short an interval can generate unnecessary network traffic, especially for many concurrent sessions.

3. Configure Client-Side SSH Keepalives Per-User (~/.ssh/config)

For more granular control, you can define keepalive settings for specific users or even specific hosts. This is highly recommended for individual users on a shared client machine or when different remote hosts require different keepalive behaviors.

  1. Create or open your user's SSH configuration file:

    vi ~/.ssh/config
    
  2. Add a Host block with the desired settings. This example sets specific keepalives for a host named myremotehost:

    Host myremotehost
        Hostname 192.168.1.100
        User sysadmin
        ServerAliveInterval 30
        ServerAliveCountMax 5
    
    Host anotherhost.example.com
        User devops
        ServerAliveInterval 90
        ServerAliveCountMax 2
    
    • Host *: You can also use Host * to apply settings to all hosts accessed by this user, effectively overriding the global /etc/ssh/ssh_config settings for this user.
    • Hostname: The actual IP address or domain name of the remote server.
    • User: The username to use when connecting to this host.
  3. Save the file and exit the editor. New SSH connections will use these settings.

Ensure the permissions for your ~/.ssh/config file are set correctly to 0600 (read/write only for the owner) to prevent SSH from ignoring it due to security concerns.

chmod 0600 ~/.ssh/config

4. Configure Client-Side SSH Keepalives Ad-Hoc (Command Line)

For a quick, temporary fix or for testing purposes without modifying configuration files, you can specify keepalive options directly on the ssh command line:

ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=5 user@remote_host_ip_or_hostname

This method overrides any settings in ~/.ssh/config or /etc/ssh/ssh_config for that specific connection.

5. Verify SSH Client Configuration

To confirm that your SSH client is using the desired keepalive settings, you can start an SSH session in verbose mode:

ssh -v user@remote_host_ip_or_hostname

Look for lines similar to these in the verbose output (scroll up, as it's typically at the beginning of the connection handshake):

debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 50: include /etc/ssh/ssh_config.d/*.conf
debug1: /etc/ssh/ssh_config line 56: Applying options for *
debug1: Reading configuration data /home/sysadmin/.ssh/config
debug1: /home/sysadmin/.ssh/config line 1: Applying options for myremotehost
debug1: Setting implicit ServerAliveCountMax to 3
debug1: ServerAliveInterval 60

This output confirms that ServerAliveInterval is being applied. The ServerAliveCountMax might be implicitly set or explicitly stated.

6. (Optional) Check Server-Side SSH Daemon Configuration (CentOS Stream / Rocky Linux)

If client-side keepalives don't fully resolve the issue, or if multiple clients report similar problems, the remote server's SSH daemon might be configured to aggressively terminate idle connections. This is less common in modern hosting environments but worth checking.

  1. Connect to your remote CentOS Stream / Rocky Linux server (if you can maintain a connection long enough) and open the sshd configuration file:

    sudo vi /etc/ssh/sshd_config
    
  2. Look for or add the following lines:

    # Send a null packet to the client every 300 seconds (5 minutes) if no data has been received from the client.
    ClientAliveInterval 300
    # Disconnect after 2 consecutive keepalive messages go unanswered.
    ClientAliveCountMax 2
    
    • ClientAliveInterval 300: The server sends a keepalive message if the client has been idle for 300 seconds (5 minutes).
    • ClientAliveCountMax 2: If the server sends 2 consecutive keepalive messages without a response, it will terminate the connection. This means the server will disconnect an unresponsive client after approximately 10 minutes (2 * 5 minutes).
  3. Save the file and exit. You must restart the sshd service for changes to take effect:

    sudo systemctl restart sshd
    

Modifying sshd_config incorrectly can lock you out of your server. Always ensure you have out-of-band access (e.g., console access, KVM, or a rescue system) before making critical changes to sshd_config. Test changes thoroughly.

7. (Optional) Address Network/Firewall Issues

If after applying both client and server-side keepalives, the problem persists, it indicates a deeper network or firewall issue.

  • Local Firewall (Client): Ensure firewalld (default on CentOS/Rocky) or iptables on your client machine is not blocking outbound SSH connections or inbound responses. This is rare for established connections, but a sanity check doesn't hurt.
    sudo firewall-cmd --state
    sudo firewall-cmd --list-all --zone=public
    # Or for specific zones/rules.
    
  • Intermediate Network Devices: Consult your network administrator or hosting provider. Some corporate firewalls or cloud load balancers have very strict idle timeouts (e.g., 30 seconds). While keepalives usually mitigate this, extreme cases might require adjustments on the network device itself or a shorter ServerAliveInterval.
  • Network Diagnostics: Tools like traceroute, mtr, or ping can help diagnose connectivity issues and identify potential packet loss or high latency along the network path.

By systematically applying these configurations, you can significantly reduce or eliminate SSH client connection timeouts, ensuring a more stable and productive remote administration experience on CentOS Stream and Rocky Linux.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.