Troubleshooting SSH Connection Timeout on macOS: Client Keepalive Configuration for Port 22
Resolve frustrating SSH connection timeouts on macOS by configuring client-side KeepAlive settings. Prevent dropped sessions and improve remote server stability.
Resolve frustrating SSH connection timeouts on macOS by configuring client-side KeepAlive settings. Prevent dropped sessions and improve remote server stability.
Introduction
Experiencing an SSH connection that suddenly freezes, hangs, or drops after a period of inactivity can be incredibly disruptive for system administrators and developers alike. This common issue, often manifesting as a "Connection timed out" or a seemingly random disconnection, is typically caused by network devices (like firewalls, routers, or NAT gateways) terminating idle TCP connections to conserve resources. While the remote server itself might have its own idle timeout settings, a robust client-side configuration on your macOS machine can effectively prevent these frustrating interruptions, ensuring your SSH sessions remain stable even during periods of low activity. This guide will walk you through configuring SSH client-side keepalives to maintain persistent connections to your remote Linux servers.
Symptom & Error Signature
Users typically encounter one of the following scenarios:
The SSH session appears to hang indefinitely after a period of inactivity, requiring you to close the terminal and re-establish the connection.
Upon attempting to type a command after a pause, the terminal may display:
Write failed: Broken pipeOr, when trying to establish a new connection, especially if a previous attempt might have left a lingering state (less common for a direct "timeout" but related to network issues):
ssh: connect to host example.com port 22: Connection timed outAfter a connection has been established and becomes idle, you might see:
Read from remote host example.com: Connection reset by peer
Root Cause Analysis
The primary reasons behind SSH connection timeouts, specifically when connecting from a macOS client, are:
- Intermediate Network Device Idle Timeout: This is the most prevalent cause. Many network devices (firewalls, routers, load balancers, NAT gateways) are configured to automatically close TCP connections that have been idle for a specific duration (e.g., 5, 10, or 30 minutes). If your SSH client doesn't send any data for this period, the connection is silently dropped by the device, leading to a perceived "timeout" or "hang" from your perspective.
- Server-Side SSHd Idle Timeout: The remote SSH server (
sshd) can also be configured to terminate idle client connections usingClientAliveIntervalandClientAliveCountMaxsettings in/etc/ssh/sshd_config. While this guide focuses on client-side fixes, it's worth noting as a potential compounding factor. - Local Network Instability or Router Issues: Less common for predictable timeouts, but an unstable local Wi-Fi, a faulty router, or intermittent ISP issues can contribute to connection drops.
- Lack of Client-Side Keepalives: By default, your macOS SSH client might not be configured to send regular "keepalive" messages, making it vulnerable to the idle timeouts imposed by network infrastructure.
Step-by-Step Resolution
The most effective way to prevent SSH connection timeouts from your macOS client is to configure client-side ServerAliveInterval and ServerAliveCountMax options. These settings instruct your SSH client to periodically send a "no-op" packet to the server if no other data has been exchanged, thereby keeping the connection "alive" in the eyes of intermediate network devices.
1. Understanding SSH Client-Side Keepalive Options
Two key options control client-side keepalives:
ServerAliveInterval: This specifies the number of seconds after which the SSH client will send a message to the server if no data has been received from the server. This is a crucial setting to prevent intermediate network devices from considering the connection idle.ServerAliveCountMax: This sets the number of server "alive" messages (as defined byServerAliveInterval) which may be sent without the client receiving any messages back from the server. If this threshold is reached, SSH will disconnect from the server, treating it as unresponsive.
2. Configuring Keepalives for a Single SSH Connection (Temporary)
You can apply keepalive settings directly on the command line for a single SSH session. This is useful for quick tests or one-off connections.
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@your_remote_server.com
In this example:
-o ServerAliveInterval=60: The client will send a keepalive packet every 60 seconds if the connection is idle.-o ServerAliveCountMax=3: The client will tolerate 3 consecutive missed keepalive responses before giving up and terminating the connection. This means it would wait up to 3 * 60 = 180 seconds (3 minutes) without a response before disconnecting.
This command-line approach is great for testing, but for permanent solutions, editing your SSH configuration file is recommended.
3. Persistent Client-Side Keepalive Configuration (Recommended)
The most robust and convenient method is to configure these settings in your SSH client configuration file, ~/.ssh/config.
Check for an existing
~/.ssh/configfile:ls -la ~/.ssh/configIf it doesn't exist, you'll need to create it.
Create or edit the
~/.ssh/configfile:Use your preferred text editor (like
nanoorvi). For beginners,nanois generally easier:nano ~/.ssh/configAdd the keepalive configuration:
You can apply these settings globally to all SSH connections, or to specific hosts.
Global Configuration (for all connections):
Add the following lines to your
~/.ssh/configfile:Host * ServerAliveInterval 60 ServerAliveCountMax 3Configuration for a Specific Host:
If you only want these settings for a particular remote server, use a
Hostblock:Host my_web_server Hostname your_remote_server.com User your_username Port 22 ServerAliveInterval 60 ServerAliveCountMax 3 Host another_server Hostname 192.168.1.100 User admin ServerAliveInterval 30 ServerAliveCountMax 5- Replace
my_web_serverwith a friendly alias for your server. Hostnameis the actual IP address or domain name.Useris the username you use to log in.Portis optional if it's the default port 22.
The values
ServerAliveInterval 60andServerAliveCountMax 3are good starting points for most environments. You might adjustServerAliveIntervalto a lower value (e.g., 30 seconds) if you're in a particularly aggressive network environment, but be mindful that more frequent packets consume slightly more bandwidth (negligible for SSH).- Replace
Save the file and set correct permissions:
If using
nano, pressCtrl+X, thenYto confirm saving, andEnterto confirm the filename.Correct permissions for
~/.ssh/configare critical! SSH will often ignore the file or complain if its permissions are too permissive (e.g., world-readable).chmod 600 ~/.ssh/configThis command ensures that only you (the owner) can read and write to the file.
4. Verifying Configuration and Testing
After saving your configuration, new SSH sessions will pick up the settings.
Verify the active configuration for a host:
You can inspect the effective configuration for a specific host using the
ssh -Gcommand:ssh -G user@your_remote_server.com | grep -E 'serveraliveinterval|serveralivecountmax'You should see output similar to this:
serveraliveinterval 60 serveralivecountmax 3Test the connection:
Establish an SSH connection as usual:
ssh user@your_remote_server.comNow, leave the session idle for a period longer than the previous timeout (e.g., 10-15 minutes, or even an hour). The connection should remain active, and you should be able to type commands without issues.
5. (Optional) Checking Server-Side Configuration
While this guide focuses on client-side fixes, it's good practice to be aware of the server-side settings that can also affect connection timeouts. On your remote Linux server (e.g., Ubuntu 22.04 LTS), sshd_config contains ClientAliveInterval and ClientAliveCountMax.
Access your remote server via SSH (after implementing client-side fix).
Inspect the
sshd_configfile:grep -E 'ClientAliveInterval|ClientAliveCountMax' /etc/ssh/sshd_configCommon default settings might look like:
#ClientAliveInterval 0 #ClientAliveCountMax 3If
ClientAliveIntervalis commented out or set to0, the server will not send its own keepalive messages to the client. If it's set to a value (e.g.,ClientAliveInterval 300for 5 minutes), the server will proactively check if the client is still responsive.For most scenarios, a well-configured client-side
ServerAliveIntervalis sufficient to prevent network device timeouts. Modifying server-side settings is typically only necessary if the problem persists or if you need to enforce server-initiated disconnections for security or resource management reasons. If you modifysshd_config, remember to restart the SSH service:sudo systemctl restart sshd.
By implementing these client-side keepalive configurations, your macOS SSH client will proactively maintain its connection, effectively bypassing the idle timeout mechanisms of intermediate network devices and providing you with a stable, persistent SSH experience.