Skip to content

Instantly share code, notes, and snippets.

@nmicic
Last active September 4, 2026 07:53
Show Gist options
  • Select an option

  • Save nmicic/19518764ffdbb4d0715e6b48d7abfc75 to your computer and use it in GitHub Desktop.

Select an option

Save nmicic/19518764ffdbb4d0715e6b48d7abfc75 to your computer and use it in GitHub Desktop.
SSH ControlMaster & Watchdog

SSH ControlMaster & Watchdog

A simple setup for SSH connection multiplexing using ControlMaster, plus a watchdog script that checks the master connection and restarts it when needed.

Useful when you frequently create short-lived SSH sessions but want them to reuse one long-lived underlying SSH connection.

1. SSH config

Add to:

~/.ssh/config

Host server.example.com
    User myuser

    ControlMaster auto
    ControlPath ~/.ssh/cm-%C
    ControlPersist 8h

    ServerAliveInterval 30
    ServerAliveCountMax 3

For multiple servers:

Host server1.example.com server2.example.com server3.example.com
    User myuser

    ControlMaster auto
    ControlPath ~/.ssh/cm-%C
    ControlPersist 8h

    ServerAliveInterval 30
    ServerAliveCountMax 3

Each destination gets its own ControlMaster connection/socket.

What these options do

  • ControlMaster auto — reuse an existing master connection when possible.
  • ControlPath ~/.ssh/cm-%C — store the multiplexing socket using SSH's hashed connection identifier.
  • ControlPersist 8h — keep the master alive after the last SSH session exits.
  • ServerAliveInterval 30 — send a keep-alive message every 30 seconds when needed.
  • ServerAliveCountMax 3 — consider the connection dead after 3 unanswered keep-alives.

2. Start a ControlMaster manually

Start a background master connection:

ssh -MNf myuser@server.example.com

Options:

  • -M — explicitly request a master connection.
  • -N — don't execute a remote command.
  • -f — move SSH into the background after authentication.

Check it:

ssh -O check myuser@server.example.com

Example:

Master running (pid=48217)

Stop it gracefully:

ssh -O exit myuser@server.example.com

3. ControlMaster watchdog

Save as:

ssh-watchdog.sh

#!/bin/sh

HOST="server.example.com"
SSH_USER="myuser"
DELAY=10

TARGET="${SSH_USER}@${HOST}"

while true; do
    STATUS=$(ssh -O check "$TARGET" 2>&1)

    if [ $? -eq 0 ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - $TARGET - $STATUS"
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') - $TARGET - ControlMaster down - starting..."

        if ssh -MNf "$TARGET"; then
            STATUS=$(ssh -O check "$TARGET" 2>&1)
            echo "$(date '+%Y-%m-%d %H:%M:%S') - $TARGET - $STATUS"
        else
            echo "$(date '+%Y-%m-%d %H:%M:%S') - $TARGET - Failed to start ControlMaster"
        fi
    fi

    sleep "$DELAY"
done

Make it executable:

chmod +x ssh-watchdog.sh

Run it:

./ssh-watchdog.sh

Example output:

2026-09-04 09:18:01 - myuser@server.example.com - Master running (pid=48217)
2026-09-04 09:18:11 - myuser@server.example.com - Master running (pid=48217)
2026-09-04 09:18:21 - myuser@server.example.com - Master running (pid=48217)

If the master dies:

2026-09-04 09:18:31 - myuser@server.example.com - ControlMaster down - starting...
2026-09-04 09:18:32 - myuser@server.example.com - Master running (pid=49103)

The PID change makes it easy to see that a new underlying SSH master connection was created.

For normal use, a longer check interval is usually enough:

DELAY=30

or:

DELAY=60

4. Verify the effective SSH configuration

SSH can show the configuration it resolved for a host:

ssh -G myuser@server.example.com | grep -Ei '^(user|controlmaster|controlpath|controlpersist|serveralive)'

Example:

user myuser
controlmaster auto
controlpath /home/myuser/.ssh/cm-...
controlpersist 28800
serveralivecountmax 3
serveraliveinterval 30

5. Test connection reuse

Start the master:

ssh -MNf myuser@server.example.com

Check its PID:

ssh -O check myuser@server.example.com

Example:

Master running (pid=48217)

Now create several sessions:

ssh myuser@server.example.com
ssh myuser@server.example.com "hostname"
ssh myuser@server.example.com "uptime"

Or copy a file:

scp file.txt myuser@server.example.com:/tmp/

Check again:

ssh -O check myuser@server.example.com

You should still see the same master PID:

Master running (pid=48217)

The individual SSH commands are separate logical sessions, but they reuse the same underlying SSH connection.

Why this is interesting

SSH ControlMaster has existed for a long time, but it is easy to overlook.

It is especially useful for tools and agents that create many short-lived SSH sessions. Instead of creating a new TCP connection, SSH handshake, and authentication for every command, multiple sessions can be multiplexed over one existing SSH connection.

This is also useful to remember from a security perspective.

Many short-lived SSH commands do not necessarily mean many new network connections or SSH authentications. With ControlMaster enabled, they may all be logical sessions multiplexed over one long-lived SSH connection.

Quick reference

Start:

ssh -MNf myuser@server.example.com

Check:

ssh -O check myuser@server.example.com

Connect normally:

ssh myuser@server.example.com

Run a command:

ssh myuser@server.example.com "uptime"

Stop the master:

ssh -O exit myuser@server.example.com

Inspect effective configuration:

ssh -G myuser@server.example.com | grep -Ei '^(user|controlmaster|controlpath|controlpersist|serveralive)'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment