I had a simple Ansible playbook.
Just 8 small tasks: check files, remove a user, verify results etc.
Nothing complicated.
Running it on 50 servers, I expected it to finish in about 10 minutes.
Instead, it took almost 1 hour.
No errors.
No network issues.
Just painfully slow execution.
After digging into logs and SSH debugging, I discovered the real reason:
Ansible was opening a new SSH connection for almost every task.
Table of Contents
What Ansible Actually Does With SSH
Let’s see how Ansible works first.
See also: Mastering the Linux Command Line — Your Complete Free Training Guide
Ansible works over SSH.
For each server, it connects via SSH and runs commands remotely.
But here’s the detail many people overlook:
Ansible tasks run independently.
If SSH connection reuse isn’t enabled, the workflow looks like this:
Task 1 → SSH connect → run command → close connection
Task 2 → SSH connect → run command → close connection
Task 3 → SSH connect → run command → close connection
Now imagine this at scale.
50 servers × 8 tasks = 400 SSH connections
Each connection requires:
- TCP handshake
- SSH authentication
- process startup
- network round-trip
Even if each connection only takes seconds, it quickly adds up.
In larger environments it becomes extreme:
100 servers × 10 tasks = 1000 SSH handshakes
Instead of executing tasks efficiently, the system spends most of its time rebuilding connections.
The mistake: Only Configuring ControlPath
Fortunately, the solution is surprisingly simple.
Instead of opening a new SSH connection for every task, you can reuse an existing connection.
This technique is called:
SSH connection multiplexing.
It allows multiple SSH sessions to share a single TCP connection.
For Ansible playbooks, this can dramatically reduce execution time.
Then, I thought I already solved the problem.
My inventory included this configuration:
ansible_ssh_common_args='-o ControlPath=/tmp/ssh-control-%h-%p-%r'
But performance didn’t change at all.
Why?
The Real Fix : ControlMaster + ControlPersist + ControlPath
There are three setting. ControlPath alone does nothing.
If you only configure ControlPath, you’re basically doing this:
Building a garage but never parking the car in it.
SSH connection multiplexing simply never activates.
To reuse SSH connections, all three options must work together:
The ansible.cfg Approach (Best Practice)
How to configure?
Create or edit the ansible.cfg file in your project root. Note the double percent signs (%%)—this is required for escaping in config files.
[defaults]
host_key_checking = False
timeout = 30
forks = 10
[ssh_connection]
# The magic happens here
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
pipelining = True
Cheatsheet Key parameters
| Parameter | Purpose | Recommended |
|---|---|---|
| ControlMaster | Enable connection reuse | auto |
| ControlPersist | Keep connection alive | 60–300s |
| ControlPath | Socket file path | /tmp/ansible-ssh-%h-%p-%r |
| pipelining | Reduce SSH operations | True |
Real Results: From 1 Hour → 15 Minutes
After applying the configuration, the difference was dramatic.
| Scenario | Before | After |
|---|---|---|
| 50 servers / 8 tasks | 60 minutes | 15 minutes |
| 100 servers / 10 tasks | 120 minutes | 25 minutes |
| SSH connections | 400 | 50 |
The playbook became roughly 8–10× faster.
How to Verify Connection Reuse
Before running your playbook, remove old socket files:
rm -f /tmp/ansible-ssh-*
Run the playbook:
ansible-playbook -i inventory playbook.yml
Then check the socket files:
ls /tmp/ansible-ssh-*
If connection reuse works correctly:
number of socket files = number of servers
Not:
servers × tasks
You can also monitor SSH connections:
watch -n 1 'netstat -an | grep :22 | grep ESTABLISHED | wc -l'
Summary
If your Ansible playbooks feel unusually slow, the problem may not be Ansible itself.
Often, it’s SSH connection issue.
Sometimes the biggest performance improvement comes from fixing just one hidden setting.




