A NetBox plugin that reads interfaces, IP addresses, and VLANs from network devices over SNMP and synchronises them directly into NetBox — entirely from within the NetBox UI, with no external scripts, cron jobs, or second tools required.
- Overview
- Features
- Requirements
- Installation
- Configuration
- Usage
- REST API
- Security
- Development & tests
- Changelog
Network devices speak SNMP: they expose their interfaces, IP addresses, VLANs, and system
information through a standard protocol that has been around for decades. netbox-snmp-sync
bridges that world with NetBox — it polls a device over SNMP, computes a diff against what
NetBox already knows, and either shows you the diff or writes the missing data directly through
the NetBox ORM.
Everything runs natively inside NetBox:
Device (SNMP)
│
▼
SNMP collector (asyncio + pysnmp)
│
▼
Diff engine ──► Preview page (pick what to write)
│ ──► Compare job (read-only diff → job log)
│ ──► Sync job (add-only write to NetBox ORM)
│ ──► Scheduled (automatic periodic sync)
│
▼
NetBox ORM ──► Changelog (who / when / before → after)
──► SyncRun (history, statistics, revert)
The plugin is a successor to a standalone netbox-snmp-sync CLI tool. The SNMP collection
and mapping logic is reused; the data is now written through Django ORM and the entire
workflow lives in NetBox's UI and background-job framework.
Each device gets its own SNMP configuration, accessible from the device detail page's SNMP Sync tab or from SNMP Sync → Device SNMP Configs:
| Field | Description |
|---|---|
| SNMP version | v1 / v2c / v3 |
| Community string | SNMPv1 and v2c authentication |
| SNMPv3 credentials | Username, auth protocol (MD5/SHA/SHA-224/256/384/512), auth key, priv protocol (DES/AES-128/192/256), priv key |
| Port | Default 161 |
| Timeout / retries | Per-device transport tuning |
| Target override | Poll a different host/IP than the device's primary IP |
| Data | Details |
|---|---|
| Interfaces | Name, type (derived from speed), MTU, speed, duplex, admin/oper status, description, MAC address, parent interface for sub-interfaces |
| IPv4 addresses | With prefix length, assigned to the correct interface |
| VLAN membership | Tagged and untagged VLAN assignments per interface (optional) |
| System info | sysName, vendor, used in the test result and job logs |
| Action | What it does |
|---|---|
| Test SNMP | Quick connectivity probe (one SNMP GET for sysName). Renders a full result page showing OK / Failed and sysName. Saves the outcome to the Last test column — no NetBox data is changed. |
| Bulk test | Select multiple configs in the list → Test selected → probes all of them concurrently (worker pool of 8) and renders a combined result page. |
| Preview & write | Full SNMP poll → diff page with checkboxes → writes only the items you select. |
| Compare | SNMP poll → diff written to the background job log (read-only, nothing is changed). |
| Sync all | SNMP poll → add-only write of all new interfaces and IPs to NetBox. |
| Scheduled sync | System job that runs every few minutes and queues a per-device sync for each enabled device whose next sync time is due. |
Per-device SNMP settings also include Rename device to sysName and sync behaviour
overrides. Each device can inherit the global settings or explicitly enable/disable
interface sync, IP address sync, updating existing interfaces, MAC writes, VLAN membership
writes, automatic VLAN creation, and whether VLAN IDs may be inferred from sub-interface
names like parent.30. When rename is enabled, apply syncs rename the NetBox device to
the collected SNMP sysName; read-only tests and compare runs do not rename devices.
Preview shows the collected sysName before writing, and successful renames are recorded
in the sync run message and change log. When automatic VLAN creation is on, set a
VLAN group on the device's SNMP config to place every VLAN auto-created for that device
(from Preview & write or a scheduled sync) into that group; leave it blank for no group.
Setting or changing the VLAN group also retroactively re-assigns every VLAN already
referenced by the device's interfaces (from before this setting existed, or from manual
entry) — saving the config is enough, no re-sync needed. Clearing the VLAN group does not
touch existing VLANs.
- SyncRun model — every run (manual or scheduled) is stored in the database with:
- timestamp, trigger type (manual / scheduled), mode (compare / apply / dry-run)
- status (OK / failed)
- counters: interfaces created / updated / existing / ignored; IPs created / existing
- free-text message / error
- NetBox changelog integration — all writes (including those from background jobs) are
wrapped in
event_trackingso they appear in NetBox's built-in change log with the correct user, timestamp, and before/after snapshots. - Revert run — each run records every object it created (
SyncRunObject). Clicking Revert run deletes exactly those objects. Deletions also land in the change log.
Plugin-level settings are stored in a database-backed singleton (SNMPSyncConfig) and
editable at SNMP Sync → Settings without restarting NetBox:
- Sync interval (minutes), sync interfaces/IPs, update existing objects, set MAC address
- VLAN write / auto-create, history retention (days + count)
SNMP Sync → Bulk setup lets you create SNMP configurations for many devices at once, optionally reading the community string from a custom field on each device.
| Dependency | Version |
|---|---|
| NetBox | 4.6 or newer |
| Python | 3.12 or newer |
| pysnmp | ≥ 7.1, < 8 |
| Redis + RQ worker | Standard NetBox prerequisite (netbox-rq service) |
Important: The RQ worker (
netbox-rq) must be running. Compare, Sync, and Scheduled jobs are dispatched to the worker queue — without it they never execute.
# Activate the NetBox virtual environment
source /opt/netbox/venv/bin/activate
# Install from GitHub
pip install git+https://github.com/adrian-13/netbox-snmp-sync.git
# Or from PyPI once published
pip install netbox-snmp-syncAdd the plugin to configuration.py (or configuration/plugins.py in netbox-docker):
PLUGINS = [
"netbox_snmp_sync",
]Run migrations and collect static files, then restart:
cd /opt/netbox/netbox
python manage.py migrate
python manage.py collectstatic --no-input
sudo systemctl restart netbox netbox-rqpython manage.py showmigrations netbox_snmp_sync
# All six migrations must show [X]The SNMP Sync menu should now appear in the NetBox navigation bar, and every device detail page should show an SNMP Sync tab.
The repository ships a Dockerfile that builds a NetBox image with the plugin installed
in editable mode and a bind-mount of the source directory so live code changes take effect
without a rebuild.
All values below can also be changed at runtime through SNMP Sync → Settings in the NetBox UI — no restart needed.
PLUGINS_CONFIG = {
"netbox_snmp_sync": {
# ── SNMP transport defaults (used when a device has no per-device override) ──
"snmp_version": "2c", # "1" | "2c" | "3"
"snmp_community": "public", # SNMPv1/v2c community string
"snmp_port": 161,
"snmp_timeout": 2.0, # seconds per request
"snmp_retries": 1,
# ── Data mapping ────────────────────────────────────────────────────────────
"default_ethernet_type": "1000base-t", # NetBox interface type when SNMP
# cannot determine one
"set_mac_address": True, # populate the MAC address field on interfaces
"update_existing": False, # True = also overwrite changed fields on
# existing interfaces (default: add-only)
"skip_loopback_ips": True, # skip 127.x.x.x addresses
# ── VLAN sync ───────────────────────────────────────────────────────────────
"write_vlans": False, # assign VLAN membership on interfaces
"create_vlans": False, # auto-create missing VLANs in the device's site
"vlan_subinterface_inference": "auto", # auto | enabled | disabled;
# auto infers parent.<vid> only for MikroTik
# ── Scheduler ───────────────────────────────────────────────────────────────
"sync_interval_minutes": 1440, # 0 = interval scheduler disabled
"sync_at_hours": "", # e.g. "3" or "3,15" → run only at those hours of the
# day (interval is then ignored). Blank = use interval.
# ── History retention (SyncRun pruning) ─────────────────────────────────────
"sync_job_timeout_seconds": 300, # max SNMP collection runtime per background job;
# 0 disables this guard
"sync_stale_job_marker_minutes": 120, # clear stale queued/running markers after this
# many minutes; 0 disables automatic cleanup
"history_keep_days": 90,
"history_keep_count": 1000,
},
}Open Devices → <device> → SNMP Sync tab → Add, or go to SNMP Sync → Device SNMP Configs → Add and select the device.
Fill in the SNMP version and credentials. The poll target defaults to the device's primary IP; set Target override if you need to poll a management address instead.
Click the Test SNMP button (the cyan icon next to the pencil in the list, or the button on the device's SNMP Sync tab). A result page is rendered immediately:
- ✅ OK — shows the sysName returned by the device
- ❌ Failed — shows the exact error (unreachable, wrong community, timeout, …)
The result is saved to the Last test column in the list and to the device's SNMP Sync tab, so you can see at a glance which devices are reachable.
To test multiple devices at once: check them in the list → click Test selected at the bottom. Results are shown in a single table.
| Button | Effect |
|---|---|
| Preview & write | Poll → diff page with checkboxes → write selected items |
| Compare | Poll → diff written to the background job log only |
| Sync all | Poll → add-only write of everything new to NetBox |
All three dispatch a background job visible at Jobs in the NetBox admin area.
Use Sync & schedule when you want a manual apply sync to also reset the device's automatic schedule from that successful run. Regular manual Sync all updates the device but leaves Next sync unchanged.
SNMP Sync → Sync Runs lists every run with its timestamp, trigger, mode, status, and counters. Click a run to open the detail page. If the run created objects and has not been reverted, the Revert run button is available.
There are two scheduling modes, both configured at SNMP Sync → Settings:
- Interval mode — set
sync_interval_minutesto a positive integer. The scheduler check runs every 5 minutes and queues a sync for every enabled device whose Next sync time is due — so intervals shorter than 5 minutes won't run any more often than that. Scheduled SNMP Sync is only a scheduler check; actual per-device SNMP Sync jobs are queued only when a device is due. Changing the interval recalculates each device's Next sync from the time the setting is saved, and due devices are picked up on the next scheduler check. When multiple devices are re-anchored at once, their next runs are spread over a short window so they do not all start at the same second. - Fixed-hour mode — set Sync at hours to one or more hours of the day (0–23,
comma-separated, e.g.
3or3,15). Syncs then run only during those hours (e.g. daily at 03:00). When set, the interval is ignored.
Newly added device configurations are picked up automatically on the next scheduler run — no restart or manual step needed. Each device gets its own isolated RQ job, so a slow or unreachable device does not block the others. If a device already has a pending or running SNMP sync job, the scheduler reuses it instead of queuing a duplicate. Failed scheduled syncs use a simple exponential retry delay (1 h, 2 h, 4 h, up to 24 h) before trying again. The device list and detail tab show Retry / Retry due with the failure count and last error message.
Background sync jobs also have a configurable SNMP collection timeout. Set
Sync job timeout seconds in Settings to cap the collection phase for one device; use 0
only if you explicitly want to disable this guard.
Each Device SNMP Configuration can override the global scheduler:
- Leave Sync interval minutes and Sync at hours blank to inherit the global schedule.
- Set Sync interval minutes on one device to give it its own rolling interval.
- Set Sync at hours on one device to run that device only at specific local hours.
- Set Sync interval minutes to
0with no per-device hours to disable automatic sync for that device while keeping manual sync available.
Changing a per-device schedule immediately re-anchors that device's Next sync. Changing the global schedule re-anchors only devices that inherit the global scheduler; devices with explicit per-device schedules keep their own cadence.
On a device SNMP configuration detail page, operators with change permission can also
Recalculate the next sync from the current effective schedule. If a queued/running marker
is visible, Reconcile marker safely clears it when it is stale. The list page also provides
Reconcile markers for selected configs, useful after a worker/container restart. Stale
marker cleanup is automatic in the scheduler too; sync_stale_job_marker_minutes controls
the age threshold and the config's last sync message records when a stale marker was cleared.
The plugin exposes two endpoints, fully integrated with NetBox's DRF infrastructure (authentication, filtering, pagination, OpenAPI schema):
GET /api/plugins/snmp-sync/device-snmp-configs/
POST /api/plugins/snmp-sync/device-snmp-configs/
GET /api/plugins/snmp-sync/device-snmp-configs/{id}/
PUT /api/plugins/snmp-sync/device-snmp-configs/{id}/
PATCH /api/plugins/snmp-sync/device-snmp-configs/{id}/
DELETE /api/plugins/snmp-sync/device-snmp-configs/{id}/
GET /api/plugins/snmp-sync/sync-runs/
GET /api/plugins/snmp-sync/sync-runs/{id}/
Interactive documentation is available at /api/schema/swagger-ui/ under the plugins section.
| Concern | Mitigation |
|---|---|
| SNMP secrets in the API | community, auth_key, and priv_key are declared write_only in the serializer — GET requests never return them |
| SNMP secrets in the database | Stored in plain text (same as a config.yaml). Restrict DB access and rotate credentials regularly. |
| Access control | All views and API endpoints respect standard NetBox permissions (view_devicesnmpconfig, add_devicesnmpconfig, change_devicesnmpconfig, delete_devicesnmpconfig) |
| Production polling | The plugin only issues read-only SNMP GET/GETBULK requests — it never writes to devices |
# Clone and install in editable mode
git clone https://github.com/adrian-13/netbox-snmp-sync.git
pip install -e netbox-snmp-sync/
# Run the test suite
export NETBOX_CONFIGURATION=netbox.configuration_testing
cd /opt/netbox/netbox
python manage.py test netbox_snmp_sync
# Run a specific module
python manage.py test netbox_snmp_sync.tests.test_api
python manage.py test netbox_snmp_sync.tests.test_securityUse snmpsim-lextudio with the provided *.snmprec walk files:
pip install snmpsim-lextudio
snmpsim-command-responder --data-dir=./snmprec --agent-udpv4-endpoint=127.0.0.1:1161Then set Target override to 127.0.0.1 and Port to 1161 on a test SNMP config.
netbox_snmp_sync/
├── models.py — DeviceSNMPConfig, SyncRun, SyncRunObject, SNMPSyncConfig
├── views.py — CRUD, Test, Bulk test, Preview, Sync, Settings, Revert
├── jobs.py — SNMPSyncJob, ScheduledSNMPSyncJob, PruneSyncRunsJob
├── engine.py — diff / apply logic (compare_device, apply_sync)
├── snmp_collector.py — async SNMP collection (pysnmp 7.x, asyncio)
├── spec.py — DeviceConfig dataclass
├── dto.py — serialise / deserialise collected data (preview snapshot)
├── filtersets.py — FilterSets for list views and API filtering
├── forms.py — ModelForms, BulkEditForm, BulkSNMPConfigForm
├── tables.py — django-tables2 table definitions
├── serializers.py — DRF serializers (secrets write_only)
├── api/ — DRF viewsets and router
├── graphql/ — Strawberry GraphQL types
├── migrations/ — 0001 … 0006
└── tests/ — test_api.py, test_filtersets.py, test_security.py
The plugin uses only public NetBox plugin APIs: NetBoxModel, NetBoxModelForm,
NetBoxTable, JobRunner, system_job, event_tracking, register_model_view.
No internal NetBox code is imported directly.
- Bulk select/deselect on the preview page - writing a preview meant clicking every checkbox by hand, and there was no way to clear the default all-selected state short of unchecking each row (a single device can report 180+ interfaces). There are now "Select all" / "Deselect all" buttons spanning every table, a toggle in each table header scoped to that section alone, and a "N of M selected" counter per section so the write scope is visible before submitting. Rows for existing/ignored objects aren't writable and stay excluded from the bulk controls.
- Fixed MAC address sync never settling on devices with shared MACs - the same-device MAC reassignment added in v0.3.11 assumed a MAC already held by another interface always meant "this port was renamed," but VLAN sub-interfaces routinely and correctly report their parent port's MAC over SNMP. On a device with many sub-interfaces sharing one MAC, every sync run reshuffled that MAC onto whichever interface synced last, so the pending change never went away no matter how many times it was written. Each interface now gets its own MAC address record instead of fighting siblings over a shared one.
- VLAN names stay in sync - when "Update existing objects" is enabled, a VLAN's name in NetBox is now refreshed if it no longer matches what SNMP reports for that VLAN ID. VLANs are already matched by VID (a stable identifier), so this never creates a duplicate - only the label was going stale before.
- Menu items respect permissions - "Device SNMP Configs", "Sync Runs", "Settings" and
the "Add" button now check the relevant
view_*/add_*/change_*permission before showing in the navigation menu, instead of always appearing regardless of access. - Bulk setup page requires add permission on GET - previously only the POST (submit)
handler checked
add_devicesnmpconfig; a logged-in user without that permission could still open the form itself via direct URL. - Action buttons hidden without change permission - Test SNMP, Sync & schedule, Edit, Recalculate and Clear stuck sync no longer render for users who can view but not change a device's SNMP config (they were already blocked server-side; now they're not shown as an option in the first place). Read-only actions like Preview & write remain visible.
- SNMP community string no longer rendered in the UI - it showed up in plaintext in the Device SNMP Configs list (a default-visible column, no extra click needed), the device tab, and the config detail page. All three now show only whether a community string is configured, matching how SNMPv3 auth/priv keys are already kept out of templates.
- Device type / manufacturer / site on SNMP views - the SNMP Sync panel now shows the device's Manufacturer and Device Type, and the Device SNMP Configs list gained Site and Device Type columns.
- Sortable Schedule / Sync state columns - both are now orderable by clicking their
header, backed by a DB-level annotation that mirrors the
schedule_label/sync_statelogic exactly (verified against every branch, not just the common cases). - Filters panel - the Device SNMP Configs list now has a proper NetBox-style Filters panel (Device, Site, SNMP version, sync behaviour toggles), matching how core models like Device expose filtering.
- Tags column - added to the Device SNMP Configs list, same
TagColumnNetBox itself uses elsewhere. - Bulk "Sync & schedule" - added a bulk action alongside Test selected / Recalculate schedule, queuing an isolated apply-and-reschedule job per selected device.
- Trimmed and renamed confusing actions - removed Compare (fully superseded by Preview & write) and Sync all (redundant with Sync & schedule, differed only in whether it reset the schedule — an easy way to click the wrong one). Renamed "Reconcile marker(s)" to Clear stuck sync(s), since "marker" is internal jargon.
- Sync interval is now minutes, not hours -
sync_interval_hoursis renamed tosync_interval_minuteson both global settings and per-device overrides. Existing values are converted automatically on upgrade (hours × 60) so real-world schedule timing is unchanged. Retry backoff (1h/2h/4h/…/24h) and Sync at hours are unaffected — only the interval setting's unit changed. - MAC address reassignment on same-device conflicts - when a collected MAC address already belongs to a different interface on the same device (common for virtual interfaces like bridges that report a physical port's MAC over SNMP), the sync now reassigns it instead of silently skipping it forever. Conflicts with a MAC already assigned on a different device are still left alone (more likely a real data problem) but now surface a clear warning instead of failing silently.
- Retroactive VLAN group assignment - setting or changing a device SNMP config's VLAN group now also re-assigns every VLAN already referenced (untagged or tagged) by that device's interfaces to the new group, not just VLANs a future sync creates. Clearing the VLAN group leaves existing VLANs untouched. Since VLANs aren't device-scoped in NetBox, a VLAN also used by another device is updated too.
- SNMP Sync device tab - the per-device SNMP Sync info moved from a right-side panel on the device page into its own SNMP Sync tab (badge shows the sync run count), alongside Interfaces, IP Addresses, etc.
- VLAN group assignment - device SNMP configs can now set a target VLAN group; VLANs auto-created for that device — via Preview & write or a scheduled/background sync — are placed in that group.
- Missed schedule recovery - after long scheduler downtime, an overdue schedule older than
a configurable grace window (
sync_missed_schedule_grace_minutes) is re-anchored instead of queued as a catch-up sync, avoiding a stampede of syncs for every device at once. - Bulk schedule recalculation - added a bulk action to recompute the next scheduled sync time for selected device SNMP configurations.
- Per-device sync behaviour - device SNMP configs can now inherit or override interface sync, IP sync, existing-interface updates, MAC writes, VLAN writes, and automatic VLAN creation.
- VLAN sub-interface inference strategy - added global and per-device controls for
whether names like
parent.30should be treated as VLAN 30.Autoenables this for MikroTik and avoids false Cisco VLANs from sub-interface unit suffixes. - Cisco VLAN safety - Cisco subinterfaces such as
Gi0/0/0.10no longer create or assign VLAN 10 unless SNMP data or an explicit inference override says so.
- Existing object enrichment - sync now fills missing IP-to-interface assignments and missing primary MAC addresses on existing interfaces without reassigning objects that already belong elsewhere.
- Worker restart recovery - stale queued/running SNMP sync markers are cleared after a configurable timeout, even when NetBox still shows the old job as active after a worker/container restart.
- Stale marker controls - added
sync_stale_job_marker_minutes, visible last-sync messages when stale markers are cleared, and a bulk Reconcile markers action for selected device SNMP configurations. - Faster SNMP tests - Test SNMP now uses a single
sysNameGET instead of a full collection walk, so connectivity checks return quickly on large or slow devices. - History pruning coverage - added tests for
history_keep_daysandhistory_keep_countretention behaviour.
- Cisco VLAN discovery - collect VLAN names from CISCO-VTP-MIB and port membership from Cisco access/trunk VLAN MIBs when Q-BRIDGE membership tables are not exposed in the default SNMP context.
- Sync run change details - sync run detail pages now store and show field-level created/updated changes, including VLAN creation and interface VLAN membership updates.
- VLAN creation by device site - when a discovered VID exists only in another site, sync now creates the VLAN in the current device's site instead of reusing the unrelated VLAN object.
- Sync run VLAN counters - sync run detail pages now show how many VLANs were created and how many interfaces had VLAN membership written.
- Preview VLAN writes - interactive preview/write now includes VLAN membership rows and uses the runtime SNMP Sync settings for VLAN writes.
- Packaged plugin templates - include NetBox HTML templates in the wheel so the Device SNMP Configurations list renders after installing the published package.
- Visible schedule state - device configs now track last sync, next sync, retry state, queued/running job markers, and stale job cleanup.
- Deterministic scheduler - due checks run every 5 minutes, queue isolated per-device jobs, avoid duplicate queued/running jobs, retry failures with backoff, and spread bulk schedule changes over a short window.
- Per-device schedule overrides - each device can inherit the global schedule, use its own interval, use its own fixed hours, or disable automatic sync while retaining manual sync.
- Job runtime guard - background sync jobs can cap the SNMP collection phase with
sync_job_timeout_seconds. - Operator recovery actions - device config detail pages include POST-only controls to recalculate next sync and safely reconcile stale sync markers.
- Migrations: 0008 (schedule state), 0009 (job state), 0010 (per-device schedule overrides), 0011 (sync job timeout)
- Fixed-hour scheduling — new Sync at hours setting (0–23, comma-separated). Scheduled syncs can now run at specific hours of the day (e.g. daily at 03:00) instead of only on a rolling interval. Blank keeps the existing interval behaviour; when set, the interval is ignored. Input is validated and normalised in the Settings form.
- Migration: 0007 (
sync_at_hoursfield)
- Test SNMP result page — full OK/Failed page instead of a toast message that was silently swallowed by the browser; last test time, status badge, and message are persisted and shown in the list column and device tab
- Bulk SNMP test — select multiple configs → Test selected → concurrent probes (thread pool of 8), combined result page
- Global settings in UI —
SNMPSyncConfigsingleton editable at SNMP Sync → Settings without restarting NetBox - Per-device scheduler —
ScheduledSNMPSyncJobenqueues one isolatedSNMPSyncJobper due device; a slow or unreachable device no longer blocks the queue - VLAN membership sync — writes tagged/untagged VLAN assignments; optionally auto-creates missing VLANs in the device's site
- Changelog integration — all ORM writes from background jobs are wrapped in
event_tracking(NetBoxFakeRequest(...))so they appear in NetBox's audit log - Revert run —
SyncRunObjecttracks created objects; Revert run deletes exactly those objects; deletions are also recorded in the change log - Bulk setup — create SNMP configs for many devices at once, optionally reading the community string from a custom field
- REST API secrets protection —
community,auth_key,priv_keyarewrite_onlyin the serializer - Security tests — 17 tests covering secret exposure, permission checks, job isolation
- Migrations: 0004 (
SNMPSyncConfig), 0005 (field cleanup), 0006 (last_test_*fields)
- Initial release: SNMP collection (pysnmp 7.x, asyncio), per-device configuration,
Compare / Sync background jobs,
SyncRunhistory, REST API
Pull requests are welcome. Please open an issue first to discuss the change, include tests
for new functionality, and follow the existing code style (single quotes, 120-char line
length, ruff for linting).