Nornir: running Python tasks across many network devices in parallel
In short: Nornir is a Python automation framework that runs tasks against many devices in parallel. It reads hosts and groups from an inventory, lets you filter targets, and uses plugins such as Netmiko and NAPALM to do the work. Everything is plain Python, so logic and debugging are straightforward.
Why a framework?
Looping over devices one by one is fine for five switches, but slow for five hundred. You also end up rewriting the same inventory loading, threading and error handling in every script. Nornir is an open-source Python framework that provides those pieces: an inventory, a way to filter it, a runner that works on many hosts in parallel, and a result object that records what happened on each host.
Inventory and configuration
The default SimpleInventory plugin reads YAML files. Hosts inherit settings from groups and defaults, so you only write shared values once. Install the packages with pip install nornir nornir-utils nornir-netmiko.
# config.yaml
inventory:
plugin: SimpleInventory
options:
host_file: inventory/hosts.yaml
group_file: inventory/groups.yaml
runner:
plugin: threaded
options:
num_workers: 20
# inventory/hosts.yaml
acc-sw1:
hostname: 192.168.10.11
groups: [ios]
data:
site: london
role: access
core-sw1:
hostname: 192.168.10.1
groups: [ios]
data:
site: london
role: core
# inventory/groups.yaml
ios:
platform: cisco_ios
Tasks, filters and results
A task is a Python function that receives a Task object for one host. You can call plugin tasks inside it, or write your own logic. The runner calls it for every host in the filtered inventory at the same time, up to num_workers.
import os
from nornir import InitNornir
from nornir.core.task import Task, Result
from nornir_netmiko.tasks import netmiko_send_command
from nornir_utils.plugins.functions import print_result
nr = InitNornir(config_file="config.yaml")
nr.inventory.defaults.username = os.environ["NET_USER"]
nr.inventory.defaults.password = os.environ["NET_PASS"]
def check_version(task: Task) -> Result:
r = task.run(task=netmiko_send_command, command_string="show version")
ok = "15.2" not in r.result
return Result(host=task.host, result="ok" if ok else "old software")
access = nr.filter(site="london", role="access")
results = access.run(task=check_version)
print_result(results)
for host, multi in results.items():
if multi.failed:
print(host, "FAILED:", multi.exception)
Tip: failed hosts are recorded, not fatal. Check results.failed_hosts after a run, and use nr.data.failed_hosts to retry only those that failed.
Choosing Nornir and using it well
- Keep tasks small: one task to gather facts, one to render a template, one to push it. Compose them inside a parent task.
- Use dry runs: plugins such as
napalm_configureacceptdry_run=Trueto show the difference without committing. - Tune workers: too many parallel sessions can overload a TACACS+ or RADIUS server or the devices. Start with 10 to 20.
- Source of truth: inventory plugins exist for NetBox and other systems, so Nornir can read devices straight from your IPAM (IP address management) tool.
- Secrets: set credentials at run time from environment variables or a vault, never in hosts.yaml.
Practice
What does the threaded runner’s num_workers control?
A task fails on two of fifty hosts. What happens to the other forty-eight?
How would you run a task only on core switches in one site?
Key takeaways
- Nornir is a pure-Python framework for parallel network automation.
- Inventory hosts inherit from groups and defaults.
- Tasks are Python functions; plugins add Netmiko, NAPALM and more.
- Filter targets, then inspect per-host results and failures.
- Set credentials at run time and tune worker counts sensibly.
Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.