Why My SSH Session Drops After a Few Minutes (and How to Fix It)
If your SSH session to a VPS keeps disconnecting after a few minutes, the most common culprit is idle timeouts imposed by NAT devices, firewalls, or server policies. Here’s a concise, copy-pasteable guide to fix it from both the client and server sides, plus a few sneaky “gotchas” to check.
TL;DR (Quick Fix)
Client (OpenSSH):
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 user@your-vps
Server (Debian/Ubuntu):
sudo nano /etc/ssh/sshd_config
# Add or adjust:
ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes
sudo systemctl reload sshd
That alone fixes it for most people.
1) Client-Side Keepalives (Immediate Relief)
OpenSSH (macOS/Linux/Windows PowerShell)
One-off:
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 user@your-vps
Persistent (recommended): create or edit ~/.ssh/config:
Host vps
HostName your-vps-domain-or-ip
User user
ServerAliveInterval 30
ServerAliveCountMax 3
Then connect with:
ssh vps
PuTTY
- Connection → Seconds between keepalives:
30 - Enable TCP keepalives (SO_KEEPALIVE): checked
Save the session so you don’t have to redo this.
2) Server-Side Keepalives (Make It Stick)
On your VPS (Debian/Ubuntu):
sudo nano /etc/ssh/sshd_config
Ensure the following lines exist (add or modify as needed—avoid duplicates):
ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes
# Optional: LoginGraceTime 120
Reload without dropping current sessions:
sudo systemctl reload sshd # or: sudo systemctl reload ssh
What this does: the server proactively pings the client so middleboxes don’t reclaim the “idle” TCP session.
3) Hidden Killers to Check
A) Shell Auto-Logout via TMOUT
If TMOUT is set, the shell will auto-logout after N seconds.
Check:
echo $TMOUT
If you see a number (e.g., 300), disable it:
unset TMOUT
Then remove or comment any TMOUT=... lines in:
/etc/profile/etc/bash.bashrc~/.bashrc~/.zshrc(if using zsh)
B) Fail2Ban / SSHGuard False Positives
If you had multiple failed logins from the same IP/device, you might be getting briefly banned.
Inspect:
sudo journalctl -u ssh -e
sudo fail2ban-client status sshd # if Fail2Ban is installed
If banned, fix the offending behavior and consider allowlisting your trusted IP.
C) VPN / NAT / Firewall Idle Timeouts
Routers and corporate networks often drop idle TCP sessions.
- Keepalives (client/server) usually solve this.
- Using WireGuard? Add a persistent keepalive:
Apply changes:
sudo wg-quick down wg0 && sudo wg-quick up wg0
On the client side, in [Peer] section:
PersistentKeepalive = 25
4) Troubleshooting Playbook (When It Still Drops)
Watch SSH logs live (on the server) while you reproduce the issue:
sudo journalctl -u ssh -f
Check for kernel/network hiccups:
dmesg --ctime | tail -n 100
Test path stability from the client (packet loss, jitter):
# On macOS: brew install mtr | On Debian/Ubuntu: sudo apt install mtr-tiny
mtr -rwzbc100 your-vps-ip
Interpretation basics:
- Loss% > 0 at the last hop? Likely network flakiness or aggressive middlebox.
- High jitter? Keepalives help; consider reducing intervals (e.g., 20–30s).
5) Suggested Defaults (Safe and Sane)
Client (~/.ssh/config):
Host *
ServerAliveInterval 30
ServerAliveCountMax 3
TCPKeepAlive yes
Server (/etc/ssh/sshd_config):
ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes
Why these values?
ServerAliveInterval 30keeps the pipe warm through cranky NATs.- Server-side 60s is conservative and reduces noise if many clients connect.
CountMax 3gives transient blips ~1.5–3 minutes to recover before disconnect.
6) FAQ
Q: Does TCPKeepAlive yes duplicate ServerAliveInterval?
A: They’re different. TCPKeepAlive uses kernel-level keepalives (slow, infrequent by default). ServerAliveInterval is an SSH-level heartbeat—more reliable across NAT/firewalls. Use both.
Q: My session drops exactly at X minutes.
A: That screams policy/timeout (router, FW, corporate proxy, cloud LB). Lower your keepalive interval (e.g., 15–30s) and set server keepalives too.
Q: I’m using a jump host / ProxyJump.
A: Add keepalives on the client and ensure the jump host’s sshd_config has ClientAlive* set. Every hop matters.
One-Line Summary
Most “SSH disconnects after a few minutes” are idle timeouts. Add client keepalives (e.g., ServerAliveInterval=30) and server keepalives (ClientAliveInterval=60), then rule out TMOUT, Fail2Ban bans, and VPN/NAT timeouts. That’s it—rock-solid sessions.