When working with SSH tunnels, proper termination is crucial for maintaining clean system states and preventing orphaned processes. The common Ctrl+C
or Ctrl+D
methods work but aren't always ideal for scripted environments.
Here are three professional approaches to closing your SSH tunnel:
# Method 1: Using the -f flag for background operation
ssh -f -N -L 5001:localhost:1019 eonil@test.local
# Then terminate with:
pkill -f "ssh -2 -N -L 5001:localhost:1019"
# Method 2: Using a control socket
ssh -f -N -M -S /tmp/ssh_tunnel -L 5001:localhost:1019 eonil@test.local
# Termination command:
ssh -S /tmp/ssh_tunnel -O exit eonil@test.local
# Method 3: PID capture and kill
ssh_pid=$!
# Later termination:
kill $ssh_pid
For complex environments with multiple tunnels, consider these patterns:
# Create named tunnel
ssh -f -N -L 5001:localhost:1019 -E /var/log/ssh_tunnel.log -o TCPKeepAlive=yes eonil@test.local
# Create management script (tunnel_manager.sh):
#!/bin/bash
case "$1" in
start)
ssh -f -N -L $2:localhost:$3 $4
;;
stop)
pkill -f "ssh -f -N -L $2:localhost:$3"
;;
esac
- Orphaned processes: Always verify termination with
ps aux | grep ssh
- Port conflicts: Check port availability with
netstat -tuln | grep 5001
- Authentication timeouts: Add
-o ServerAliveInterval=60
to connection string
For mission-critical tunnels:
# Use autossh for automatic reconnection
autossh -M 0 -f -N -L 5001:localhost:1019 eonil@test.local
# Set up monitoring with:
while true; do
if ! nc -z localhost 5001; then
pkill -f "autossh.*5001:localhost:1019"
autossh -M 0 -f -N -L 5001:localhost:1019 eonil@test.local
fi
sleep 60
done
ssh -2 -N -L 5001:localhost:1019 eonil@test.local
When you terminate an SSH tunnel with Ctrl+C or Ctrl+D, you're essentially sending a SIGINT (interrupt signal) to the SSH process. While this works, it's not the cleanest method because:
- It immediately terminates the TCP connections
- May leave orphaned processes on the remote server
- Could cause issues with connection tracking
Method 1: Using SSH Escape Sequence
The proper way involves SSH's built-in escape character:
~.
(Enter the tilde followed by period while connected)
This sends a clean disconnect signal through the SSH protocol itself.
Method 2: Killing by Process ID
For more control:
# Find the process
ps aux | grep "ssh -2 -N -L"
# Then kill it properly
kill -TERM [PID]
Method 3: Using ControlMaster (Advanced)
If you setup ControlMaster in your ~/.ssh/config:
Host test.local
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 5m
You can then use:
ssh -O exit test.local
For scripts, consider this wrapper:
#!/bin/bash
ssh -f -N -L 5001:localhost:1019 eonil@test.local
trap "kill -TERM $!" EXIT
# Your commands here
This ensures the tunnel closes when your script exits.
If connections hang despite proper termination:
# Check for orphaned connections
netstat -tulnp | grep 5001
# Force cleanup if needed
fuser -k 5001/tcp