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

ConnectionTargetProtocolUse Case
sshLinux/UnixSSHDefault for most hosts
paramikoLinux/UnixSSH (Python)Legacy or no OpenSSH
winrmWindowsWinRM/HTTPSWindows automation
psrpWindowsPowerShell RemotingModern Windows
localControllerNoneLocalhost tasks
dockerContainersDocker APIRunning containers
podmanContainersPodman APIPodman containers
kubectlKuberneteskubectlK8s pods
network_cliNetwork devicesSSH + CLICisco, Juniper, Arista
httpapiNetwork/APIHTTPS RESTModern network devices
netconfNetwork devicesNETCONF/SSHXML-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=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'

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=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.

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: 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 -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

See 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: 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.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home