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 |
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=22SSH 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 = 30SSH 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_keySSH 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'See also: Ansible Development: Write Custom Modules, Plugins & Collections
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=ignoreWinRM 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=credsspPSRP Connection (Modern Alternative)
[windows:vars]
ansible_connection=psrp
ansible_psrp_auth=ntlm
ansible_psrp_cert_validation=ignorePSRP (PowerShell Remoting Protocol) is faster than WinRM for large payloads and supports more authentication options.
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=localCommon 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.confPodman Connection
[containers]
my_pod ansible_connection=containers.podman.podmanSee also: Configure WSL in a Domain Environment: Step-by-Step Guide
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 -hNetwork 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: confighttpapi — 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_keySee also: Four Methods to Configure Maximum PowerShell Memory in Windows Server
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: dockerChoosing 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 → localPerformance 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 5Troubleshooting
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 -vvvvWinRM: "Connection refused"
# Check WinRM listener on Windows
winrm enumerate winrm/config/Listener
# Test from Linux
curl -k https://192.168.1.20:5986/wsmanNetwork CLI: "Timeout"
# Increase timeouts
ansible_command_timeout: 60
ansible_connect_timeout: 30
# Or in ansible.cfg
[persistent_connection]
connect_timeout = 60
command_timeout = 60FAQ
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
Category: installation