Resolving SSH Connection Timeout (Client Keepalive Configuration) on Ubuntu 22.04 LTS

Fix persistent SSH connection timeouts on Ubuntu 22.04 by configuring client-side keepalives. Diagnose network and server-side factors effectively.


Fix persistent SSH connection timeouts on Ubuntu 22.04 by configuring client-side keepalives. Diagnose network and server-side factors effectively.

SSH connection timeouts can be a significant productivity hindrance for system administrators and developers. This guide focuses on diagnosing and resolving persistent SSH connection drops or initial "Connection timed out" errors specifically related to client-side keepalive configurations on an Ubuntu 22.04 LTS system. Understanding how network devices and SSH protocols manage idle connections is key to establishing stable, long-running SSH sessions.

Symptom & Error Signature

Users typically experience one of the following symptoms:

  • Initial Connection Failure: The SSH command fails immediately or after a prolonged wait.
    ssh: connect to host your_server_ip port 22: Connection timed out
    
  • Session Freezing/Dropping: An established SSH session freezes after a period of inactivity, often followed by a disconnect message or the terminal becoming unresponsive.
    Write failed: Broken pipe
    
    or
    Read from remote host your_server_ip: Connection reset by peer
    
    or the terminal simply stops responding to input.

Root Cause Analysis

The "SSH connection timeout" error, particularly when relating to keepalives and client configuration, often stems from how network infrastructure and SSH itself handle idle connections.

  1. Network Inactivity (NAT/Firewall Timeouts): This is the most prevalent cause. Intermediate network devices like routers, firewalls, or NAT gateways often maintain a state table for active connections. If no traffic passes through a TCP connection for a certain period (e.g., 5 minutes, 10 minutes), these devices might prematurely prune the connection state from their tables. When the client or server later tries to send data, the other end, having its connection state reset by an intermediate device, doesn't respond, leading to a "Connection reset by peer" or a perceived timeout.
  2. Client-Side SSH Keepalive Configuration:
    • ServerAliveInterval: Configured on the client, this parameter specifies the time in seconds after which, if no data has been received from the server, the client will send a NULL packet to the server. This "keepalive" packet prevents network devices from timing out the session due to inactivity.
    • ServerAliveCountMax: Also configured on the client, this specifies the number of ServerAliveInterval messages (see above) which may be sent without ssh(1) receiving any messages back from the server. If this threshold is reached, SSH will disconnect the session. Insufficient or absent client-side keepalives allow network devices to drop the connection.
  3. Server-Side SSH Configuration (ClientAliveInterval): While the problem description emphasizes client configuration, it's crucial to acknowledge the server's role. The server-side equivalent, ClientAliveInterval in /etc/ssh/sshd_config, dictates how often the server sends keepalive messages to the client. If the server is aggressively configured to disconnect idle clients, it can override weak client-side settings.
  4. Firewall or Security Group Issues: Though more commonly associated with initial connection failures, overly aggressive or misconfigured stateful firewalls (e.g., UFW on Ubuntu, cloud provider security groups) can drop connections that appear idle, leading to timeouts.
  5. Network Latency and Packet Loss: High latency or intermittent packet loss on the network path can cause packets, including SSH keepalives, to be dropped or delayed significantly, leading to the SSH client or server believing the connection has died.

Step-by-Step Resolution

Follow these steps to diagnose and resolve SSH connection timeouts on Ubuntu 22.04 LTS, prioritizing client-side configuration.

1. Confirm Basic Network Reachability

Before modifying SSH configurations, ensure the server is generally reachable on the network and port 22.

  1. Ping the Server:

    ping -c 5 your_server_ip_or_hostname
    

    This checks basic IP connectivity. If you see 100% packet loss, there's a fundamental network issue or firewall blocking ICMP.

  2. Test Port 22 Accessibility: Use nc (netcat) or telnet to check if port 22 on the server is open and listening.

    # Using netcat (recommended)
    nc -vz your_server_ip_or_hostname 22
    
    # Expected successful output:
    # Connection to your_server_ip_or_hostname 22 port [tcp/ssh] succeeded!
    
    # Using telnet (install if not present: sudo apt install telnet)
    telnet your_server_ip_or_hostname 22
    
    # Expected successful output:
    # Trying your_server_ip_or_hostname...
    # Connected to your_server_ip_or_hostname.
    # Escape character is '^]'.
    # SSH-2.0-OpenSSH_9.0p1 Ubuntu-1ubuntu7.2 (or similar)
    # Press Ctrl+] then type 'quit' and Enter to exit.
    

    If these fail, the issue is likely a firewall on the server, a network routing problem, or the SSH daemon (sshd) not running on the server.

2. Configure Client-Side SSH Keepalives

This is the primary solution for client-side timeout issues. You can configure keepalives per-session, per-host, or globally.

  1. Per-Session Configuration (Temporary): For a quick test without altering configuration files, add the ServerAliveInterval and ServerAliveCountMax options directly to your ssh command.

    ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@your_server_ip_or_hostname
    

    This tells the client to send a keepalive packet every 60 seconds if no data is received, and to disconnect after 3 unanswered packets (total 180 seconds of no server response). Adjust 60 based on your network's typical idle timeout. A common value is 30 or 60 seconds.

  2. Per-Host Configuration (Recommended): For specific servers you connect to regularly, modify your personal SSH configuration file ~/.ssh/config. If it doesn't exist, create it.

    nano ~/.ssh/config
    

    Add an entry for your server, or modify an existing one:

    Host my_remote_server # Use an alias for easier access, e.g., 'prod-web-01'
        HostName your_server_ip_or_hostname
        User your_username
        Port 22 # Optional, defaults to 22
        ServerAliveInterval 60
        ServerAliveCountMax 3
        # Add other common options like IdentityFile if needed
        # IdentityFile ~/.ssh/id_rsa
    

    Save and close the file. Now, you can connect using the alias: ssh my_remote_server.

    Ensure ~/.ssh/config has appropriate permissions: chmod 600 ~/.ssh/config. Incorrect permissions might cause SSH to ignore the file.

  3. Global Configuration (Affects all Client Connections): If you want to apply keepalives to all SSH connections made from your Ubuntu client, modify the global SSH client configuration file /etc/ssh/ssh_config.

    sudo nano /etc/ssh/ssh_config
    

    Find or add the following lines, typically under a Host * block:

    Host *
        SendEnv LANG LC_*
        HashKnownHosts yes
        GSSAPIAuthentication yes
        # Add/uncomment these lines:
        ServerAliveInterval 60
        ServerAliveCountMax 3
    

    Save and close the file.

    Modifying /etc/ssh/ssh_config affects all SSH connections originating from this client system. It's generally safer and more flexible to use ~/.ssh/config for specific hosts or users unless a system-wide policy is required.

3. Diagnose Server-Side SSH Configuration

If client-side keepalives don't fully resolve the issue, the server's SSH daemon (sshd) might be configured to aggressively terminate idle client connections. You'll need to connect to the server (perhaps temporarily using the per-session client config from Step 2.1 or via another access method like a cloud console) to check its configuration.

  1. Check Server's sshd_config: On the remote server, open the SSH daemon configuration file:

    sudo nano /etc/ssh/sshd_config
    
  2. Look for ClientAlive directives: Search for ClientAliveInterval and ClientAliveCountMax. If they are uncommented and set to low values (e.g., ClientAliveInterval 0 or very small numbers, which disables server-side keepalives or makes them too aggressive), this could be the problem.

    # Uncomment and/or adjust these lines if present and restrictive
    # ClientAliveInterval 60
    # ClientAliveCountMax 3
    
    • ClientAliveInterval: The server will send a null packet to the client if no data has been received from the client for this many seconds.
    • ClientAliveCountMax: The number of client alive messages (see above) which may be sent without sshd(8) receiving any messages back from the client. If this threshold is reached, sshd will disconnect the client.
  3. Modify and Restart SSH Daemon: If you make changes, save the file and restart the sshd service:

    sudo systemctl restart sshd
    

    Always test SSH access from another terminal before closing your current administrative SSH session after modifying /etc/ssh/sshd_config. Incorrect configurations can lock you out of the server. Have a backup access method ready (e.g., cloud provider console, KVM).

4. Review Firewall and Security Group Rules

Ensure that no firewall or security group is prematurely dropping established connections or blocking port 22.

  1. Client-Side Firewall (UFW): On your Ubuntu client, check UFW status. By default, UFW allows all outbound connections, but custom rules might exist.

    sudo ufw status verbose
    

    Ensure nothing is explicitly blocking outbound traffic on port 22 or for SSH.

  2. Server-Side Firewall (UFW & Cloud Security Groups):

    • UFW on the server:
      sudo ufw status verbose
      
      Confirm that port 22 is open for inbound connections from your client's IP address or a sufficiently broad range (Anywhere). Example:
      To                         Action      From
      --                         ------      ----
      22/tcp                     ALLOW       Anywhere
      
    • Cloud Provider Security Groups/Firewall Rules: If your server is hosted on AWS, Azure, GCP, or similar, check its associated security groups or network firewall rules. Ensure inbound TCP port 22 is permitted from your client's public IP address or the necessary IP range. These rules operate at the network edge and can override OS-level firewalls like UFW.

5. Analyze System Logs for Clues

System logs can provide valuable insights into why connections are being dropped or failing.

  1. Client-Side Logs: Open a new terminal on your client and tail the system journal while attempting an SSH connection or waiting for a timeout.

    journalctl -f | grep ssh
    

    Look for messages indicating connection attempts, failures, or errors from the ssh client.

  2. Server-Side Logs: On the remote server, check the sshd service logs.

    journalctl -u sshd -f
    

    or, for older systems or specific configurations:

    grep sshd /var/log/auth.log
    

    Look for messages related to accepted connections, disconnections, authentication failures, or specific error messages from sshd that might explain why a session was terminated.

By systematically applying these troubleshooting steps, especially focusing on the client-side ServerAliveInterval and ServerAliveCountMax configurations, you should be able to establish and maintain stable SSH connections on your Ubuntu 22.04 LTS system.