Paramiko Deprecated for network_cli — Migrate to libssh
Introduction
The Ansible project has officially deprecated Paramiko as an SSH transport for network_cli connections. Paramiko support will be removed after 2028-02-01. The recommended replacement is ansible-pylibssh, which provides better performance and aligns with Ansible's direction toward OpenSSH/libssh for all SSH connectivity.
This guide covers why the change is happening, how to migrate, and how to verify your network automation playbooks work with libssh.
Why Paramiko Is Being Deprecated
| Aspect | Paramiko | libssh (pylibssh) |
|---|---|---|
| Performance | Pure Python, slower | C library, faster |
| Maintenance | Community-maintained | Part of Ansible direction |
| SSH features | Limited subset | Full OpenSSH compatibility |
| Key exchange | Older algorithms | Modern algorithms |
| Future support | Removed after 2028-02-01 | Long-term supported |
Ansible's SSH strategy is converging on OpenSSH and libssh across all connection types — ssh, network_cli, httpapi, and netconf.
Timeline
2026-05 ─── Deprecation announced (Bullhorn #227)
Paramiko still works, but warnings appear
2027-xx ─── Expected: stronger warnings in Ansible releases
2028-02-01 ─── REMOVAL DATE
Paramiko support removed from network_cli
Playbooks using Paramiko will FAIL
Check Your Current Configuration
# Check if you're using Paramiko
grep -r "ssh_type.*paramiko" ansible.cfg inventory/ group_vars/ host_vars/ 2>/dev/null
# Check network_cli connection settings
ansible-config dump | grep -i paramiko
# Check installed packages
pip list | grep -i paramiko
pip list | grep -i pylibssh
# If you see this in ansible.cfg or inventory, you need to migrate:
[persistent_connection]
ssh_type = paramiko
Migration Steps
Step 1: Install ansible-pylibssh
# Install the libssh Python binding
pip install ansible-pylibssh
# Verify installation
python -c "import pylibsshext; print('pylibssh installed successfully')"
# On RHEL/CentOS, you may need development headers first
sudo dnf install libssh-devel gcc python3-devel
pip install ansible-pylibssh
Step 2: Update Configuration
# ansible.cfg — switch from paramiko to libssh
[persistent_connection]
ssh_type = libssh
# Or remove ssh_type entirely (libssh is becoming the default)
# inventory/group_vars/network.yml
ansible_network_os: cisco.ios.ios
ansible_connection: ansible.netcommon.network_cli
ansible_network_cli_ssh_type: libssh # Explicit libssh
Step 3: Test Connectivity
# Test with a simple ping
ansible network_devices -m ping -i inventory.yml
# Run existing playbooks in check mode first
ansible-playbook network-config.yml --check -v
Step 4: Handle SSH Key Differences
# If using SSH keys, libssh may need explicit key configuration
network:
vars:
ansible_ssh_private_key_file: ~/.ssh/network_key
ansible_network_cli_ssh_type: libssh
# libssh supports the same key types as OpenSSH
Example Network Playbook (After Migration)
---
- name: Configure network devices with libssh
hosts: switches
gather_facts: false
connection: ansible.netcommon.network_cli
vars:
ansible_network_cli_ssh_type: libssh
tasks:
- name: Get device facts
cisco.ios.ios_facts:
gather_subset:
- min
register: device_facts
- name: Display hostname
ansible.builtin.debug:
msg: "Device: {{ device_facts.ansible_facts.ansible_net_hostname }}"
- name: Configure NTP servers
cisco.ios.ios_ntp_global:
config:
servers:
- server: 10.1.1.1
prefer: true
- server: 10.1.1.2
state: merged
- name: Save running config
cisco.ios.ios_config:
save_when: modified
Multi-Platform Inventory
# inventory.yml
all:
children:
cisco_ios:
hosts:
switch01:
ansible_host: 10.0.0.1
vars:
ansible_network_os: cisco.ios.ios
ansible_network_cli_ssh_type: libssh
arista_eos:
hosts:
leaf01:
ansible_host: 10.0.0.10
vars:
ansible_network_os: arista.eos.eos
ansible_network_cli_ssh_type: libssh
juniper_junos:
hosts:
router01:
ansible_host: 10.0.0.20
vars:
ansible_network_os: junipernetworks.junos.junos
ansible_network_cli_ssh_type: libssh
Troubleshooting
| Issue | Solution |
|---|---|
ModuleNotFoundError: pylibsshext | Install: pip install ansible-pylibssh |
libssh.so not found | Install system package: dnf install libssh |
| Key exchange failure | Update libssh to latest version |
| Connection timeout | Check ansible_command_timeout (default 30s) |
| SSH host key verification | Add keys to ~/.ssh/known_hosts or set host_key_checking = False |
| Compilation error on install | Install dev headers: dnf install libssh-devel gcc |
Checking for Paramiko Usage in CI/CD
# Add a CI check for Paramiko usage
- name: Verify no Paramiko references
ansible.builtin.command:
cmd: grep -r "paramiko\|ssh_type.*paramiko" {{ playbook_dir }}
register: paramiko_check
changed_when: false
failed_when: paramiko_check.rc == 0
ignore_errors: true
- name: Warn about Paramiko deprecation
ansible.builtin.debug:
msg: "WARNING: Paramiko references found. Migrate to libssh before 2028-02-01."
when: paramiko_check.rc == 0
Best Practices
- Migrate now, don't wait — libssh is stable and ready for production
- Test in a lab first — verify all device types work before production rollout
- Update ansible.cfg globally — set
ssh_type = libsshonce, not per-host - Pin
ansible-pylibsshin requirements — add torequirements.txt - Update CI/CD pipelines — ensure
ansible-pylibsshis installed in build images - Monitor Ansible release notes — watch for changes in default SSH behavior
Conclusion
The Paramiko deprecation is a clear signal: Ansible's future SSH connectivity runs on libssh and OpenSSH. The migration is straightforward — install ansible-pylibssh, update one config line, and test. Do it now while you have time, not in 2028 when it breaks.