AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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 TroubleshootingAnsible Performance TuningAnsible for KubernetesAnsible for Windows Automation

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home