NetBox IPAM Guide: Model Prefixes, VLANs and IP Addresses - 夜莺博客

NetBox IPAM Guide: Model Prefixes, VLANs and IP Addresses

Spreadsheets stop scaling the moment your address plan has more than one site, and that is exactly when IP conflicts start costing outages. NetBox is the open-source source-of-truth for IPAM and DCIM, and its IP model is deliberately hierarchical: aggregates contain prefixes, prefixes nest inside each other, and IP addresses are arranged automatically underneath the prefix that matches them. This guide walks through modelling an address plan in NetBox, handling overlapping space with VRFs, keeping the data current through the REST API, and the workflow habits that keep the database trustworthy.

The IP hierarchy, and why it matters

Object Represents Example
RIR Registry holding the space APNIC, RIPE, RFC 1918
Aggregate Root of an addressing hierarchy 10.10.0.0/16
Prefix Subnet, nests inside parents 10.10.20.0/24
IP Range Arbitrary range inside a prefix 10.10.20.10-99/24 (DHCP pool)
IP Address Single address with mask 10.10.20.5/24

Because the hierarchy is formed automatically, you never assign a parent manually - NetBox places 10.10.20.0/24 under 10.10.0.0/16 based purely on the addressing. Prefix utilisation reflects child objects: a Container prefix reports utilisation based on its child prefixes, while an active prefix reports utilisation based on the IP addresses and ranges assigned inside it.

Statuses encode intent, and intent is what makes the numbers meaningful. Container means "this exists only to hold smaller blocks, do not allocate from it directly"; Active means "allocate here"; Reserved means "planned, not yet in use"; Deprecated means "still routed, stop using"; Available marks free space you have deliberately documented. A site whose prefixes are all Active will show convincing utilisation figures that mean nothing, because the containers are missing.

Modelling a site end to end

  1. Create the aggregate for the block in use, tied to the correct RIR.
  2. Create VLAN groups scoped to the site, then the VLANs (IDs 10, 20, 30) with roles such as Data, Voice, Guest, Management.
  3. Create prefixes with a functional role and status, and nest them under the aggregate.
  4. Assign the VLAN to the prefix, so the L2 and L3 view stay linked.
  5. Add IP ranges for DHCP scopes and mark them as pools.
  6. Assign IP addresses to device interfaces (or VM interfaces) instead of typing them as free text - this is what makes the database verifiable.

The DCIM side is what turns IPAM into something automation can use. A site contains racks, racks contain devices, devices contain interfaces, and interfaces hold the addresses. Once that chain exists, "which addresses are in use on the floor-1 access switches" is a query rather than a spreadsheet reconciliation:

# which device holds a given address?
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://netbox.example.com/api/ipam/ip-addresses/?address=10.10.20.5/24" | jq '.results[0].assigned_object'

# every interface on one device, with its addresses
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://netbox.example.com/api/dcim/interfaces/?device=sw-floor1-01&limit=0" \
  | jq '.results[] | {name, mac_address, ip: .count_ipaddresses}'

VLANs, VLAN groups and layer 2 context

VLANs are not decoration in an IPAM database - they are the L2 object that gives a prefix its meaning. Create a VLAN group per site (or per campus), give every VLAN a VID, a name and a role, and use the group to keep the same VID space independent at each location:

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://netbox.example.com/api/ipam/vlan-groups/ \
  --data '{"name":"site-berlin","slug":"site-berlin","scope_type":"dcim.site","scope_id":3}' | jq .id

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://netbox.example.com/api/ipam/vlans/ \
  --data '{"vid":20,"name":"VOICE","status":"active","group":7,"role":2,"site":3}' | jq .

Two conventions keep VLAN data useful. First, always set the VLAN on the prefix as well as the site, so the L3 view answers "which subnet is VLAN 20" without a second lookup. Second, model subinterfaces with the standard NetBox name - GigabitEthernet0/1.20 for VLAN 20 - because that convention is what lets config templates derive the interface name from the VLAN ID instead of relying on a hard-coded mapping.

IP addresses, roles and interface assignment

An IP address in NetBox is more than an address: it carries a role, a DNS name, a status and an assignment. Roles are what prevent the classic argument about which of a device's five loopbacks is "the" management address:

  • Primary - the address most tooling and monitoring should use, and the one referenced as the device's primary IP.
  • Secondary - additional addresses on the same interface, including anycast and VIP addresses.
  • OOB - out-of-band management, usually on an entirely separate network.
  • Loopback, Anycast, VIP - roles that make an address's purpose queryable instead of tribal knowledge.
# an address attached to a specific interface, with role and DNS name
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://netbox.example.com/api/ipam/ip-addresses/ \
  --data '{"address":"10.10.20.12/24","status":"active","role":"loopback",
           "dns_name":"sw-floor1-01.mgmt.example.com",
           "assigned_object_type":"dcim.interface","assigned_object_id":4211}' | jq .

# set the primary IP on the device object itself
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://netbox.example.com/api/dcim/devices/912/ \
  --data '{"primary_ip4":50123}' | jq '.primary_ip4.address'

Statuses matter as much as roles here. Mark DHCP-served addresses as DHCP rather than Active, mark reservations as Reserved, and use Deprecated plus a description for addresses that must keep working during a migration. A report over "Active addresses with no interface assignment" is then a reliable list of documentation debt, and it stays short only if the statuses are used honestly.

Handling overlapping addressing with VRFs

Every IP object can be assigned to a VRF, and each VRF maintains its own isolated hierarchy. That is how you track the same 10.0.0.0/24 in five different tenants without a conflict. Define the VRF once with a unique route distinguisher, then assign it whenever you create overlapping prefixes or addresses. Mark the primary VRF in your templates so operators do not create duplicate space in the wrong context.

The RD is the only field in a VRF that has to be globally unique, so treat it as a controlled namespace rather than free text - site number plus a local counter works well. Anything else useful about the VRF (import and export targets, the owning tenant, whether it is a transit VRF) belongs in custom fields, because NetBox will not validate it for you:

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://netbox.example.com/api/ipam/vrfs/ \
  --data '{"name":"TENANT-A","rd":"65000:101","description":"tenant A production VRF"}' | jq .

# scope a prefix to that VRF, then find it by VRF name
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://netbox.example.com/api/ipam/prefixes/?vrf=TENANT-A&family=4&limit=0" \
  | jq -r '.results[] | "\(.prefix)  \(.status)  \(.description)"'

Keeping it current with the REST API

# token in the header, JSON in and out
TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxx

# find the next free prefix inside a container
curl -s -H "Authorization: Bearer ***"   "https://netbox.example.com/api/ipam/prefixes/?within=10.10.0.0/16&status=container"

# create a prefix scoped to a site
curl -s -X POST -H "Authorization: Bearer ***" -H "Content-Type: application/json"   https://netbox.example.com/api/ipam/prefixes/   --data '{"prefix":"10.10.99.0/24","scope_type":"dcim.site","scope_id":6,"status":"active"}' | jq .

# reserve an address, then read it back
curl -s -X POST -H "Authorization: Bearer ***" -H "Content-Type: application/json"   https://netbox.example.com/api/ipam/ip-addresses/   --data '{"address":"10.10.99.5/24","status":"active"}' | jq .

# list only written-down details to audit
curl -s -H "Authorization: Bearer ***"   "https://netbox.example.com/api/ipam/ip-addresses/?status=active&limit=0" | jq 'length'

Use ?brief=true for dropdown-style lookups; it returns only identifying fields and keeps large automation runs fast. Write operations always need a token, and every create should carry a description so a human can tell why the object exists.

The filters worth memorising are the ones that turn NetBox into a query engine rather than a form. ?within= and ?within_include= walk the hierarchy, ?family=4|6 splits address families, ?status= and ?role= slice by intent, ?vrf= resolves the overlapping-space problem, ?assigned_object_id= answers "what is on this interface", and ?limit=0 returns everything for a script that is going to filter locally anyway. Prefer PATCH for updates so a partial payload cannot silently blank fields that another team maintains.

Automating with pynetbox

For anything beyond a one-off lookup, use the official Python client instead of assembling cURL calls. pynetbox wraps the same API with objects and iterators, which makes reconciliation scripts short enough to review:

import pynetbox

nb = pynetbox.api('https://netbox.example.com', token=TOKEN)

# every active /24 under one container, with its site
for pfx in nb.ipam.prefixes.filter(within='10.10.0.0/16', status='active'):
    print(pfx.prefix, pfx.site, pfx.vlan, pfx.get_available_ips().size)

# interfaces and addresses for one device, as records
dev = nb.dcim.devices.get(name='sw-floor1-01')
for intf in dev.interfaces:
    for ip in intf.ip_addresses:
        print(intf.name, ip.address, ip.dns_name)

# allocate the next free address in a prefix and record why
prefix = nb.ipam.prefixes.get(prefix='10.10.20.0/24')
addr = prefix.available_ips.create({'description': 'new access switch SVI'})
print(addr.address)

Because the client raises an exception on an invalid field, an automation run fails loudly instead of writing half-true data - which is exactly what you want from the system that everything else trusts.

Automation patterns that work

The highest-value integration is generating device configuration from NetBox instead of typing it: pull the interface list, IPs and VLANs for a device, render Jinja2 templates, and push with Ansible or NAPALM. The second is a reconciliation job that compares show ip interface brief output with the database and opens a ticket for every mismatch - that is the loop which actually keeps a source of truth accurate. Useful companions on this site: Ansible inventory and playbooks, NAPALM getters and config diff, and NETCONF vs RESTCONF vs gNMI.

A reconciliation loop that stays manageable has three properties: it reads the device with a getter rather than a screen scrape, it compares only the fields NetBox considers authoritative (addresses, masks, VLAN IDs, interface descriptions), and it reports - it does not auto-correct. Automatic correction without review turns a documentation bug into a network outage, and destroys the audit trail that made the mismatch visible in the first place.

Discipline that keeps NetBox useful

Define a small set of statuses and enforce them; require a description on every prefix and address; run the reconciliation job weekly and treat mismatches as incidents; and never let a deployment process create address space outside NetBox. A source of truth that is 90% accurate is worse than no source of truth, because people stop checking it.

Three operational habits carry most of the weight. Turn on change logging (it is on by default) and review it during incidents, because it answers "who reserved this address and when" faster than any log search. Restrict write access with permission groups so scripts use service tokens with only the object types they need. And schedule real backups: manage.py dumpdata through the container, or the supported nb-dump / database backup path, restored into a test instance periodically - a source of truth that has never been restored is a source of truth you are trusting blindly.

原文链接:https://netbox.readthedocs.io/en/stable/features/ipam/