Linux & OS Intermediate

Troubleshooting SSH Connection Timeout on WSL2 Ubuntu: Port 22 Keepalive Client Configuration

Resolve SSH connection timeouts from your WSL2 Ubuntu environment by configuring client-side keepalives and addressing common network and firewall issues.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve SSH connection timeouts from your WSL2 Ubuntu environment by configuring client-side keepalives and addressing common network and firewall issues.

SSH connections from your Windows Subsystem for Linux 2 (WSL2) Ubuntu environment to remote servers can sometimes experience frustrating timeouts. This often manifests as a connection dropping after a period of inactivity, or even failing to establish entirely, disrupting critical development and deployment workflows. This guide will walk you through diagnosing and resolving these issues, focusing on client-side keepalive configurations and WSL2-specific networking challenges.

Symptom & Error Signature

Users typically encounter one of the following error messages in their WSL2 Ubuntu terminal when attempting to establish or maintain an SSH connection:

ssh user@remote_host
# ... connection hangs for a long time ...
Read from remote host remote_host: Connection timed out
ssh: connect to host remote_host port 22: Connection timed out

Or, if the connection was established but dropped due to inactivity:

# ... session was active, then suddenly unresponsive ...
packet_write_wait: Connection to x.x.x.x port 22: Broken pipe

Root Cause Analysis

The primary reasons for SSH connection timeouts when using WSL2 as a client often stem from a combination of default SSH client behavior and the unique networking characteristics of WSL2:

  1. Lack of Client-Side Keepalives: By default, the SSH client does not send periodic "keepalive" packets to the server. Intermediate network devices (like NAT routers, firewalls, or load balancers) are designed to drop idle connections to free up resources. Without keepalives, an SSH session might be considered idle and terminated by such devices, leading to a timeout or a "Broken pipe" error.
  2. WSL2 Networking Peculiarities:
    • Hyper-V Virtual Switch & NAT: WSL2 leverages a Hyper-V virtual switch and often uses Network Address Translation (NAT) for network connectivity. This virtualized layer can sometimes introduce its own set of challenges, including aggressive connection timeout settings or transient network instability.
    • Windows Defender Firewall: While less common for outbound SSH connections initiated from WSL2, the Windows Defender Firewall can, in certain configurations, interfere with network traffic or specific ports.
    • Network State After Sleep/Hibernate: WSL2's network adapter can occasionally become unstable or misconfigured after the host Windows machine goes to sleep or hibernates, leading to connectivity issues upon waking.
  3. MTU (Maximum Transmission Unit) Mismatch: An MTU mismatch between your WSL2 environment, your local network, and the remote server's network path can cause packets to be silently dropped or fragmented inefficiently, leading to connection delays and eventual timeouts.
  4. DNS Resolution Issues: Slow or failing DNS lookups within WSL2 for the remote hostname can cause the ssh command to hang while attempting to resolve the IP address, eventually leading to a timeout error.

Step-by-Step Resolution

Follow these steps to diagnose and resolve SSH connection timeouts from your WSL2 Ubuntu instance.

1. Configure SSH Client Keepalives

The most common fix is to configure your SSH client to send periodic "alive" messages to the server. This prevents intermediate network devices from considering the connection idle and dropping it.

  1. Create or Edit the SSH Client Configuration File: The SSH client configuration is typically located at ~/.ssh/config in your WSL2 Ubuntu environment. If the file or directory does not exist, create them.

    mkdir -p ~/.ssh
    chmod 700 ~/.ssh # Ensure correct permissions
    nano ~/.ssh/config
    
  2. Add Keepalive Directives: Inside the ~/.ssh/config file, add the following lines. You can apply them globally to all SSH connections or to specific hosts.

    Host *
        ServerAliveInterval 60
        ServerAliveCountMax 5
    
    • ServerAliveInterval 60: This tells the SSH client to send a null packet to the server every 60 seconds if no data has been exchanged. This keeps the connection "alive" in the eyes of network devices.
    • ServerAliveCountMax 5: If the client does not receive any response from the server after sending 5 consecutive keepalive messages, it will disconnect. With ServerAliveInterval 60, this means the connection will be dropped after 5 * 60 = 300 seconds (5 minutes) of unresponsiveness.

    For specific hosts, you can define blocks:

    Host my_remote_server
        HostName your.remote.server.com
        User your_username
        ServerAliveInterval 60
        ServerAliveCountMax 3
    
    Host another_server
        HostName another.server.example.org
        User admin
        ServerAliveInterval 30
        ServerAliveCountMax 5
    

    Ensure that the permissions for your ~/.ssh directory are 700 (drwx------) and for ~/.ssh/config are 600 (-rw-------). Incorrect permissions will cause SSH to ignore the configuration file due to security concerns.

  3. Test the Connection: Save the changes to ~/.ssh/config and try your SSH connection again.

    ssh user@remote_host
    

2. Verify WSL2 Network Connectivity and DNS Resolution

Ensure that your WSL2 instance has stable network connectivity and can resolve hostnames correctly.

  1. Test Basic Connectivity: From your WSL2 terminal, try to ping the remote server's IP address and a public website.

    ping -c 4 8.8.8.8 # Ping Google's DNS server
    ping -c 4 your.remote.server.com # Ping your SSH target by hostname
    ping -c 4 <remote_server_IP_address> # Ping your SSH target by IP
    

    If ping to 8.8.8.8 fails, your WSL2 network is down. If pinging the IP works but the hostname fails, you have a DNS issue.

  2. Check DNS Resolution: Verify that your WSL2 instance can resolve hostnames.

    cat /etc/resolv.conf
    # Example output:
    # nameserver 172.X.X.X # This is often the WSL2-managed DNS server
    # nameserver 8.8.8.8   # Or directly assigned by Windows
    
    dig your.remote.server.com
    

    If dig fails or is very slow, DNS resolution is a problem. You might temporarily add a public DNS server to /etc/resolv.conf (e.g., nameserver 8.8.8.8) for testing, but be aware that WSL2 often overwrites this file. For persistent custom DNS, you need to configure ~/.wslconfig (see section 7).

3. Adjust Windows Defender Firewall Rules

While typically not the cause for outbound client-initiated connections, Windows Defender Firewall can sometimes interfere.

  1. Check Firewall Logs: Review your Windows Event Viewer (Applications and Services Logs -> Microsoft -> Windows -> Windows Firewall With Advanced Security -> Firewall) for any dropped packets originating from your WSL2 virtual network adapter to the remote SSH port (22).

  2. Temporarily Disable Firewall (for testing only): As a diagnostic step, you can temporarily disable Windows Defender Firewall on the Public or Private profile (whichever your WSL2 virtual network uses, typically Private) to rule it out.

    Disabling your firewall leaves your system vulnerable. Only do this in a controlled environment and re-enable it immediately after testing.

  3. Create Outbound Rule: If the firewall is indeed blocking, create an outbound rule allowing TCP traffic on port 22 from any source to any destination. This is typically done via "Windows Defender Firewall with Advanced Security" in Windows.

4. Investigate MTU Settings

An MTU mismatch can cause packet fragmentation and silent drops, leading to timeouts.

  1. Identify WSL2 Network Interface: Inside your WSL2 Ubuntu terminal, identify your primary network interface. It's usually eth0.

    ip a | grep "inet "
    

    Look for the interface with an IP address (e.g., eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...). Note its mtu value.

  2. Test MTU to Remote Host: Use ping with the do (Don't Fragment) flag to test the maximum packet size that can reach your remote server without fragmentation. Start with your current MTU (e.g., 1500) and decrease it gradually. The actual data size for ping -s is MTU - 28 (for IP/ICMP headers).

    ping -c 4 -M do -s 1472 your.remote.server.com # For MTU 1500
    # If this fails, try smaller:
    ping -c 4 -M do -s 1400 your.remote.server.com
    # Keep decreasing until it succeeds, then you've found your maximum payload.
    

    If ping starts succeeding at a smaller size (e.g., 1400), then your effective MTU is 1400 + 28 = 1428.

  3. Adjust MTU (if necessary): If you determine a smaller MTU is required, you can temporarily set it in WSL2. However, this change is often reset after a WSL2 restart.

    sudo ip link set dev eth0 mtu 1428 # Replace eth0 and 1428 with your values
    

    Permanently changing MTU in WSL2 is tricky as netplan isn't typically used for eth0 and changes often don't persist. If this is a persistent issue, consider checking the MTU settings on your Windows host network adapter or home router.

5. Reset WSL2 Network Adapters

Sometimes, WSL2's internal networking can get into a bad state. A full reset can resolve transient issues.

  1. Shut Down WSL2 Instances: From an elevated PowerShell or Command Prompt on Windows:

    wsl --shutdown
    
  2. Renew Windows IP Configuration: Still in PowerShell/CMD:

    ipconfig /release
    ipconfig /renew
    
  3. Re-launch WSL2: Simply open your Ubuntu distribution from the Start Menu or by running wsl in PowerShell. This will reinitialize the WSL2 network adapter.

6. System-Wide TCP Keepalive Settings (Advanced)

These settings control the operating system's default TCP keepalive behavior. While ServerAliveInterval in SSH config is usually sufficient for client-side issues, adjusting system-wide TCP keepalives can sometimes help, especially if many applications are experiencing similar timeouts.

  1. Check Current Settings:

    sysctl -a | grep keepalive
    

    You'll see values like:

    • net.ipv4.tcp_keepalive_time: The time (in seconds) an idle TCP connection remains active before TCP starts sending keepalive probes. Default is often 7200 seconds (2 hours).
    • net.ipv4.tcp_keepalive_intvl: The time (in seconds) between individual keepalive probes. Default is often 75 seconds.
    • net.ipv4.tcp_keepalive_probes: The number of keepalive probes TCP sends before dropping the connection. Default is often 9.
  2. Modify Settings (Temporarily): For testing, you can change them immediately:

    sudo sysctl -w net.ipv4.tcp_keepalive_time=600
    sudo sysctl -w net.ipv4.tcp_keepalive_intvl=60
    sudo sysctl -w net.ipv4.tcp_keepalive_probes=20
    

    This sets idle timeout to 10 minutes, probes every minute, up to 20 times.

  3. Make Changes Persistent: To make these changes permanent, edit /etc/sysctl.conf.

    sudo nano /etc/sysctl.conf
    

    Add or modify the following lines:

    # Custom TCP Keepalive Settings for WSL2 (optional)
    net.ipv4.tcp_keepalive_time = 600
    net.ipv4.tcp_keepalive_intvl = 60
    net.ipv4.tcp_keepalive_probes = 20
    

    Apply the changes:

    sudo sysctl -p
    

    Modifying system-wide TCP keepalive settings can affect all applications using TCP. Only do this if you understand the implications and SSH client-specific settings are not sufficient.

7. Troubleshoot DNS Resolution within WSL2

If you identified DNS issues in Step 2, here's how to address them more robustly.

  1. Check /etc/resolv.conf: WSL2 dynamically generates /etc/resolv.conf. By default, it points to a virtual DNS server provided by Windows.

    cat /etc/resolv.conf
    
  2. Disable Auto-Generation and Set Custom DNS: If the auto-generated DNS server is unreliable, you can prevent WSL2 from overwriting resolv.conf and specify your own DNS servers.

    • Create or edit the .wslconfig file in your Windows user profile directory (C:Users<YourUsername>.wslconfig). If it doesn't exist, create it.

      # .wslconfig
      [wsl2]
      dns=8.8.8.8
      dns=8.8.4.4
      generateResolvConf=false
      

      Replace 8.8.8.8 and 8.8.4.4 with your preferred reliable DNS servers (e.g., your router's IP, corporate DNS, or other public DNS).

    • Shut down WSL2 for changes to take effect:

      wsl --shutdown
      
    • Re-launch your WSL2 instance. Your /etc/resolv.conf should now reflect the custom DNS servers you specified.

By systematically applying these troubleshooting steps, you should be able to resolve persistent SSH connection timeouts when working from your WSL2 Ubuntu environment.

👨‍💻

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.