If you run WP-CLI over SSH many times in a row, you may notice that much of the delay comes before the command even starts. Each call opens a new TCP connection, negotiates keys and authenticates from scratch.
OpenSSH can reuse an established connection through ControlMaster. This article explains what happens under the hood, how to configure it, and what to watch for, with timings measured against a real server.
Note: I measured from a Mac (OpenSSH 10.2) to a hosting server in Japan by running
truerepeatedly. Numbers depend on your network and server, so treat them as one example.
What each SSH call does
Every ssh host command runs these steps again:
- Open a TCP connection
- Negotiate ciphers and exchange keys
- Verify the server and authenticate with a public key
- Open a session and run the command
Only step 4 is the work you care about. Steps 1–3 repeat every time, and in a script that calls SSH dozens of times they make up most of the waiting.
The idea behind ControlMaster
The first connection stays alive as a “master”. Later ssh commands attach their sessions to it instead of connecting again.
- The first
sshopens the connection and creates a control socket file locally - Later
sshcommands talk to that socket and add a session to the existing connection - No new key exchange or authentication takes place
Configuration
Add this to ~/.ssh/config per host. The host, user and key path are placeholders.
Host myserver
HostName example.com
User deploy
IdentityFile ~/.ssh/example_key
ControlMaster auto
ControlPath ~/.ssh/cm-%C
ControlPersist 60
- ControlMaster auto: become the master if no socket exists, otherwise attach to it
- ControlPath: where the socket lives;
%Cis a hash of the connection details, so each host gets its own short path - ControlPersist 60: keep the master running in the background for 60 seconds after the last session ends
ControlPersist matters. Without it the master exits with the first session, and the next command opens a fresh connection again. Socket paths also have an OS-specific length limit, which %C helps you stay under.
Measured results
I ran true five times in a row against the same server (single measurement):
| Condition | Total for 5 runs |
|---|---|
| No multiplexing | 2.64 s |
| Multiplexing (ControlPersist 30) | 0.83 s |
Only the first run actually opened a connection; the rest reused it.
For three runs of wp option get blogname:
| Condition | Total for 3 runs |
|---|---|
| No multiplexing | 3.36 s |
| Multiplexing | 2.64 s |
The gap is smaller because WP-CLI spends its own time starting PHP and loading WordPress. Multiplexing only removes connection setup; it does not change how long the command itself takes. The benefit is most visible with many short commands.
Checking and stopping the master
ssh -O check myserver
ssh -O exit myserver
I stopped the master with -O exit after each measurement.
Things to watch
- The socket file: whoever can use the socket can use that connection. Keep it in a directory only you can read and write, never a shared location.
- Stale sockets: after an abnormal exit a leftover socket can make connections fail; delete it and reconnect.
- Shared fate: if the master drops, the attached sessions drop with it. For long jobs or large transfers, a separate connection may be safer.
- Keep ControlPersist short: tens of seconds to a few minutes is plenty.
- After changing settings: a master started with old settings keeps them; stop it with
-O exitbefore testing.
Summary
- Every SSH call repeats TCP, key exchange and authentication.
- ControlMaster reuses the first connection for later calls.
ControlMaster auto,ControlPathandControlPersistin~/.ssh/configare the core settings.- Measured: 2.64 s to 0.83 s for five short commands, with a smaller gain for heavier commands like WP-CLI.
- Mind where the socket lives and what happens if the master drops.
If you manage several servers, first organize them as in Organizing hosts with ~/.ssh/config, then add multiplexing only to the hosts you connect to repeatedly.