Using SSH Multiplexing with ControlMaster in Automation
AI generated
$_
#!/
Bash · SSH · Deployment · Automation
SSH Multiplexing with ControlMaster
One open connection for many commands instead of a handshake per call

Anyone connecting via SSH to the same server ten or twenty times in a row inside a deployment script pays the full price of a TCP handshake, key exchange and authentication every single time. ControlMaster shares a single established SSH connection over a local Unix socket with every further call, making Bash automation noticeably faster.

15 min read ControlMaster · ControlPath · ControlPersist SSH · Deployment · Bash

1. The problem: every SSH connection costs a full handshake

A single ssh call goes through a TCP handshake, a Diffie-Hellman key exchange to negotiate the session encryption, and finally the actual authentication, whether by public key or password. On a local network that barely registers, but over an internet connection with noticeable latency, or against a server with a slow public key check, that overhead quickly adds up to several hundred milliseconds per connection.

In a deployment script that uploads application code, runs migrations, clears caches and restarts the service one after another, five separate ssh calls mean five full handshakes in a row, even though the network path, keys and target server never change between calls. That repeated negotiation is exactly what SSH multiplexing eliminates entirely.

2. ControlMaster, ControlPath and ControlPersist in the SSH configuration

The three relevant options typically live in ~/.ssh/config, but can also be set per call with -o. ControlMaster auto tells ssh to establish a master connection on the first connection to a host and to check for an existing master connection on every subsequent one. ControlPath sets where the Unix domain socket for that master connection is placed on the filesystem, and ControlPersist determines how long the master connection stays open after the last channel using it closes, before shutting down automatically.

Without ControlPersist, the master connection would close as soon as the first interactive session ends, even if a script wants to open another connection right after. With a value like ControlPersist 10m, the socket stays open in the background for ten minutes, with no visible active session, and serves any new connection during that window instantly.


# ~/.ssh/config
Host deploy-*
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%C
  ControlPersist 10m
  ServerAliveInterval 30
  ServerAliveCountMax 3

3. Speeding up repeated SSH calls in a deployment script

Once ControlMaster is set up in the configuration, the actual Bash script does not need to change at all: every ssh and scp call automatically uses the existing master connection, as long as ControlPath resolves to the same host, port and user. The first call in the script still establishes the connection from scratch, every further call in the same run benefits from the already open socket and skips the full handshake.

With several commands run in sequence, that difference becomes very noticeable: instead of negotiating keys and going through authentication again for each of five deployment steps, ssh simply routes the new session through the existing, already authenticated channel, often cutting the total runtime of a multi step deployment by more than half.


#!/usr/bin/env bash
set -euo pipefail

HOST="deploy-prod"

# Each call reuses the same multiplexed connection automatically
ssh "$HOST" "cd /var/www/app && git pull"
ssh "$HOST" "cd /var/www/app && composer install --no-dev"
ssh "$HOST" "cd /var/www/app && php artisan migrate --force"
ssh "$HOST" "sudo systemctl reload php-fpm"
scp deploy-manifest.json "$HOST:/var/www/app/deploy-manifest.json"

4. Controlling the master connection explicitly: check, close, renew

For long running automation it is worth controlling the state of the master connection explicitly instead of relying purely on ControlPersist. ssh -O check host reports whether an active master connection currently exists, without opening a new session itself, and ssh -O exit host deliberately closes an existing master connection, for example at the end of a deployment run, so a socket is not left open longer than necessary.

A robust deployment script therefore opens the master connection explicitly at the start with ssh -MNf host, runs its actual commands, and closes the connection again in a trap handler on exit, regardless of whether the script completes successfully or aborts with an error.


#!/usr/bin/env bash
set -euo pipefail

HOST="deploy-prod"

# -M: act as master, -N: no remote command, -f: go to background
ssh -MNf "$HOST"
trap 'ssh -O exit "$HOST" 2>/dev/null || true' EXIT

ssh "$HOST" "cd /var/www/app && git pull"
ssh "$HOST" "sudo systemctl reload php-fpm"

# Verify the master connection is still up before relying on it further
if ssh -O check "$HOST" 2>&1 | grep -q "Master running"; then
  echo "Multiplexed connection still active"
fi

5. Choosing ControlPath correctly: the path length limit and the %C hash

Unix domain sockets have a hard path length limit, typically just over a hundred characters on Linux. A ControlPath pattern like ~/.ssh/sockets/%h-%p-%r, which embeds hostname, port and username in plain text, can exceed that limit with long hostnames or deeply nested home directories, and ssh then silently falls back to a normal, non multiplexed connection without warning.

The %C token solves this by hashing host, port and username into a single, short value, so the resulting socket path always stays the same length regardless of hostname length. The socket directory should also exist and carry tight permissions before ssh uses it for the first time.


#!/usr/bin/env bash
set -euo pipefail

# Ensure the socket directory exists with tight permissions before ssh uses it
mkdir -p -m 700 ~/.ssh/sockets

# %C is a hash of host, port and user -- always short, avoids the
# ~104 byte Unix domain socket path length limit that %h-%p-%r can hit
grep -q 'ControlPath ~/.ssh/sockets/%C' ~/.ssh/config || \
  echo 'ControlPath ~/.ssh/sockets/%C' >> ~/.ssh/config

6. Security implications of a shared socket

The Unix domain socket through which ControlMaster shares the connection is, in effect, direct access to an already authenticated SSH session. Any local process with read and write permission on that socket can open a new session through the existing connection without presenting a key or password of its own. On a multi user system with sloppy directory permissions that is a real risk: another local user with access to the socket can impersonate the owner of the master connection on the target server.

The socket directory should therefore consistently be created with mode 700, so only the owning user has access, and should never sit in a path readable by others, such as a shared /tmp without restrictive permissions. Anyone using ControlMaster on a CI runner or a shared build server should additionally make sure every job runs in its own, isolated home directory, so sockets from different jobs can never overlap.

7. Error handling: orphaned sockets and dropped connections

If a script aborts abnormally, for example via kill -9 or a hard system crash, the socket file entry can survive even though the actual ssh master process is long gone. A subsequent ssh call first tries to use that orphaned socket, fails, and depending on the ssh version must detect on its own that a new master connection is required.

A robust Bash wrapper script therefore explicitly checks with ssh -O check before critical commands whether the master connection actually works, and manually cleans up the stale socket path with rm -f on a failed check before attempting a new connection. That prevents a silent fallback to single connections from quietly eating away the expected speed benefits.

8. Multiplexing also benefits scp, rsync and Git over SSH

ControlMaster's speed benefit is not limited to plain ssh calls. Any tool that uses ssh as transport internally, for example scp, rsync with the -e ssh option, or a git remote over an ssh:// URL, automatically benefits from the same master connection, as soon as the matching Host pattern in ~/.ssh/config applies, without those tools needing to know anything about multiplexing themselves.

Especially with a backup routine that syncs many small files individually through repeated rsync calls, or a deployment script that alternates between git pull over SSH and individual ssh commands on the same server, the saved handshakes add up across many calls into a noticeable time savings for the entire script run.


#!/usr/bin/env bash
set -euo pipefail

HOST="deploy-prod"

# rsync automatically reuses the multiplexed SSH connection to $HOST,
# as long as the same Host pattern in ~/.ssh/config applies here too
rsync -az -e ssh ./build/ "$HOST:/var/www/app/current/"

# git over ssh:// benefits the same way, no extra flags needed
git -C /var/www/app pull "ssh://$HOST/var/www/app.git"

9. When ControlMaster pays off and what the alternatives are

SSH multiplexing pays off above all when a script talks to the same target server multiple times in a row, for example in multi step deployments, backup routines with many small file transfers, or monitoring scripts that poll status regularly. For one off, infrequent connections, the extra configuration brings little benefit.

Approach Handshake per command Configuration effort Typical use
No multiplexing Yes, on every call None One off, infrequent SSH connections
ControlMaster + ControlPersist No, after the first call Low, once in ssh_config Deployment scripts with many commands per run
ssh-agent Yes, but without re-entering a password Low Interactive sessions, public key without passphrase prompts
Ansible with SSH pipelining No, uses multiplexing internally Moderate, Ansible configuration Configuration management across many hosts

Mironsoft

Shell automation, DevOps tooling and deployment infrastructure

Shell scripts that hold up in production?

We review existing Bash scripts, spot fragile patterns and replace them with robust Bash patterns: complete error handling, logging and safe parallelization for your deployment stack.

Code Review

ShellCheck analysis and manual review for critical Bash pattern violations.

Refactoring

Retrofitting error handling, logging and safe file operations.

CI Integration

Wiring ShellCheck and BATS into pipelines and building regression tests.

10. Summary

SSH Multiplexing in Bash: The Essentials at a Glance

Core idea

ControlMaster shares a single established SSH connection over a Unix socket with every further call to the same host.

Configuration

ControlMaster auto, ControlPath ~/.ssh/sockets/%C and ControlPersist 10m in ~/.ssh/config cover most cases.

Security

The socket directory must have mode 700, otherwise any local user with access can reuse the existing session.

Error handling

ssh -O check verifies the master connection, ssh -O exit closes it deliberately and prevents orphaned sockets.

11. FAQ: SSH Multiplexing in Bash: The Essentials at a Glance

1What does ControlMaster do in the SSH configuration?
ControlMaster auto tells ssh to establish a master connection on the first connection to a host and share every further connection through that one connection instead of negotiating again each time.
2What exactly does ControlPath do?
ControlPath sets the filesystem path of the Unix domain socket through which the master connection is shared. %C turns that into a short hash of host, port and user.
3What happens without ControlPersist?
Without ControlPersist the master connection closes as soon as the first session ends. With ControlPersist 10m it stays open in the background for ten minutes and serves new connections instantly.
4Do I need to change my deployment script for ControlMaster?
No. Once the configuration is in ~/.ssh/config, every ssh and scp call automatically uses the master connection, as long as host, port and user match.
5How do I check whether a master connection is active?
With ssh -O check host. The command reports whether a running master connection exists without opening a new session itself.
6How do I deliberately close a master connection?
With ssh -O exit host. That closes the shared connection cleanly, which is recommended at the end of a deployment run.
7Why use %C instead of %h-%p-%r in ControlPath?
Unix domain sockets have a hard path length limit. %C produces a short hash and stays within that limit regardless of hostname length.
8What security risk does a shared socket introduce?
Any local process with access to the socket can open a new session through the existing, already authenticated connection. The socket directory must therefore have mode 700 and belong only to the owning user.
9What do I do about an orphaned socket after a crash?
Check with ssh -O check before critical commands whether the master connection works, and manually remove the stale socket path with rm -f on a failed check before attempting a new connection.
10Is ControlMaster worth it for a script that connects only once?
Barely. The speed benefit only appears from the second call to the same host onward. For one off connections the extra configuration brings no measurable benefit.