# Migrating to ssh\_type=libssh for ansible.netcommon.network\_cli across Ansible Networking Collections

**URL:** <https://forum.ansible.com/t/migrating-to-ssh-type-libssh-for-ansible-netcommon-network-cli-across-ansible-networking-collections/45780>\
**Category:** Guides, FAQs & Howtos\
**Tags:** libssh, paramiko, network, galaxy-ng\
**Created:** [May 7, 2026, 11:51am UTC](https://forum.ansible.com/t/migrating-to-ssh-type-libssh-for-ansible-netcommon-network-cli-across-ansible-networking-collections/45780 "2026-05-07T11:51:59Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![komal](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/komal/32/9265_2.png) [@komal](https://forum.ansible.com/u/komal)\
**Post date:** [May 7, 2026, 11:51am UTC](https://forum.ansible.com/t/migrating-to-ssh-type-libssh-for-ansible-netcommon-network-cli-across-ansible-networking-collections/45780/1 "2026-05-07T11:51:59Z")

</div>

## Why libssh

Paramiko is on a long-term deprecation path in the Ansible ecosystem. ansible-pylibssh (libssh) will be the supported SSH backend for network automation going forward, offering better security, maintenance, and feature development. Paramiko has been marked deprecated in ansible.netcommon v8.5.0 and will be removed in a release after 2028-02-01. All network collections should migrate to libssh.

The ssh\_type=libssh setting applies to the ansible.netcommon.network\_cli connection plugin, which is the primary SSH-based connection used by Ansible network collections.

## Prerequisites

### Install ansible-pylibssh

```auto

pip install ansible-pylibssh>=1.4.0

```

### Verify installation

```auto
pip list | grep -i pylibssh

python3 -c "import pylibsshext; print(pylibsshext. __version__ )"

```

### Required collection versions

| Collection | Minimum Version | Notes |
| --- | --- | --- |
| ansible.netcommon | \>= 6.0.0 | Full libssh support; v8.5.0+ recommended |
| ansible-pylibssh | \>= 1.4.0 | Required by netcommon v8.5.0+ |

## How ssh\_type Works

The ssh\_type option controls which Python SSH library the ansible.netcommon.network\_cli connection plugin uses to connect to network devices. There are three choices:

| Value | Behavior |
| --- | --- |
| auto | Default. Uses libssh if ansible-pylibssh is installed, falls back to paramiko |
| libssh | Explicitly use libssh. Fails if ansible-pylibssh is not installed |
| paramiko | Use paramiko (deprecated, removal after 2028-02-01) |

## Ways to Set ssh\_type=libssh

The following methods configure ssh\_type=libssh for the ansible.netcommon.network\_cli connection plugin. Pick the one that fits your deployment.

### 1. Environment variable (global)

`export ANSIBLE_NETWORK_CLI_SSH_TYPE=libssh`

### 2. ansible.cfg (project-level)

```auto
[persistent_connection]

ssh_type = libssh

```

### 3. Inventory variable (per-host or per-group)

```auto
[all:vars]

ansible_network_cli_ssh_type=libssh

```

### 4. Playbook variable (per-play or per-task)

```auto
vars:
  ansible_network_cli_ssh_type: libssh

```

## Collection Examples

* * *

### Cisco IOS (cisco.ios)

#### Inventory (inventory\_ios.ini)

```auto
[ios_devices]
ios-router ansible_host=192.168.1.10

[ios_devices:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=cisco.ios.ios
ansible_user=admin
ansible_password=secret
ansible_network_cli_ssh_type=libssh

```

#### Playbook: Gather facts and push config

```auto
---
- name: Cisco IOS - libssh examples
  hosts: ios_devices
  gather_facts: false

  tasks:
    - name: Gather IOS facts
      cisco.ios.ios_facts:
        gather_subset: all

    - name: Show version
      cisco.ios.ios_command:
        commands:
          - show version
          - show ip interface brief
      register: output

    - name: Display output
      ansible.builtin.debug:
        var: output.stdout_lines

    - name: Configure logging
      cisco.ios.ios_config:
        lines:
          - logging buffered 8192
          - logging console informational
        save_when: modified

    - name: Configure interfaces using resource module
      cisco.ios.ios_l3_interfaces:
        config:
          - name: Loopback100
            ipv4:
              - address: 10.100.0.1/32
        state: merged

```

#### Run it

```auto
ansible-playbook -i inventory_ios.ini ios_libssh_examples.yaml -vvv

```

* * *

## File Transfer with libssh (net\_put / net\_get)

File transfer operations use the SSH transport directly. When using libssh, the underlying SFTP/SCP implementation comes from ansible-pylibssh.

#### Upload a file to a device

```auto
---
- name: File transfer with libssh
  hosts: ios_devices
  gather_facts: false
  vars:
    ansible_network_cli_ssh_type: libssh

  tasks:
    - name: Upload file to device (SCP)
      ansible.netcommon.net_put:
        src: ./configs/running_backup.cfg
        dest: flash:running_backup.cfg
        protocol: scp

    - name: Upload file to device (SFTP)
      ansible.netcommon.net_put:
        src: ./configs/startup_template.cfg
        dest: flash:startup_template.cfg
        protocol: sftp

```

#### Download a file from a device

```auto
- name: Download file from device
      ansible.netcommon.net_get:
        src: running-config
        dest: ./backups/{{ inventory_hostname }}_backup.cfg
        protocol: scp

```

Important: File transfer behavior can differ by device, OS image, and protocol (SCP vs SFTP). Test on your platforms after switching from paramiko. Retest whenever you upgrade ansible.netcommon, ansible-pylibssh, or your network collections.

* * *

## Using ansible.netcommon Modules Directly with libssh

These modules work across all network OSes when ssh\_type=libssh is set on the ansible.netcommon.network\_cli connection.

```auto
---
- name: Generic netcommon modules with libssh
  hosts: all
  gather_facts: false
  vars:
    ansible_network_cli_ssh_type: libssh

  tasks:
    - name: Run arbitrary CLI command
      ansible.netcommon.cli_command:
        command: show version
      register: version_output

    - name: Display version
      ansible.builtin.debug:
        var: version_output.stdout

    - name: Push config lines
      ansible.netcommon.cli_config:
        config: |
          interface Loopback99
           description Configured via libssh

    - name: Network reachability test
      ansible.netcommon.net_ping:
        dest: 8.8.8.8
        count: 3
      register: ping_result
      ignore_errors: true

```

## Migrating Variables: Paramiko / SSH to libssh

When switching from paramiko (or OpenSSH-based connections) to libssh, several connection variables must be updated. libssh uses its own namespace (ansible\_libssh\_\*) and does not inherit paramiko or OpenSSH variables. If you skip this step, your existing authentication, host key, and proxy settings will be silently ignored — connections may fail or fall back to unexpected defaults with no warning.

### Variables that MUST change

These variables are not shared across SSH backends. If you used them with paramiko or OpenSSH, you must replace them with the libssh equivalents.

| Setting | Paramiko / OpenSSH variable | libssh variable | What changed | How to verify |
| --- | --- | --- | --- | --- |
| Private key file | ansible\_ssh\_private\_key\_file or ansible\_paramiko\_private\_key\_file | ansible\_libssh\_private\_key\_file | libssh does not read the old variable at all. If only the old variable is set, libssh ignores your key and falls back to password auth or fails. | Run a playbook with -vvvv and confirm the log shows Loading private key from \<your\_key\_path\>. If you see password authentication instead, the key variable is not being picked up. |
| Host key checking | ansible\_host\_key\_checking or ansible\_paramiko\_host\_key\_auto\_add | ansible\_libssh\_host\_key\_checking | Paramiko’s host\_key\_auto\_add=true silently accepted and saved unknown host keys to known hosts. libssh’s host\_key\_checking=false skips verification entirely but does not save keys. In production, set to true and pre-populate your known hosts file. | Connect to a device not in ~/.ssh/known\_hosts. With host\_key\_checking=true, the connection should fail. With host\_key\_checking=false, it should succeed (lab only). |
| Proxy / jump host | ansible\_ssh\_common\_args (e.g., -o ProxyCommand=“ssh -W %h:%p bastion”) | ansible\_libssh\_proxy\_command | This is the most common migration pitfall. ansible\_ssh\_common\_args is OpenSSH-specific and is completely ignored by libssh. Also, drop the -o ProxyCommand= wrapper — pass the command string directly. | Connect to a device behind a bastion host. With -vvvv, confirm the log shows the proxy command being invoked. If the connection goes direct (bypassing the bastion), the old variable is still in use. |
| SSH config file | ~/.ssh/config (auto-loaded by OpenSSH) | ansible\_libssh\_config\_file | OpenSSH automatically reads ~/.ssh/config for host aliases, identity files, and proxy settings. libssh does not auto-load it — you must point to it explicitly. | If you rely on ~/.ssh/config, set ansible\_libssh\_config\_file=~/.ssh/config and verify the connection picks up settings defined there (e.g., a host alias or identity file). |
| Auto-discover keys | ansible\_paramiko\_look\_for\_keys | No equivalent | Paramiko could automatically search ~/.ssh/ for usable private keys. libssh does not do this. You must specify your key explicitly. | Unset ansible\_libssh\_private\_key\_file and confirm that libssh does not magically find your key. Then set it explicitly and confirm authentication succeeds. |

### Variables that stay the same (no change needed)

These variables are shared across all SSH backends (paramiko, libssh, OpenSSH). They work as-is after migration.

| Setting | Variable | Notes |
| --- | --- | --- |
| SSH password | ansible\_password or ansible\_ssh\_pass | Used for password-based authentication. Works the same with libssh. |
| SSH user | ansible\_user | The remote username. Shared across all connection types. |
| Connection timeout | ansible\_connect\_timeout | How long to wait for the initial SSH connection. No change needed. |
| Command timeout | ansible\_command\_timeout | How long to wait for a command to complete on the device. No change needed. |
| Become / enable | ansible\_become, ansible\_become\_method, ansible\_become\_pass | Privilege escalation is handled at the connection plugin level, not the SSH library. No change needed. |

### Side-by-side example: inventory before and after

Before (paramiko):

```auto
[all:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=cisco.ios.ios
ansible_network_cli_ssh_type=paramiko

# Authentication
ansible_user=admin
ansible_ssh_private_key_file=~/.ssh/id_rsa

# Host key handling
ansible_paramiko_host_key_auto_add=true

# Proxy / bastion
ansible_ssh_common_args=-o ProxyCommand="ssh -W %h:%p bastion.example.com"

# Timeouts (these stay the same)
ansible_connect_timeout=30
ansible_command_timeout=60

```

After (libssh):

```auto
[all:vars]
ansible_connection=ansible.netcommon.network_cli
ansible_network_os=cisco.ios.ios
ansible_network_cli_ssh_type=libssh

# Authentication — variable name changed
ansible_user=admin
ansible_libssh_private_key_file=~/.ssh/id_rsa

#Use true in production (requires pre-populated known hosts); set false only in lab/test       
ansible_libssh_host_key_checking=true 

# Proxy / bastion — variable name changed, syntax simplified (no -o ProxyCommand= wrapper)
ansible_libssh_proxy_command=ssh -W %h:%p bastion.example.com

# Timeouts (unchanged)
ansible_connect_timeout=30
ansible_command_timeout=60

```

### Migration checklist

Use this checklist to verify you have covered all variable changes:

- Search your inventory files, group\_vars/, and host\_vars/ for ansible\_ssh\_private\_key\_file and ansible\_paramiko\_private\_key\_file — replace with ansible\_libssh\_private\_key\_file
- Search for ansible\_paramiko\_host\_key\_auto\_add and ansible\_host\_key\_checking — replace with ansible\_libssh\_host\_key\_checking
- Search for ansible\_ssh\_common\_args containing ProxyCommand — replace with ansible\_libssh\_proxy\_command (drop the -o ProxyCommand= prefix)
- Search for ansible\_paramiko\_look\_for\_keys — remove it and ensure keys are set explicitly via ansible\_libssh\_private\_key\_file
- If you rely on ~/.ssh/config — add ansible\_libssh\_config\_file=~/.ssh/config
- Run a test playbook with -vvvv against each platform and verify in the logs that libssh is the active backend and all authentication/proxy settings are applied correctly

* * *

## What Else You Should Verify

Before declaring your migration to libssh complete, work through these checks for each platform and workflow you support.

### 1. Environment and dependencies

- ansible-pylibssh is installed and meets the minimum version: pip show ansible-pylibssh (\>= 1.4.0)
- ansible.netcommon collection meets the minimum version: ansible-galaxy collection list ansible.netcommon (\>= 6.0.0, 8.5.0+ recommended)
- Python can import the library without error: python3 -c “import pylibsshext; print(pylibsshext. **version** )”

### 2. Connection and authentication

- SSH connectivity works with libssh to each target device (password auth)
- SSH connectivity works with libssh using key-based authentication (ansible\_libssh\_private\_key\_file)
- Host key verification behaves as expected (accept known hosts, reject unknown in production)
- If using a proxy/jump host: ansible\_libssh\_proxy\_command connects through the bastion successfully
- Connection timeouts are reasonable for your environment (ansible\_command\_timeout, ansible\_connect\_timeout)

### 3. Core operations per platform

For each platform you use (e.g., Cisco IOS):

- \*\_facts module returns facts successfully (e.g., cisco.ios.ios\_facts)
- \*\_command module executes show commands and returns expected output
- \*\_config module pushes configuration changes and they persist on the device
- Resource modules (\*\_interfaces, \*\_vlans, \*\_acls, etc.) apply config with state: merged and report correct changed status
- Resource modules with state: replaced and state: deleted behave correctly
- Idempotency: running the same playbook twice results in changed: false on the second run

### 4. File transfer (if applicable)

- net\_put successfully uploads files to the device over SCP
- net\_put successfully uploads files to the device over SFTP
- net\_get successfully downloads files from the device
- File contents match after transfer (no corruption, no truncation)
- Test with both small files (\< 1 KB) and larger files (\> 1 MB) if your workflows require it

### 5. Behavioral differences from paramiko

- Verify that error messages and failure modes are still actionable in your automation (libssh may surface different error text than paramiko)
- If your playbooks rely on ansible\_ssh\_common\_args, switch to ansible\_libssh\_proxy\_command – ansible\_ssh\_common\_args does not apply to libssh
- If you set ansible\_paramiko\_\* variables anywhere, confirm they are replaced with the equivalent ansible\_libssh\_\* or ansible\_network\_cli\_\* variables
- Confirm that long-running commands (large config pushes, file copies) complete without unexpected timeouts

* * *

## Debugging and Troubleshooting

### Enable verbose output

```auto

ansible-playbook -i inventory.ini playbook.yaml -vvvv

```

### Enable libssh debug logging

```auto

export ANSIBLE_LOG_PATH=./ansible_libssh_debug.log

export ANSIBLE_DEBUG=1

```

At verbosity -vvvv (level 4+), libssh automatically switches to DEBUG-level logging.

### Verify which SSH backend is active

Add this task to confirm libssh is being used:

```auto
  - name: Verify SSH backend
      ansible.builtin.debug:
        msg: "SSH type configured: {{ ansible_network_cli_ssh_type | default('auto') }}"

```

### Common issues and fixes

| Issue | Fix |
| --- | --- |
| ModuleNotFoundError: pylibsshext | Install: pip install ansible-pylibssh\>=1.4.0 |
| Timeout errors on connection | Increase ansible\_command\_timeout in inventory or ansible.cfg |
| File transfer fails with net\_put | Try switching protocol from scp to sftp or vice versa |
| Host key verification failure | Set ansible\_libssh\_host\_key\_checking=false (lab only) |
| Proxy command not working | Use ansible\_libssh\_proxy\_command (not ansible\_ssh\_common\_args) |

## Summary

| Connection Type | Variable to Set | Value | Notes |
| --- | --- | --- | --- |
| network\_cli | ansible\_network\_cli\_ssh\_type | libssh | Works with all vendor collections |
| netconf | ansible\_netconf\_libssh | true | Needs ncclient\>=0.7.0 |
| httpapi | N/A | N/A | REST-based, does not use SSH |
| grpc | N/A | N/A | gRPC-based, does not use SSH |

## Support

If you encounter any issues while using ssh\_type=libssh with the ansible.netcommon.network\_cli connection plugin, you can open a GitHub issue on the relevant repository:

- ansible.netcommon (connection plugins, net\_put/net\_get, cli\_command, cli\_config): [[Issues · ansible-collections/ansible.netcommon · GitHub](https://github.com/ansible-collections/ansible.netcommon/issues) ]
- ansible-pylibssh (SSH library itself, connection failures, key exchange, SFTP/SCP): [Issues · ansible/pylibssh · GitHub](https://github.com/ansible/pylibssh/issues)
