Many sysadmins encounter this frustrating scenario: You're connected to a remote server via SSH, then activate a VPN connection (like OpenVPN or WireGuard), and suddenly - your SSH session freezes. This occurs because VPN clients typically modify the system's routing table to redirect all traffic through the VPN tunnel, including your existing SSH connection.
When a VPN connection establishes, it generally does three things:
- Creates a new network interface (tun0/tap0)
- Pushes new routes that override the default gateway
- Updates DNS resolvers to use VPN providers' servers
Here's what a typical routing table looks like post-VPN connection:
$ ip route show
default via 10.8.0.1 dev tun0
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.2
123.45.67.89 via 192.168.1.1 dev eth0
For OpenVPN, add these directives to your client configuration:
route-nopull
route 123.45.67.89 255.255.255.255 net_gateway
route add default gw 10.8.0.1
Replace 123.45.67.89 with your server's actual IP. This tells OpenVPN to maintain the original route to your SSH connection while routing everything else through VPN.
For advanced users, creating a separate network namespace for VPN keeps your main connection intact:
# Create namespace
ip netns add vpnspace
# Move interface to namespace
ip link set eth1 netns vpnspace
# Execute VPN client in namespace
ip netns exec vpnspace openvpn --config client.ovpn
While not solving the routing issue, using terminal multiplexers ensures work continuity:
# Before VPN connection
tmux new -s mysession
# After reconnection
tmux attach -t mysession
For WireGuard users, add these to your config (wg0.conf):
[Interface]
Table = off
PostUp = ip route add 123.45.67.89/32 dev eth0
PostUp = ip route add default via $(ip route show default | awk '{print $3}')
Always test changes with these commands before relying on them:
# Check routes before VPN
ip route show
# Monitor route changes in real-time
watch -n 1 ip route show
# Test SSH persistence through separate terminal
while true; do ssh user@server "date"; sleep 1; done
When you establish a VPN connection on your Linux VPS while maintaining an active SSH session, the default route often gets overwritten by the VPN configuration. This routing table modification causes your SSH session to terminate abruptly because:
- VPN clients typically push a new default gateway (0.0.0.0/0)
- Existing TCP connections can't survive the route change
- SSH keepalives fail when packets route through VPN tunnel
The most reliable solution involves creating explicit routing rules for your original SSH connection before starting the VPN. Here's how to implement it:
# Get your current public IP (SSH client)
CLIENT_IP=$(curl -s ifconfig.me)
# Add routing rule for your existing SSH connection
ip route add $CLIENT_IP via $(ip route show default | awk '{print $3}') dev eth0
ip route add default via $(ip route show default | awk '{print $3}') table 100
# Add policy routing rule
ip rule add from $(hostname -I | awk '{print $1}') table 100
ip rule add to $CLIENT_IP table 100
For OpenVPN Users
Add these directives to your client configuration:
route-nopull
route $CLIENT_IP 255.255.255.255 net_gateway
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
For WireGuard Configuration
Modify your wg0.conf to preserve SSH:
[Interface]
Table = off
PostUp = ip route add $CLIENT_IP/32 dev %i
PostUp = ip route add default via $(ip route show default | awk '{print $3}')
Combine routing fixes with SSH configuration:
# ~/.ssh/config
Host myvps
HostName vps.example.com
User admin
ServerAliveInterval 60
TCPKeepAlive yes
EscapeChar none
RemoteCommand sudo -i
For mission-critical operations, consider these additional measures:
- Setup a secondary SSH listener on non-standard port with static routes
- Configure Mosh (mobile shell) as fallback connection
- Implement automatic reconnection scripts using tmux/screen
Essential diagnostics when connection drops occur:
# Check active routes
ip route show table all
# Verify routing for specific IP
ip route get $CLIENT_IP
# Monitor interface changes
ip monitor route & ip monitor link
# Check VPN routing tables
wg show | grep -A10 "routing table"