Network Automation

Netmiko Tutorial: Back Up Every Switch Config With Python

G Gurpreet Singh September 7, 2026 7 min read
One Python script fanning out to four network devices, with packets travelling to each and a confirmation that the configs were saved
Netmiko wraps Paramiko and handles device prompts, paging and enable mode, so a config backup script is about thirty lines. Install with pip install netmiko, set device_type correctly, wrap the session in a with block, and catch timeout and authentication errors so one dead device does not stop the run.
Key takeaways

  • Netmiko solves four things raw SSH does not: paging, prompt detection, enable mode and vendor differences.
  • Getting device_type wrong is the most common cause of a script that hangs and then times out.
  • Use a with block so the session always closes, because leftover sessions hold VTY lines and lock people out of the device.
  • Catch NetmikoAuthenticationException and NetmikoTimeoutException separately, then a general exception, so one failure does not end the run.
  • Read credentials from environment variables so the script can go into version control safely.
  • Test against one device before running against fifty, or wrong credentials will lock out your account.

Config backups are the right first automation project. There is no risk, because nothing gets written to a device. The output is obviously useful the first time something breaks. And it teaches the same connect, send, parse loop that every other script you write will use.

By the end of this you will have a script that logs into every device in a list, pulls the running config, and writes each one to a dated folder. It runs unattended, and one broken device does not stop the rest.

What Netmiko actually solves

Comparison showing that a raw SSH library makes you parse the prompt, deal with paging and enable mode and vendor differences, while Netmiko waits for the prompt, disables paging and gives one API across vendors
Netmiko is a wrapper around Paramiko. What it wraps is the tedious part.

You can SSH to a switch with Paramiko in about ten lines. Those ten lines will then fail in four predictable ways.

The output stops after 24 lines because the device is paging and waiting for you to press space. You do not know when a command has finished, because there is no marker beyond the prompt reappearing, and you have to recognise that prompt yourself. Getting to enable mode means detecting a password challenge and answering it. And every vendor does all of this slightly differently.

Netmiko handles all four. It sends the right paging command on connect, it knows what each platform prompt looks like, it gives you an enable method, and the same code works against Cisco, Arista and Juniper with only the device type changed.

Installing it

One command, and it brings Paramiko with it.

pip install netmiko

Your first script

The five stage Netmiko session lifecycle: connect and detect the prompt, enable if a secret is set, send the show running-config command, save the file, and disconnect automatically
Use the with block. It closes the session even when something raises halfway through.

This connects to one device and prints its config.

from netmiko import ConnectHandler

device = {
    "device_type": "cisco_ios",
    "host": "192.0.2.10",
    "username": "admin",
    "password": "your-password",
    "secret": "your-enable-password",
}

with ConnectHandler(**device) as conn:
    conn.enable()
    output = conn.send_command("show running-config")

print(output)

Three things in there are worth understanding rather than copying.

device_type is what selects the platform behaviour, and getting it wrong is the most common reason a first script hangs. Use cisco_ios for IOS and IOS-XE switches, cisco_nxos for Nexus, arista_eos and juniper_junos for those. If the prompt never matches, the session waits and then times out with an error that does not obviously say the type was wrong.

send_command waits for the prompt to come back before returning. That is the behaviour you want almost always. The variant send_command_timing waits on a timer instead, which you need for commands that prompt for confirmation and therefore never return to a normal prompt.

The with block disconnects for you. Leaving sessions open holds VTY lines on the device, and a switch with five VTY lines will refuse the sixth connection. On a script that runs nightly, that turns into a device nobody can log into.

Backing up every device

Five failure modes to handle: NetmikoTimeoutException for unreachable devices, NetmikoAuthenticationException for wrong credentials, ReadTimeout for slow commands, a wrong device type that hangs, and no exception handling at all
The last row is the one that matters. Without handling, the first dead device ends the run.

Here is the full thing. It reads credentials from the environment, runs eight devices at a time, catches the failures that actually happen, and reports what each device did.

import os
from datetime import date
from concurrent.futures import ThreadPoolExecutor
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException

BACKUP_DIR = "backups"

INVENTORY = [
    {"name": "core-sw-1", "host": "192.0.2.10", "device_type": "cisco_ios"},
    {"name": "core-sw-2", "host": "192.0.2.11", "device_type": "cisco_ios"},
    {"name": "edge-rtr-1", "host": "192.0.2.20", "device_type": "cisco_ios"},
]

def backup(entry):
    device = {
        "device_type": entry["device_type"],
        "host": entry["host"],
        "username": os.environ["NET_USER"],
        "password": os.environ["NET_PASS"],
        "secret": os.environ.get("NET_ENABLE", ""),
    }
    try:
        with ConnectHandler(**device) as conn:
            if device["secret"]:
                conn.enable()
            config = conn.send_command("show running-config", read_timeout=60)
    except NetmikoAuthenticationException:
        return (entry["name"], "auth failed")
    except NetmikoTimeoutException:
        return (entry["name"], "unreachable")
    except Exception as exc:
        return (entry["name"], "error: %s" % exc)

    folder = os.path.join(BACKUP_DIR, date.today().isoformat())
    os.makedirs(folder, exist_ok=True)
    path = os.path.join(folder, entry["name"] + ".cfg")
    with open(path, "w") as handle:
        handle.write(config)
    return (entry["name"], "saved %d lines" % len(config.splitlines()))

def main():
    with ThreadPoolExecutor(max_workers=8) as pool:
        for name, status in pool.map(backup, INVENTORY):
            print("%-12s %s" % (name, status))

if __name__ == "__main__":
    main()

Set the credentials in your shell rather than in the file, so the script can go into version control without going into an incident report.

export NET_USER=admin
export NET_PASS=your-password
export NET_ENABLE=your-enable-password
python backup.py

Why it is built this way

The catches are ordered from specific to general. Authentication failure and timeout mean different things and deserve different messages. The bare Exception at the end is there so that something unexpected on device three does not prevent devices four through forty being backed up.

Auth failures are worth watching. If credentials are wrong, running this against fifty devices produces fifty failed logins in a short window. On a network with lockout policies that is how you lock out the account you use for everything. Test against one device first.

Eight threads is a deliberate number. It is fast enough for a few hundred devices and gentle enough not to look like an attack to whatever is monitoring your management network. Raise it if you need to, but understand that each thread is a live SSH session.

The read timeout matters on big configs. A large chassis config can take longer than the default to return, and the failure looks like a timeout rather than what it is.

Dated folders make this useful. One config per device per day means you can diff yesterday against today, which is how you answer the question of what changed just before things broke.

Where to go next

Four network automation tools compared: Netmiko to send commands and read output, NAPALM for structured data across vendors, Nornir for running tasks over an inventory, and Ansible for declarative shared automation
Each one exists because the previous one ran out of road. Do not skip ahead until you hit that point yourself.

Netmiko returns text. When you find yourself writing regular expressions to pull an interface name out of that text, you have reached its limit, and there are two ways forward.

Pass use_textfsm=True to send_command and Netmiko will parse common show commands into a list of dictionaries for you. That covers a surprising amount.

Beyond that, NAPALM gives you structured data with the same method names across vendors, so get_interfaces returns the same shape on Cisco and on Arista. Nornir handles the inventory and threading that this script does by hand. Ansible is where you go when several people need to run the automation and none of them want to read Python.

Start here though. Most working networks are running on a handful of Netmiko scripts somebody wrote in an afternoon, and a nightly config backup is worth more than an elegant framework nobody finished.

Questions people ask

Quick answers panel noting that pip install netmiko is the whole setup, device_type must be correct, credentials should never be hardcoded, and eight concurrent threads is plenty
Four things worth getting right before the script goes anywhere near production.

How do I push configuration rather than read it?

Use send_config_set with a list of commands. It enters config mode, sends each line, and exits. Follow it with save_config to write to startup. Test on one device you can reach another way before running it widely.

Does Netmiko work with anything other than Cisco?

Yes, it supports a long list of platforms including Arista, Juniper, HPE, MikroTik and Palo Alto. The code stays the same and only device_type changes, which is most of the reason to use it.

Why does my script hang and then time out?

Usually a wrong device_type, so the expected prompt never appears. Sometimes a command that asks for confirmation, which needs send_command_timing instead. Occasionally a device so slow that the read timeout expires first.

Should I use SSH keys instead of a password?

Yes where the platform supports it. Netmiko accepts use_keys and key_file, and it removes the password handling problem entirely. Not every network device supports key based authentication for all account types, so check before rolling it out.

Is this enough automation for the CCNA?

More than enough. The automation domain is about ten percent of the exam and largely conceptual. Being able to read this script and explain what it does covers it comfortably. Our guide to building a CCNA home lab covers where to practise it.

If you want to try this against virtual devices before touching production, the lab options in that guide will run Netmiko scripts exactly as real hardware does.

GU
Written by

Gurpreet Singh

Hey! I"m Gurpreet Singh and I Have 7+ Years of experience in the Network & Security Domain as well as the Cloud Infra Domain. I am Certified with Cisco ( CCNA ), CheckPoint ( CCSA ), 1xAWS, 3xAZURE, and 3xNSE. So I love to share my tech knowledge with you.

Leave a Reply

Your email address will not be published. Required fields are marked *