Ansible Connection Types: SSH, WinRM, Local, Docker (Complete Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
All Ansible connection types explained: SSH, WinRM, local, Docker, network_cli, httpapi. Configure connection plugins for Linux, Windows, containers.
Ansible connects to managed hosts through connection plugins. SSH is the default, but there are 10+ connection types for Windows, containers, network devices, cloud APIs, and local execution. Here's how each one works and when to use it.
Connection Types Overview
| Connection | Target | Protocol | Use Case |
|-----------|--------|----------|----------|
| ssh | Linux/Unix | SSH | Default for most hosts |
| paramiko | Linux/Unix | SSH (Python) | Legacy or no OpenSSH |
| winrm | Windows | WinRM/HTTPS | Windows automation |
| psrp | Windows | PowerShell Remoting | Modern Windows |
| local | Controller | None | Localhost tasks |
| docker | Containers | Docker API | Running containers |
| podman | Containers | Podman API | Podman containers |
| kubectl | Kubernetes | kubectl | K8s pods |
| network_cli | Network devices | SSH + CLI | Cisco, Juniper, Arista |
| httpapi | Network/API | HTTPS REST | Modern network devices |
| netconf | Network devices | NETCONF/SSH | XML-based config |
See also: Ansible Development: Write Custom Modules, Plugins & Collections
SSH Connection (Default)
Basic Configuration
# ansible.cfg
[defaults]
remote_user = deploy
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
# inventory
[web_servers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
[web_servers:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key
ansible_port=22
SSH Multiplexing (Performance)
SSH multiplexing reuses connections across tasks — critical for performance:
# ansible.cfg
[ssh_connection]
# Enable connection sharing
ssh_args = -o ControlMaster=auto -o ControlPersist=600s -o PreferredAuthentications=publickey
# Enable pipelining (reduces SSH operations per task)
pipelining = True
# Connection timeout
timeout = 30
SSH with Jump Host (Bastion)
# inventory
[private_servers]
db1 ansible_host=10.0.1.50
[private_servers:vars]
ansible_ssh_common_args='-o ProxyJump=bastion@jump.example.com'
Or with SSH config:
# ~/.ssh/config
Host bastion
HostName jump.example.com
User bastion
IdentityFile ~/.ssh/bastion_key
Host 10.0.1.*
ProxyJump bastion
User deploy
IdentityFile ~/.ssh/deploy_key
SSH Key vs Password
# Key-based (recommended)
[servers:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key
# Password-based (use ansible-vault for the password)
[servers:vars]
ansible_user=admin
ansible_password="{{ vault_ssh_password }}"
ansible_ssh_common_args='-o PubkeyAuthentication=no'
WinRM Connection (Windows)
Windows Host Setup
# On Windows target — enable WinRM
winrm quickconfig -q
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
# For HTTPS (recommended for production)
$cert = New-SelfSignedCertificate -DnsName "server.example.com" -CertStoreLocation "cert:\LocalMachine\My"
winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"server.example.com`";CertificateThumbprint=`"$($cert.Thumbprint)`"}"
Ansible Inventory for Windows
# inventory
[windows]
win1 ansible_host=192.168.1.20
win2 ansible_host=192.168.1.21
[windows:vars]
ansible_connection=winrm
ansible_user=Administrator
ansible_password="{{ vault_win_password }}"
ansible_winrm_transport=ntlm
ansible_port=5986
ansible_winrm_server_cert_validation=ignore
WinRM Transport Options
# Basic auth (HTTP — lab only)
ansible_winrm_transport=basic
ansible_port=5985
# NTLM (most common)
ansible_winrm_transport=ntlm
ansible_port=5986
# Kerberos (Active Directory)
ansible_winrm_transport=kerberos
ansible_winrm_kerberos_delegation=true
# CredSSP (for double-hop)
ansible_winrm_transport=credssp
PSRP Connection (Modern Alternative)
[windows:vars]
ansible_connection=psrp
ansible_psrp_auth=ntlm
ansible_psrp_cert_validation=ignore
PSRP (PowerShell Remoting Protocol) is faster than WinRM for large payloads and supports more authentication options.
See also: Configure WSL in a Domain Environment: Step-by-Step Guide
Local Connection
Run tasks on the Ansible controller itself:
---
- name: Local operations
hosts: localhost
connection: local
tasks:
- name: Create local directory
ansible.builtin.file:
path: /tmp/output
state: directory
- name: Call API
ansible.builtin.uri:
url: https://api.example.com/deploy
method: POST
body_format: json
body:
version: "{{ app_version }}"
Or per-host in inventory:
[local]
localhost ansible_connection=local
Common use cases: • API calls (cloud providers, webhooks) • Terraform/kubectl commands • File generation • Database queries
Docker Connection
Execute inside running containers:
# inventory
[containers]
my_app ansible_connection=docker ansible_docker_extra_args="--tls"
web_container ansible_connection=docker
# Or community.docker.docker_containers inventory plugin
---
- name: Configure running container
hosts: my_app
connection: docker
tasks:
- name: Install package in container
ansible.builtin.raw: apt-get update && apt-get install -y curl
- name: Copy config into container
ansible.builtin.copy:
src: app.conf
dest: /etc/app/app.conf
Podman Connection
[containers]
my_pod ansible_connection=containers.podman.podman
See also: Four Methods to Configure Maximum PowerShell Memory in Windows Server
Kubectl Connection
Execute inside Kubernetes pods:
[pods]
my-pod ansible_connection=kubernetes.core.kubectl
my-pod ansible_kubectl_namespace=production
my-pod ansible_kubectl_pod=web-app-abc123
my-pod ansible_kubectl_container=app
---
- name: Debug pod
hosts: my-pod
connection: kubernetes.core.kubectl
tasks:
- name: Check process list
ansible.builtin.command: ps aux
register: processes
- name: Show disk usage
ansible.builtin.command: df -h
Network Device Connections
network_cli — CLI over SSH
For traditional network devices (Cisco IOS, Juniper JUNOS, Arista EOS):
# inventory
[switches]
sw1 ansible_host=192.168.1.100
[switches:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=cisco.ios.ios
ansible_user=admin
ansible_password="{{ vault_network_password }}"
ansible_become=true
ansible_become_method=enable
ansible_become_password="{{ vault_enable_password }}"
---
- name: Configure switch
hosts: switches
gather_facts: false
tasks:
- name: Configure interface
cisco.ios.ios_interfaces:
config:
- name: GigabitEthernet0/1
description: "Uplink to core"
enabled: true
state: merged
- name: Show running config
cisco.ios.ios_command:
commands:
- show running-config
register: config
httpapi — REST API
For modern network devices with REST APIs:
[firewalls]
fw1 ansible_host=192.168.1.200
[firewalls:vars]
ansible_connection=ansible.netcommon.httpapi
ansible_network_os=fortinet.fortios.fortios
ansible_httpapi_use_ssl=true
ansible_httpapi_validate_certs=false
ansible_user=admin
ansible_password="{{ vault_fw_password }}"
netconf — XML Configuration
For devices supporting NETCONF (Juniper, some Cisco):
[routers]
r1 ansible_host=192.168.1.150
[routers:vars]
ansible_connection=ansible.netcommon.netconf
ansible_network_os=junipernetworks.junos.junos
ansible_user=admin
ansible_ssh_private_key_file=~/.ssh/network_key
Per-Task Connection Override
---
- name: Mixed connection playbook
hosts: web_servers
tasks:
- name: Configure remote server (SSH)
ansible.builtin.apt:
name: nginx
state: present
- name: Update load balancer via API (local)
ansible.builtin.uri:
url: https://lb.example.com/api/backends
method: POST
body_format: json
body:
server: "{{ inventory_hostname }}"
delegate_to: localhost
connection: local
- name: Run command in docker sidecar
ansible.builtin.command: health-check
delegate_to: sidecar-container
connection: docker
Choosing the Right Connection
What are you connecting to?
├── Linux/Unix server → ssh (default)
│ └── No OpenSSH available? → paramiko
├── Windows server
│ ├── Standard WinRM → winrm
│ └── Modern/faster → psrp
├── Container
│ ├── Docker → docker
│ ├── Podman → containers.podman.podman
│ └── K8s pod → kubernetes.core.kubectl
├── Network device
│ ├── CLI-based (IOS, EOS, JUNOS) → network_cli
│ ├── REST API (FortiOS, NXOS) → httpapi
│ └── NETCONF support → netconf
└── Local machine → local
Performance Tuning per Connection
# ansible.cfg
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=600s
[persistent_connection]
# For network_cli, httpapi, netconf
connect_timeout = 30
command_timeout = 30
# Increase parallelism
# ansible.cfg
[defaults]
forks = 20 # Default is 5
Troubleshooting
SSH: "Permission denied"
# Test SSH manually
ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no deploy@192.168.1.10
# Verbose Ansible output
ansible web1 -m ping -vvvv
WinRM: "Connection refused"
# Check WinRM listener on Windows
winrm enumerate winrm/config/Listener
# Test from Linux
curl -k https://192.168.1.20:5986/wsman
Network CLI: "Timeout"
# Increase timeouts
ansible_command_timeout: 60
ansible_connect_timeout: 30
# Or in ansible.cfg
[persistent_connection]
connect_timeout = 60
command_timeout = 60
FAQ
What's the difference between ssh and paramiko?
ssh uses the system's OpenSSH client (faster, supports ControlMaster multiplexing). paramiko is a pure Python SSH implementation (no system dependency, but slower). Use ssh unless you're on a system without OpenSSH.
Can I use different connections for different hosts in the same playbook?
Yes. Set ansible_connection per host or group in your inventory. Ansible handles the connection type per host automatically.
Is WinRM secure enough for production?
Use WinRM over HTTPS (port 5986) with certificate validation in production. Basic auth over HTTP is only acceptable in isolated labs. Kerberos or CredSSP provide the strongest authentication for Active Directory environments.
How does pipelining improve SSH performance?
Without pipelining, Ansible copies a module to the remote host, then executes it — two SSH operations per task. With pipelining, it pipes the module directly through SSH stdin — one operation per task. This roughly halves SSH overhead.
Can I write a custom connection plugin?
Yes. Connection plugins implement connect(), exec_command(), put_file(), and fetch_file(). Place them in connection_plugins/ in your project. This is useful for proprietary protocols or custom APIs.
Conclusion
SSH handles 80% of Ansible use cases. Add WinRM/PSRP for Windows, network_cli for switches and routers, docker/kubectl for containers, and local for API calls. Set the connection type per host in inventory and Ansible handles the rest — your playbooks don't need to change regardless of how they connect.
Related Articles
• Ansible Failed to Connect via SSH Troubleshooting • Ansible Performance Tuning • Ansible for Kubernetes • Ansible for Windows AutomationCategory: installation