Connection Plugins¶
What You'll Learn¶
- What a connection plugin does, and how Ansible picks one per host
- The plugins you'll actually use:
ssh,local,docker,winrm/psrp, and a few cloud ones - How pipelining and ControlPersist make SSH runs dramatically faster
- The
requirettycaveat that bites when you turn pipelining on
Why This Exists¶
ssh is the default and covers most cases, but Ansible's connection layer is pluggable — the same modules and JSON contract work over a completely different transport when the target isn't a normal SSH-reachable Linux box.
Mental Model¶
A connection plugin answers one question: how do bytes get to the target and back? It must be able to put a file, run a command, and fetch a file. Modules don't care which plugin carried them — a
copytask works the same over SSH, into a container, or over WinRM.
flowchart LR
C["Control node:<br>module + args"] --> P{"connection plugin"}
P -->|ssh| L["Linux host"]
P -->|local| S["The control node itself"]
P -->|community.docker.docker| D["Running container"]
P -->|winrm / psrp| W["Windows host"]
P -->|amazon.aws.aws_ssm| E["EC2 instance via SSM"]
Choosing a Plugin¶
Set it per host or group with ansible_connection, per play with connection:, or per task:
[web]
web01 ansible_host=10.0.1.11
[containers]
checkout-dev ansible_connection=community.docker.docker
[windows]
win-build01 ansible_host=10.0.3.20
[windows:vars]
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_port=5986
| Plugin | Use it when | Needs |
|---|---|---|
ssh (default) |
Linux/Unix hosts | OpenSSH client on the control node; see SSH and Connectivity |
local |
Tasks that should run on the control node itself (API calls, rendering files locally) | Nothing |
community.docker.docker |
Configuring or testing inside a running container, without SSH in the image | community.docker collection, Docker access |
kubernetes.core.kubectl |
Running tasks inside a pod | kubernetes.core collection, kubectl |
winrm / psrp |
Windows hosts over WinRM | pywinrm or pypsrp on the control node, WinRM configured on Windows |
amazon.aws.aws_ssm |
EC2 instances with no inbound SSH | amazon.aws collection, SSM agent, an S3 bucket for file transfer |
paramiko_ssh |
Rare cases where the OpenSSH binary can't be used | paramiko Python library |
Windows can also be managed over SSH (ansible_connection=ssh, ansible_shell_type=powershell) now that OpenSSH ships with Windows.
local: No Network Hop¶
- name: Talk to APIs from the control node
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Create a DNS record
community.general.cloudflare_dns:
zone: example.com
record: shop
type: A
value: 203.0.113.10
api_token: "{{ cloudflare_api_token }}"
For a single task inside a remote play, delegate_to: localhost is usually clearer; see Delegation and Become.
docker: Tasks Inside a Container¶
- name: Check a dev container's config without SSH
hosts: containers
gather_facts: false
tasks:
- name: Show the rendered app config
ansible.builtin.command: cat /app/config.yml
changed_when: false
Useful for container debugging and Molecule-style tests. Don't use it to configure production containers — rebuild the image instead.
Pipelining: The Biggest Free Speed-Up¶
By default, running a module over SSH is several round trips: create a temp directory, upload the module file, make it executable, run it, delete it. Pipelining sends the module straight into the remote Python interpreter's stdin, in one SSH operation.
On typical playbooks, this cuts the number of SSH operations per task sharply, and it also avoids the temporary-file permission problem when becoming a non-root user.
The requiretty caveat¶
Some older sudo configurations contain:
That tells sudo to refuse commands without a terminal, which pipelining doesn't allocate. Symptoms: become tasks fail with sudo errors right after enabling pipelining. Fix it on the managed nodes, ideally just for the automation user:
Modern RHEL, Debian, and Ubuntu defaults don't set requiretty. See Become and Permission Problems.
ControlPersist: Reuse One SSH Connection¶
Without multiplexing, every SSH operation repeats the key exchange and authentication. ControlPersist keeps a master connection open and reuses it:
[ssh_connection]
pipelining = True
ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
control_path_dir = ~/.ansible/cp
Ansible's default ssh_args already include ControlMaster=auto -o ControlPersist=60s. If you override ssh_args for another reason (a jump host, custom options), re-include these, or you silently lose multiplexing:
If you see ControlPath too long errors, shorten control_path_dir.
Measuring the Difference¶
Pair these settings with fork and strategy tuning from Forks, Serial, Strategy, and Throttle and the broader list in Performance.
Common Mistakes¶
- Not enabling pipelining, leaving real performance on the table for free.
- Overriding
ssh_argsfor a bastion and accidentally droppingControlMaster/ControlPersist. - Assuming
winrmneeds no extra setup — it requirespip install pywinrmand Windows-side configuration, unlike SSH which is on by default on most Linux distributions. - Using the short name
dockerfor the connection plugin after it moved tocommunity.docker, without the collection installed. - Forgetting
become: falseonconnection: localplays that inheritbecome: true, so the control node prompts for sudo.
Interview Questions¶
- What does pipelining change about how a module reaches the managed node, and what can break when you enable it?
- Why would you use the
dockerconnection plugin instead ofssh? - What does ControlPersist save, and how can a custom
ssh_argsaccidentally disable it? - How would you manage EC2 instances that have no inbound SSH port open?
Next¶
Continue to Lookup and Filter Plugins.