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

AspectParamikolibssh (pylibssh)
PerformancePure Python, slowerC library, faster
MaintenanceCommunity-maintainedPart of Ansible direction
SSH featuresLimited subsetFull OpenSSH compatibility
Key exchangeOlder algorithmsModern algorithms
Future supportRemoved after 2028-02-01Long-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

IssueSolution
ModuleNotFoundError: pylibsshextInstall: pip install ansible-pylibssh
libssh.so not foundInstall system package: dnf install libssh
Key exchange failureUpdate libssh to latest version
Connection timeoutCheck ansible_command_timeout (default 30s)
SSH host key verificationAdd keys to ~/.ssh/known_hosts or set host_key_checking = False
Compilation error on installInstall 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

  1. Migrate now, don't wait — libssh is stable and ready for production
  2. Test in a lab first — verify all device types work before production rollout
  3. Update ansible.cfg globally — set ssh_type = libssh once, not per-host
  4. Pin ansible-pylibssh in requirements — add to requirements.txt
  5. Update CI/CD pipelines — ensure ansible-pylibssh is installed in build images
  6. 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.