If you're working with remote resources like AWS DocumentDB or any cloud-hosted service behind a bastion host, chances are you're using SSH tunnels to securely access them. While setting up an SSH tunnel is straightforward, keeping it alive and monitoring its status over time can be a bit trickier.
Today, I’m sharing a lightweight Bash script I wrote to set up and monitor an SSH tunnel. It’s simple, portable, and gives you real-time feedback on the tunnel’s status.
💡 Use Case
Imagine this: You have an AWS DocumentDB cluster that's only accessible through a bastion host. You want to:
- Forward a local port (e.g.
27088
) to the remote DocumentDB port (27017
). - Automatically monitor if the SSH tunnel drops.
- Get a simple minute-by-minute status update.
- Clean up the SSH process cleanly if the script or terminal is terminated.
🧩 The Script
Here’s the script in full:
#!/bin/bash
KEY="my-bastion-host-key-pair.pem"
USER="ec2-user"
HOST="ec2-YYY-YYY-YYY-YYY.compute-1.amazonaws.com"
REMOTE_HOST="docdb-dima-1.cluster-xxxxxxxxxxxx.us-east-1.docdb.amazonaws.com"
LOCAL_PORT=27088
REMOTE_PORT=27017
START_TIME=$(date +%s)
# Start SSH tunnel in the background
ssh -i "$KEY" \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-L ${LOCAL_PORT}:${REMOTE_HOST}:${REMOTE_PORT} \
${USER}@${HOST} -N &
SSH_PID=$!
# Trap to clean up if script is terminated
trap "kill $SSH_PID 2>/dev/null" EXIT
# Monitor loop
while kill -0 $SSH_PID 2>/dev/null; do
NOW=$(date +%s)
ELAPSED_MIN=$(( (NOW - START_TIME) / 60 ))
printf "\r⏱️ SSH tunnel alive for %d minute(s)..." "$ELAPSED_MIN"
sleep 60
done
printf "\n❌ SSH tunnel disconnected after %d minute(s).\n" "$ELAPSED_MIN"
🔍 How It Works
Let’s break it down:
-
SSH Tunnel Setup: Uses
ssh -L
to forwardlocalhost:27088
to the remote DocumentDB endpoint via the bastion host. -
Keepalive Options:
-
ServerAliveInterval=60
: Sends a keepalive packet every 60 seconds. -
ServerAliveCountMax=3
: If three keepalives are missed, the connection is considered dead.
-
Background Process: The SSH tunnel runs in the background.
Monitoring Loop: Every minute, it checks if the SSH process is still alive using
kill -0 $SSH_PID
.Cleanup Trap: If the script is interrupted (Ctrl+C or killed), it cleans up the SSH process to avoid zombie tunnels.
✅ When to Use This
- Developing with services behind a bastion host.
- Need a quick local connection to remote databases.
- Want visibility into tunnel uptime without heavy tooling.
- Avoiding tools like
autossh
or systemd for simpler setups.
🧹 Final Thoughts
This script might look simple, but it has saved me countless hours of wondering “is my tunnel still alive?” during long development or migration sessions.
Top comments (1)
I run into disconnected tunnels all the time. This looks to be super helpful - thanks!