← All notes
Proxmox & Virtualization

Inventorying nine Proxmox VE nodes in NetBox 4.7: DMI, MAC addresses and network topology

Production case study: read-only collection of nine Proxmox hosts and 362 interfaces, safe enrichment of existing NetBox devices, and a verified idempotent dry-run.

Status: currentPublished: 2026-10-19Updated: 2026-10-19

Tested on: Proxmox VE · NetBox 4.7.3 · Python 3.13 · AlmaLinux 9

Objective

NetBox already held the physical Proxmox devices and interfaces. The next step was to validate the actual hardware inventory against DCIM: DMI manufacturer/model, usable hardware serials, current physical NIC MAC addresses, bridges, VLAN interfaces and their parent relationships.

This case study covers a nine-host Proxmox VE cluster verified in October 2026. Hostnames, addresses, domains and local NetBox slugs are examples; the aggregate counts come from completed checks. The published toolkit is greksw/netbox-inventory-toolkit. This toolkit is not a two-way automatic synchronizer.

01 / Collection architecture

Proxmox cluster (9 nodes)
  ├── pvesh GET: hosts, status, network configuration
  ├── local sysfs: DMI, physical NIC MACs
  └── optional trusted SSH to peers: DMI and NIC MACs
               |
               v
       private inventory v0.3 (0600)
               |
       topology/completeness audit
               |
       NetBox read-only preview
               |
       guarded hardware enrichment of pre-existing DCIM objects

The collector runs on a cluster node as root because pvesh needs privileged access. Remote DMI and NIC reads are opt-in; SSH is batch-only and requires previously verified host keys. Nothing in the collector writes to NetBox or changes the network configuration. VM/CT inventory, rack cabling, VLAN reachability and IPAM creation are separate concerns.

02 / Running a private read-only collection

git clone https://github.com/greksw/netbox-inventory-toolkit.git
cd netbox-inventory-toolkit

# Start with the local host:
python3 collectors/proxmox_inventory.py \
  --node "$(hostname -s)" --cluster-label example-pve

# After explicitly configuring trusted key-based SSH:
python3 collectors/proxmox_inventory.py \
  --cluster-label example-pve --dmi-ssh --mac-ssh \
  --output output/proxmox-inventory-v03.json

The JSON is written atomically with mode 0600; the output/ directory is Git-ignored. Never publish the actual inventory JSON, host identifiers, MACs, serials or SSH credentials.

The physical MAC fallback reads /sys/class/net/*/address only when a physical sysfs device exists. These are current interface MAC values, not guaranteed factory MACs. No physical NIC MAC is guessed for Linux bridges or VLAN subinterfaces.

The built-in topology audit verifies references such as VLAN parents, bridge ports and bond slaves. It does not prove cable continuity, network reachability or correct VLAN forwarding.

03 / Verified results

Measure Result
Successfully collected hosts 9/9
Interface records 362
Physical NIC MAC values 20
Usable DMI serials 7
Nodes without a usable serial 2
Topology reference issues 0

The audit compared node/interface names as well as counts. A SHA-256 digest over a sorted node|interface list provided an additional consistency check; the actual infrastructure digest is omitted.

Two hosts had no usable serial from the source. These values stayed unknown rather than being fabricated.

04 / Existing-object reconciliation

The toolkit includes an offline comparison:

python3 netbox/preview.py \
  --inventory output/proxmox-inventory-v03.json \
  --output output/netbox-preview.json

Important distinction: devices and interfaces already existed in the live NetBox database. Initial serial/MAC enrichment was carried out in a separate manually reviewed, transaction-protected administrative procedure after a PostgreSQL backup. The generic netbox/enrich_existing.py module was subsequently validated in production in dry-run mode only; it did not perform that initial write.

A representative protected Django shell invocation, with generic site/role values:

runuser -u netbox -- /opt/netbox/venv/bin/python \
  /opt/netbox/netbox/manage.py shell -c \
  "from runpy import run_path; run_path('/secure/enrich_existing.py')['run'](
    '/secure/proxmox-inventory-v03.json',
    site_slug='example-office', role_slug='hypervisor',
    expected_devices=9, expected_interfaces=362,
    expected_serials=7, expected_macs=20
  )"

The verified repeat audit reported:

mode: DRY-RUN
verified_devices: 9
verified_interfaces: 362
serials_to_add: 0
macs_to_assign: 0
serials_matching: 7
macs_matching: 20

The generic enrichment module is guarded, requires a separate confirmation for an optional apply, and updates eligible existing hardware fields only. It does not create IP addresses, VLANs, interfaces, VMs or devices. Avoid concurrent importers that modify the same records.

05 / Operational conclusions

The next part of this implementation is covered in Daily Proxmox–NetBox drift detection with systemd and Zabbix.

Resources