netbox-kea-ng
Fork notice: This is
netbox-kea-ng, an independently maintained fork of netbox-kea by Devon Mar. It is published to PyPI asnetbox-kea-ngand tracked in this repository. Upstream changes are periodically merged where applicable.
NetBox plugin for the Kea DHCP server. Manage your DHCP infrastructure directly from NetBox — view daemon status, search and manage leases, manage host reservations, configure subnets/pools/options, and keep your NetBox IPAM synchronized with live Kea data via a background job.
Features
Core (from upstream)
- View Kea daemon status (DHCPv4/DHCPv6 daemons — and the Control Agent on Kea < 3.0)
- Full DHCPv4 and DHCPv6 support
- Search, view, delete and export DHCP leases
- Search for NetBox devices/VMs directly from DHCP leases
- View DHCP subnets from Kea configuration
- REST API and GraphQL support for Server objects
Additions in this fork
Host Reservations
- Full CRUD for DHCPv4 and DHCPv6 reservations via
host_cmdsandsubnet_cmdshooks - Identifier types: hw-address (v4), DUID (v6), client-id, flex-id, circuit-id, remote-id
- Reservations that reserve no address — an identifier-only host (hostname, options or client classes only) and a DHCPv6 host that only delegates prefixes. Both are listed, created, edited and deleted by client identifier instead of by IP, and the IPAM sync reports them as skipped: there is no address to record in NetBox. Their Lease column matches the client identifier against the leases of the reservation's own subnet, there being no reserved address to match on
- Per-reservation DHCP options
- The hostname field takes the name that clients get. Kea adds the effective
ddns-qualifying-suffix(the Pool that contains the reserved address, then the Subnet, the Shared Network and the global value) to a reserved hostname, except to a name that ends in a dot or already ends in the suffix. When a Pool of the Subnet sets a suffix, a Reservation without an address has no known published name, and the form refuses a hostname for it. The form shows a preview of the published name, and the plugin stores the hostname that makes Kea publish the entered name: the labels without the suffix, a single label as entered (Kea qualifies it), or any other name with a trailing dot. A saved Reservation keeps its stored hostname until you edit the name - Journal entries on add/edit/delete
- Validated YAML or JSON bulk transfer. Export a Complete Snapshot from one server or the combined view, then import it from the matching DHCPv4 or DHCPv6 Reservations page
Subnet Management
- Add, edit and delete subnets (requires
subnet_cmdsorconfig-set) - Pool management (add/delete pools per subnet)
- Shared network management (add/edit/delete)
- Per-subnet and global DHCP option editing
IPAM Sync
- Sync current address leases → NetBox
IPAddress(statusdhcp) - Sync current DHCPv6 delegated-prefix leases → NetBox
Prefix, when lease sync and Prefix sync are both on - Sync reservations → NetBox
IPAddress(statusreserved) - Sync button on individual leases and reservations
- Bulk sync for entire lease tables
- Pending-change detection: badge on leases where a reservation exists at a different IP
- MAC address sync → NetBox
MACAddress - Sets
dns_nameon IPAddress for automatic DNS sync via netbox-dns IPAMDNSsync
Periodic Background Sync (requires rqworker)
- Automatic Kea→NetBox IPAM sync on a configurable interval (default 5 minutes)
- Syncs all current leases and reservations from all configured servers
- Visible in NetBox System → Background Jobs
DHCP Control
- Enable/disable DHCPv4 and DHCPv6 daemons from the NetBox UI
Server connection validation
- Creating a Server or changing connection values checks every enabled DHCP service.
- Connection values include URLs, credentials, TLS settings, Control Agent routing and enabled DHCP families.
- Metadata changes, such as names, tags and sync settings, work while Kea is unavailable. Submitting unchanged connection values sends no connectivity request.
- Bulk edits reject the entire batch if one changed connection fails its check.
Dual-URL Server
- Optional separate URLs for the DHCPv4 and DHCPv6 endpoints
- Supports Kea 3.0+ (each daemon exposes its own HTTP control socket) and split v4/v6 deployments
Global / Cross-Server Views
- Combined dashboard, lease, reservation, subnet and shared-network views across all servers
Lease Add / Edit / Delete / Bulk Import
- Add and edit individual leases, and bulk import leases from CSV. A DHCPv4 lease needs a hardware
address (
hw-address), because Kea refuses one without it. A CSV row that is not a valid lease rejects the whole file; the message names the file line and the column, never the value. A line number counts every line of the file, also the header, comments and blank lines. - Edit, Delete and Reserve act on the allocation that the row shows: an address, or a DHCPv6 delegated
prefix (
address/length). Reserve on a delegated prefix fills the Reservation prefix field. - Edit and Delete read the lease again before they change it. They change nothing when the client
binding (MAC address and client ID, or DUID and IAID), the Subnet, the kind, the prefix length or an
edited field differs from the page; reload and try again. A renewal alone does not stop the change,
and an edit keeps the renewed times and Kea's extension values, such as
user-context. This is a check before the change, not an atomic compare-and-write: another writer can still change the lease between the check and the change. A lease that is gone is never created again. - An edit writes only the fields that you change. A blank hostname clears the hostname; a blank client identifier or valid lifetime keeps the current value.
- After an add, the plugin reads the new lease back from Kea. Sync to NetBox IPAM claims it only when that read shows a current lease whose client binding, and Subnet and hostname when the form gave them, match the request.
- A Reservation of the client that holds only the other kind (addresses for a delegated prefix, or prefixes for an address) shows as Reservation, not Reserved, and the row offers no + Reserve.
- The
lease_addedandleases_deletedsignals carry typed values (seenetbox_kea/signals.py).lease_addedsendscreation, the request that Kea confirmed, andlease, the lease that the read back observed, orNonewhen that read failed or does not match the request. A CSV import sendslease_addedfor each row that Kea created, withleaseNone, because it reads nothing back.leases_deletedsendsleases, the leases whose deletion Kea confirmed. A refused, changed or failed action sends no signal. Wiping the leases of a Subnet sends no signal, because Kea does not report which leases it removed.
Lease observations
-
The plugin validates each lease record that Kea returns. A malformed record is left out and the page shows a safe reason for it (a code, the Kea field and the record position, never the rejected value). The other leases stay visible. A malformed record never counts as "no lease".
-
Only a Current Lease is live evidence: an assigned address or delegated prefix, or a registered DHCPv6 address, whose valid lifetime has not ended. An infinite lifetime never ends. The Reservation Lease column shows No Lease only when the lease query of that Reservation is complete; otherwise it shows Lease Unknown with the reason.
-
The lease REST actions (
/api/plugins/kea/servers/<pk>/leases4/andleases6/) take one filter:ip_address,hw_address(DHCPv4),duid(DHCPv6),hostnameorsubnet_id. When a request gives more than one, the first in that order is used.statekeeps only the leases in one Kea state code (0assigned to4registered) for every filter. Asubnet_idsearch takes only0(assigned) or1(declined), because Kea counts only those states for the query guard. They return one normalized observation:count,results,diagnostics,complete,next_cursor,query(family,selector,value,state),coverage(exhaustiveorpage) andevaluated_at. Each result hasfamily,kind(addressordelegated-prefix),address,prefix_length,subnet_id,state(the Kea state name),current,binding(hw_addressandclient_idfor DHCPv4,duidandiaidfor DHCPv6),hostname,valid_lifetime,last_transactionandexpiration(infinite,expires_at). Each diagnostic hascode,field,message,source_positionandkinds. The response contains no Kea extension values, such asuser-context, and no display labels. A search covers its own query scope only, socompletenever means that the daemon has no other leases. -
A DHCPv6 IP Address search reads the address and the delegated prefix at that address. It is complete only when Kea confirms both. A Subnet search for a CIDR that Kea does not configure is refused, because Kea can keep leases under a Subnet ID that is no longer configured: search by Subnet ID instead. When
lease_query_max_unpaged_leasesis set, a Subnet read that returns more leases than the limit is refused too, because Kea's statistics do not count every lease state. -
Export All Leases and All Data are complete exports. They refuse an incomplete observation (a malformed record, or more leases than the export limit) and show the reason. Their columns are:
- DHCPv4:
family,kind,address,prefix_length,subnet_id,state,current,hostname,valid_lifetime,last_transaction,infinite,expires_at,hw_address,client_id. - DHCPv6:
family,kind,address,prefix_length,subnet_id,state,current,hostname,valid_lifetime,last_transaction,infinite,expires_at,duid,iaid,hw_address,preferred_lifetime.
kindisaddressordelegated-prefix.stateis the Kea state name (assigned,declined,expired-reclaimed,released,registered) andcurrentistrueorfalse. Lifetimes are in seconds.last_transactionandexpires_atare ISO 8601 times in UTC;expires_atis empty wheninfiniteistrue. An empty cell is a value that the lease does not have. - DHCPv4:
-
Current View (limited coverage) exports the readable leases of the search, in the columns that the table shows, as
leases_limited_coverage.csv. It is not a complete export.
Upgrading from 1.x: lease contracts that changed
Integrations that read leases from the plugin must change with the typed lease release (2.0 and later):
- The
leases4andleases6REST actions return the observation above. The Kea fields with underscores, such asip_address,valid_lft,clttanduser_context, and the display keysstate_label,expires_inandexpiry_classare gone. Checkcompletebefore you treat a missing lease as absent. - Export All Leases and All Data use the documented columns and refuse an incomplete observation.
lease_addedsendscreationandleaseinstead ofip_address,hw_addressandhostname.leases_deletedsendsleasesinstead ofip_addresses. A delegated prefix is aDHCPv6PrefixLeasewith its prefix length, not an IP address.- The DHCPv4 lease CSV import requires the
hw-addresscolumn. - The IPAM sync records a delegated-prefix lease as a Prefix, not an IP address. The first complete runs after the upgrade repair the IP addresses that earlier releases made of delegated prefixes (see Delegated-prefix leases from earlier releases).
Requirements
- NetBox 4.3 – 4.7
- Kea 3.0+ (recommended) — the plugin connects directly to each daemon's built-in HTTP control socket (
kea-dhcp4/kea-dhcp6). The Kea Control Agent was deprecated in Kea 2.7 and removed in 3.0; on Kea < 3.0, point the server URL at the Control Agent instead. lease_cmdshook library (for lease search and management)stat_cmdshook library (for guarded Subnet lease searches unlesslease_query_max_unpaged_leasesis0)- Kea 3.1.5+ for state-filtered Subnet lease searches. Earlier releases do not provide the scoped state commands, so these searches fail closed.
host_cmdshook library (optional, for reservation management — also requiressubnet_cmdsto resolve a reservation's subnet from its CIDR)subnet_cmdshook library (optional, for subnet add/edit/delete, reservation management, and the subnet suggestions on the lease search and reservation forms)
The plugin degrades gracefully when optional hooks are absent — tabs for unavailable features are hidden automatically. Two pages offer the server's configured subnets as suggestions and read them through subnet_cmds; without that hook each says so in a banner rather than silently offering nothing:
- Lease search keeps working: the Search field offers no subnet suggestions, so type an exact configured subnet CIDR or ID.
- Add reservation cannot save, because resolving the entered CIDR to a Kea subnet ID needs
subnet_cmds. Load the hook first.
Compatibility
| netbox-kea-ng | NetBox | Kea |
|---|---|---|
| 1.x | 4.3 – 4.7 | 3.0+ recommended (2.4+ via Control Agent) |
On Kea 3.0+ the plugin talks directly to each DHCP daemon's HTTP control socket; on Kea < 3.0 it connects through the (now-deprecated) Control Agent. CI tests against Kea 3.2.0 using the memfile lease database.
netbox-branching
In a netbox-branching branch, live Kea and import operations are read-only. CI tests
NetBox 4.7 with netbox-branching 1.2.1. List netbox_branching last in PLUGINS.
- Kea Servers, sync settings and IPAM ownership links stay in main. A branch has no copy of them, so it shows main's values.
- DHCP Import Mappings follow imported Subnets and Global Reservations in a fresh branch. Deleting either target removes its branch mapping while main retains both. Discard preserves main. Squash merge deletes the matching main pair; revert restores both original identities, so reimport updates the restored object. This applies to IPv4, IPv6, instance deletion and queryset deletion. Mapping recovery adds no manual editor or branch-local import.
- Kea data is live, in main and in every branch.
- In a branch, the plugin refuses every change that comes through its web pages or its REST API,
before it sends anything to Kea or writes to the database. A page shows HTTP 409 with a link to
main; the REST API answers 409 with the code
branch_write_refused. Plugin pages show a banner, and plugin and GraphQL responses carry anX-NetBox-Kea-Sourcesheader. - A change request that still selects a branch that you can no longer use changes nothing. A
_branchquery that names a merged, archived, or not ready branch, and anactive_branchcookie that names such a branch or a deleted one, get HTTP 409 with the codebranch_selection_unusable. netbox-branching answers HTTP 400 first when a_branchquery names a deleted branch, and when an APIX-NetBox-Branchheader names a deleted or unusable branch. Select main (?_branch=) and try again. - Code that runs outside a web request (a custom script, for example) is also refused in a branch.
A save or a delete of a Kea Server, sync settings or an IPAM ownership link raises
BranchActive, and so does a Kea command that changes Kea, from a client thatServer.get_client()returns. The periodic IPAM sync job fails when it runs in a branch. - A merge fails, and changes nothing, when the branch deletes a VRF that a Kea server in main now syncs into. Clear or change that server's Sync VRF, then merge again.
- In a branch, a delete of an IP address, Prefix or IP Range that a Kea server owns is refused, and so is a delete of a device or virtual machine that holds such an IP address, because the ownership data exists in main only. NetBox shows the refusal as an error message; the REST API answers 400.
DHCP mapping recovery. Select the squash strategy for affected branches. Recovery refuses the whole action if main has conflicting changes, history is incomplete, a source Server is missing, an identity has been reused, or restoration would violate a source or target constraint. A busy transaction refuses with a retry message. Resolve the reported conflict before retrying; newer main state is preserved. Older branches keep independent read pages, but their DHCP Plugin tab requires a fresh branch. A mapping, target or required Tag branching exemption also requires a fresh branch after the exemption is removed. Existing branch schemas and missing history are not retrofitted. Apply Tag changes separately on main or in a Tag-only branch, then create a fresh branch for DHCP changes. Recovery refuses mixed Tag changes and mapped DHCP target replay, including unrelated Tag changes in the same branch. A required missing, renamed or replaced Tag also causes a refusal.
Mappings lost under the previous policy require explicit operator repair. Preserve the existing target, verify its Server, family and source identity from trusted records, and repair the association on main through an audited operator procedure. A matching target name does not prove that association. See the recovery contract.
Upgrade with open branches. Earlier releases let netbox-branching copy the Kea servers table into each new branch. Nothing removes that copy: branch sync no longer updates it, and branch migrate does not drop it. NetBox still reads Kea servers from main, but a VRF delete in the branch checks the old copy, not main. A merge also applies to main every Kea server change that the branch recorded before the upgrade. Before you upgrade, merge or delete every branch that is not yet merged, and create new branches after the upgrade. If a branch changed a Kea server password, delete it and make the change again in main: the change log holds a placeholder, not the password, so a merge can write the placeholder to main. A merged branch keeps its schema until you archive it; archive it when you no longer need to revert it.
Installation
1. Install the package
Add netbox-kea-ng to your local_requirements.txt (or install with pip):
pip install netbox-kea-ng
2. Enable the plugin
In configuration.py:
PLUGINS = ["netbox_kea"]
Optionally configure plugin settings (see Configuration):
PLUGINS_CONFIG = {
"netbox_kea": {
"kea_timeout": 30,
"lease_query_max_unpaged_leases": 1000,
"sync_interval_minutes": 5,
"sync_leases_enabled": True,
"sync_reservations_enabled": True,
"sync_prefixes_enabled": True,
"sync_ip_ranges_enabled": True,
"sync_max_leases_per_server": 50000,
"stale_ip_cleanup": "remove",
}
}
3. Run migrations
./manage.py migrate
4. Start the background worker (required for periodic sync)
The periodic IPAM sync job runs via NetBox's built-in rqworker. If you're not already running it:
./manage.py rqworker
The Kea IPAM Sync job will appear under System → Background Jobs and runs on the configured interval.
Configuration
All settings are under PLUGINS_CONFIG["netbox_kea"]:
| Setting | Default | Description |
|---|---|---|
kea_timeout |
30 |
HTTP request timeout in seconds for Kea API calls |
lease_query_max_unpaged_leases |
1000 |
Reject an unpaged Subnet lease query when its Kea statistics count exceeds this limit. Set to 0 to disable this safety check |
stale_ip_cleanup |
"remove" |
What to do with stale IPs after sync: "remove" (delete), "deprecate" (set status=deprecated), "none" (skip) |
sync_interval_minutes |
5 |
Initial interval of the background sync job (minutes). Edit it later on the Sync Jobs page |
sync_enabled |
True |
Initial state of the global sync switch. Edit it later on the Sync Jobs page |
sync_leases_enabled |
True |
Sync current DHCP address leases to NetBox IPAM. With sync_prefixes_enabled, also DHCPv6 delegated-prefix leases as Prefixes |
sync_reservations_enabled |
True |
Sync Kea reservations to NetBox IPAM |
sync_prefixes_enabled |
True |
Sync Kea subnets to NetBox IPAM as IP Prefixes. With sync_leases_enabled, also DHCPv6 delegated-prefix leases |
sync_ip_ranges_enabled |
True |
Sync Kea pools to NetBox IPAM as IP Ranges |
sync_max_leases_per_server |
50000 |
Hard cap on leases fetched per server per sync run. Set to 0 for no limit |
NetBox refuses to start when one of these settings has a value of the wrong type or outside its range.
The error names the setting, the allowed values, and the given value. A number must be an integer, not a string
or a boolean. kea_timeout must be at least 1, sync_interval_minutes must be from 1 to 1440, and the other
numbers must be at least 0.
./manage.py migrate reads sync_interval_minutes, sync_enabled and the four sync_*_enabled
toggles once, when it creates the Sync Configuration. After that, the Sync Jobs page holds these values,
and a later change to these settings in PLUGINS_CONFIG has no effect.
Subnet lease searches use stat-lease4-get or stat-lease6-get before an
unpaged lease command. Kea statistics can reject a query that is already too
large. They cannot prove that an unqualified query is below the limit because
stored expired states are not included in all statistics. Select Active or
Declined to narrow a large query. Other states require an exact IP address or
client identifier search. The guard fails closed when the stat_cmds hook is
not available. State-filtered searches also fail closed when Kea does not
support the scoped state commands. Set the limit to 0 only when you accept
unbounded responses.
A Configuration Change holds a PostgreSQL advisory lock in an open transaction
for all of its Kea requests, up to and including the save to disk. A PostgreSQL
idle_in_transaction_session_timeout or a connection pooler timeout shorter than
the longest Configuration Change ends that transaction and releases the lock
while the change runs, so set these timeouts longer.
Server Configuration
Single-URL (Control Agent, or a single-protocol daemon)
Point one Server URL at a Kea endpoint that serves every enabled protocol — a Control Agent (Kea < 3.0, which fronts both DHCPv4 and DHCPv6), or a single DHCP daemon's HTTP control socket (Kea 3.0+) when the server runs only DHCPv4 or only DHCPv6. A dual-stack Kea 3.0+ deployment needs one URL per daemon — see Dual-URL below.
| Field | Description |
|---|---|
CA / Server URL (ca_url) |
URL of the Kea HTTP endpoint — a DHCP daemon control socket (Kea 3.0+) or the Control Agent (Kea < 3.0), e.g. https://kea.example.com:8000 |
DHCPv4 |
Enable DHCPv4 lease/reservation/subnet management |
DHCPv6 |
Enable DHCPv6 lease/reservation/subnet management |
CA Username (ca_username) / CA Password (ca_password) |
HTTP Basic Auth credentials (if required) |
CA File Path |
Path to a custom CA certificate file for TLS verification |
SSL Verification |
Enable/disable TLS certificate verification (enabled by default) |
Dual-URL (separate v4/v6 processes)
When DHCPv4 and DHCPv6 have separate endpoints — the norm on Kea 3.0+, where each daemon exposes its own HTTP control socket:
| Field | Description |
|---|---|
DHCPv4 URL |
URL of the DHCPv4 daemon's HTTP control socket (or its Control Agent on Kea < 3.0) |
DHCPv6 URL |
URL of the DHCPv6 daemon's HTTP control socket (or its Control Agent on Kea < 3.0) |
The main CA URL (ca_url) is required and acts as a fallback for any protocol without a dedicated URL.
By default, both DHCPv4 URL and DHCPv6 URL use CA-level credentials; see Per-protocol credentials below for optional overrides.
Per-protocol credentials
When connecting directly to DHCP daemons (bypassing the Control Agent), you can configure separate credentials per protocol:
| Field | Description |
|---|---|
dhcp4_username |
Username for the DHCPv4 daemon (overrides ca_username for DHCPv4) |
dhcp4_password |
Password for the DHCPv4 daemon (overrides ca_password for DHCPv4) |
dhcp6_username |
Username for the DHCPv6 daemon (overrides ca_username for DHCPv6) |
dhcp6_password |
Password for the DHCPv6 daemon (overrides ca_password for DHCPv6) |
If per-protocol credentials are not set, the CA-level credentials (ca_username/ca_password)
are used as the default for all connections.
Per-server IPAM sync settings
Each server has optional overrides for the IPAM sync job:
| Field | Default | Description |
|---|---|---|
IPAM Sync Enabled (sync_enabled) |
True |
Include this server in the periodic sync job |
Sync Leases (sync_leases_enabled) |
True |
Sync current DHCP address leases as NetBox IP Addresses. With Sync Prefixes, also DHCPv6 delegated-prefix leases as Prefixes |
Sync Reservations (sync_reservations_enabled) |
True |
Sync DHCP reservations as NetBox IP Addresses |
Sync Prefixes (sync_prefixes_enabled) |
True |
Sync Kea subnets as NetBox IP Prefixes. With Sync Leases, also DHCPv6 delegated-prefix leases |
Sync IP Ranges (sync_ip_ranges_enabled) |
True |
Sync Kea pools as NetBox IP Ranges |
Deprecate stale Prefixes and IP Ranges (sync_deprecate_prefixes_and_ranges) |
False |
Deprecate an owned Prefix or IP Range when this server drops its last ownership link as stale. These objects are never deleted |
Sync VRF (sync_vrf) |
None (global routing table) | VRF to assign when syncing Prefixes, IP Ranges, and lease and reservation IP Addresses. There is no global fallback: leave blank to use the global routing table (no VRF). NetBox refuses to delete a VRF while a server syncs into it |
First complete IPAM observation (ipam_first_complete_at) |
None | Read-only time when this server first completed all enabled job and DHCP import observations after the ownership upgrade |
Persist configuration (persist_config) |
True |
Automatically save Kea config after each change via config-write. Disable when Kea config is managed externally (e.g. Ansible) |
These fields override the global PLUGINS_CONFIG values for that specific server.
Background IPAM Sync
The Kea IPAM Sync job runs automatically when rqworker is active:
- Iterates all configured
Serverobjects - For each server: fetches all leases (v4 + v6) and all reservations. Only a current lease is an owner: an assigned address or delegated prefix, or a registered address, whose valid lifetime has not ended
- Creates or updates NetBox
IPAddressobjects in the server'ssync_vrf, and links each one to the server and its source (lease or reservation):- Leases →
status=dhcp,dns_nameset from the lease hostname, without a trailing dot - In-subnet reservations →
status=reserved,dns_nameset from the published name: the reserved hostname with the effectiveddns-qualifying-suffix, as Kea gives it to the client - An address with both a lease and a reservation →
status=active; the reservation hostname wins - The MAC address description
dhcp_hostname: <name>takes the same name from both sources, so a lease and its reservation do not change it on each run - Global reservations create and change no IP address
- The description starts with the sync marker
[kea-sync: <kind>]. Text after the marker is an operator note, and the sync keeps it. To release an address from the sync, remove the marker or move it away from the start.
- Leases →
- Cleans up stale IPs (configurable via
stale_ip_cleanup). A server's lease or reservation that Kea no longer reports loses its link. When the last link of every server to an address goes,stale_ip_cleanupapplies, but only after a complete lease sync and a complete reservation sync. If the server disables reservation sync, a complete lease sync is sufficient. An unavailablehost_cmdshook leaves the reservation source incomplete, because config-file reservations can still exist. Failed or truncated lease reads keep their stale links. - Links Subnets to Prefixes and Pools to IP Ranges in the server's
sync_vrf. A complete Subnet or Pool phase drops its own stale links. These objects stay unchanged by default and are never deleted. The server that drops the last link can opt in to deprecation withsync_deprecate_prefixes_and_ranges. A deprecated object's last link stays marked stale until an applied report restores its active status, or another owner supersedes the stale link. Removing the description marker releases every link without deprecation. DHCP plugin references prevent deprecation. - Links each current DHCPv6 delegated-prefix lease to a Prefix in the server's
sync_vrf(sourcelease-prefix), when lease sync and Prefix sync are both on. The DHCP plugin import owns delegated prefixes through its own source, so neither cleans up the other's links. These Prefixes follow the rules of step 5. A delegated prefix never creates an IP address - One server failing does not block others
- Summary logged per server and in total
Each execution groups its native changelog records under one request ID, including MAC address changes.
Sync now records the initiating user. Scheduled runs use the reserved user netbox-kea-sync.
A job whose initiating user was deleted also uses system attribution.
The job creates this attribution account inactive, with an unusable password and no permissions. Keep it inactive and without access grants. If an existing account with that name has login access or permissions, the job fails before synchronization and leaves the account unchanged.
Each server's summary reports created, updated, errors, prefix_errors, conflicts, disagreements,
skipped, waiting and unclassified. The job total also reports unowned:
- skipped: reservations the sync deliberately did not write: global reservations and reservations that reserve no address. They are not errors and do not fail the job.
- errors: lease and reservation rows that failed to sync, snapshots that could not be read, and reservations that the snapshot could not read. Any error fails the job. A failed snapshot and the first 10 failed rows of each server and IP version are logged at warning level, with the source (lease or reservation), the address and the exception.
- conflicts: IPAM objects that the sync left unchanged: the description of the object does not start with the sync marker (it was created by hand, or an operator removed the marker), or the new marker and the note after it do not fit in the 200-character description. They are deduplicated per server across its phases. Up to 20 of them are named in the summary and the log, so the addresses to look at are visible without trawling debug output.
- disagreements: addresses that two Kea servers report with different facts (the hostname or the prefix length), or that one server reports twice with different facts, for example from two reservations in overlapping subnets. A lease and a reservation disagree only on the prefix length, and an empty hostname makes no claim. The address keeps its values until the reports agree, or until one of them goes.
- unowned: marker objects with no ownership link, including objects left by a deleted server. Only the job total reports this count, because these objects have no owning server. It counts each object once.
- waiting: adopted objects whose cleanup this server held during the run while a potential owner had not completed its initial observations. The job total counts objects still waiting after all servers finish. Counts include each object once across sources and address families.
- unclassified: IP addresses that keep a stale DHCPv6 lease link from before the sync recorded the allocation kind (see "Delegated-prefix leases from earlier releases" below).
View job history, next scheduled time and logs under System → Background Jobs → Kea IPAM Sync.
Manual Sync permissions
A manual Sync makes the writes of the automatic sync, and permission constraints do not limit the automatic sync.
Thus a manual Sync runs only for an active superuser, or for a user who has an unconstrained grant of each
permission that the Sync can use. An enabled ObjectPermission of the user, or of a group of the user, gives a grant.
DEFAULT_PERMISSIONS also gives a grant. A grant is unconstrained when it has no constraint set, or an empty one.
| Control | Required permissions |
|---|---|
| Lease Sync, lease add with Sync to NetBox IPAM | ipam.add_ipaddress, ipam.change_ipaddress, dcim.add_macaddress, dcim.change_macaddress |
| Lease Sync of a DHCPv6 delegated prefix | ipam.add_prefix, ipam.change_prefix |
| Reservation Sync all and Sync All to NetBox, Reservation add or edit with Sync to NetBox IPAM | The same as for a lease |
| Sync to DHCP plugin now | The same as for a lease, ipam.add_prefix, ipam.change_prefix, ipam.add_iprange, ipam.change_iprange, change permission on the Server, and the netbox_dhcp permissions below |
Sync All to NetBox runs only the reservation phase. Its stale cleanup cannot delete an IP address, so it needs no
delete permission. When a user does not have a required grant, the Sync control is disabled. Its tooltip names each
missing or constrained permission, and the server refuses the request with the same text. A grant that only an LDAP
group mapping (FIND_GROUP_PERMS) gives does not count.
Sync to DHCP plugin now also writes the DHCP plugin objects. It needs add and change on DHCP Server, Option, Client Class, Subnet and Host Reservation, and add on Option Definition and Pool. The import never changes a Pool or an Option Definition that exists.
Upgrade to ownership links
The first observations adopt marker objects that Kea still reports. No data migration assigns owners.
An object with a blank description stays unchanged unless an operator forces a claim.
When every configured server uses the same non-global sync_vrf, an unowned marker IP address in the global
VRF moves into that VRF with its primary key and changelog intact. A row already owned by another server does
not move. A destination address collision keeps the global row unowned and reports a conflict. Servers with
different VRFs keep the global row and create their own scoped rows. A global-VRF server adopts it in place.
Adopted objects keep their last link until every potential owner completes all relevant enabled observations.
Periodic jobs and DHCP imports record completion separately. An incomplete phase or missing family does not
count as complete. Disabled sources and families do not hold the barrier. The read-only
ipam_first_complete_at field shows when a server first completed its enabled observations. Newly enabled
sources still require complete observations, even when that timestamp is already set.
stale_ip_cleanup = "remove" now also removes the rows of expired leases, deleted Reservations and hostless
leases after their final ownership link becomes stale. On upgrade, lease removal applies only to leases that
expire after adoption. Rows already stale before the upgrade get no link, stay unchanged and count as
unowned. Review these rows before removing them manually.
Delegated-prefix leases from earlier releases
Earlier releases synced a DHCPv6 delegated-prefix lease as an IP address at the prefix base. The sync now records the allocation kind on each lease link. DHCPv4 lease links are address links. Existing DHCPv6 lease links start unclassified, because the stored facts cannot show the original kind:
- An address lease that a complete run reads at that address, current or not, classifies the link as an address link.
- A current delegated prefix at that base classifies the link only after a complete run links its Prefix.
The normal stale cleanup then retires the old IP address under
stale_ip_cleanup, and other owners keep it. - Prefix sync off, a failed Prefix claim (for example an operator Prefix without the marker) or an incomplete lease read keeps the old link unclassified.
- An unclassified link that Kea no longer reports stays, and the summary counts it as
unclassified. Remove the sync marker from the IP address to release it.
To change the sync interval, edit it on the Sync Jobs page. You do not need to restart the worker: the new interval applies after the next scheduled run.
Event rules and webhooks
NetBox queues an event for each change and sends the queue to event rules and webhooks when the request or job ends. The plugin writes each sync row, each DHCP-plugin import family and each import receipt in its own transaction. On NetBox 4.6.9 and later, when such a transaction starts with no other transaction open in a request or in the IPAM sync job, NetBox dispatches its events right after its COMMIT, and none when it rolls back. A failed row or family then sends no event for the changes that it reverted. This has these consequences:
- Events dispatch once per transaction, not once per request or job. An object that two transactions change
sends two events (for example
created, thenupdated), where one request sends one combined event. This can change which event rules match. - The events of these transactions dispatch before the other events of the same request.
- A sync job that fails still dispatches the events of the rows that it committed before the failure.
- Each dispatch reads the enabled event rules once per event type and object type, about two reads for each synchronized address. On a development host, 10,000 such transactions took 151 s, against 127 s for the same changes in one transaction.
- When the dispatch fails after a COMMIT (for example, the events pipeline cannot reach Redis), the operation stops with an error and the committed rows stay. The IPAM sync job logs the error for that server and counts it.
The sync refuses a Reservation row before its first IP address or MAC address write when a hardware address is not an EUI-48 or EUI-64 address, or when NetBox has more than one MAC address row for it. A row whose address is a conflict is reported as a conflict. The exception is a legacy address in the global table: the sync moves it into its VRF before it finds that its description cannot fit, so a bad hardware address refuses that row.
Known limits. NetBox gives a plugin no public way to remove one event from its queue, so these remain:
- Inside a transaction that something else owns, a plugin transaction is a savepoint. When it rolls back, NetBox still dispatches the events of its reverted changes. This applies to the rows of a DHCP-plugin import when a database error, another app's signal receiver, or a MAC address row that another writer duplicated during the row fails it after a change. It also applies to a DHCP-plugin import that a caller runs in its own transaction, and to a deletion that the DHCP mapping guard refuses after NetBox queued the delete event.
- On NetBox 4.3 to 4.6.8,
event_trackingcannot be nested, so every plugin transaction is plain and the events of a failed row dispatch with the request. On these releases a failed request also dispatches its events. - An
on_commithook of another app that raises after a COMMIT drops the events of that transaction. - A netbox-branching merge or revert dispatches the events of each change inside its own transaction. When a later change fails, the merge rolls back, but the events of the earlier changes have already dispatched.
DNS Integration
When netbox-dns with IPAMDNSsync is installed:
- The IPAM sync sets
dns_nameonIPAddressobjects from the name that Kea publishes (see Background IPAM Sync) - IPAMDNSsync picks up
dns_namechanges via Django signals - A/AAAA/PTR records are created automatically (provided matching DNS views + zones exist)
No additional configuration is required — the integration is automatic when both plugins are present.
Custom Links
Add custom links to NetBox models to navigate directly to Kea lease searches.
Replace <Kea Server ID> with your server's object ID (visible in the top-right corner of the server detail page as netbox_kea.server:<ID>).
Show DHCP leases for a prefix
Content type: IPAM > Prefix
URL: https://netbox.example.com/plugins/kea/servers/<Kea Server ID>/leases{{ object.prefix.version }}/?q={{ object.prefix }}&by=subnet
Show DHCPv4 leases for a device/VM interface (by MAC)
Content types: DCIM > Interface, Virtualization > Interface
DHCPv4 URL: https://netbox.example.com/plugins/kea/servers/<Kea Server ID>/leases4/?q={{ object.mac_address }}&by=hw
Kea has no DHCPv6 lease search by MAC address. To find a DHCPv6 lease by client identifier, search by DUID.
Show DHCP leases for a device/VM (by hostname)
Content types: DCIM > Device, Virtualization > Virtual Machine
DHCPv4 URL: https://netbox.example.com/plugins/kea/servers/<Kea Server ID>/leases4/?q={{ object.name|lower }}&by=hostname
DHCPv6 URL: https://netbox.example.com/plugins/kea/servers/<Kea Server ID>/leases6/?q={{ object.name|lower }}&by=hostname
You can substitute {{ object.name|lower }} with a custom field: {{ object.cf.<your_field>|lower }}.
Development
# Install dev dependencies
uv sync
# Lint
uv run ruff check .
uv run ruff format --check .
# REUSE compliance check
uv run reuse lint
# Format
uv run ruff format .
# Install pre-commit hooks
uv run pre-commit install
# Build wheel (required before integration tests)
uv build
# Run unit tests; both variables are required (see AGENTS.md for picking values)
TEST_DB_NAME=test_netbox_kea_local TEST_REDIS_HOST=localhost \
uv run --native-tls pytest --reuse-db -n auto --maxschedchunk=1 -q
# Run integration tests (requires Docker — see tests/test_setup.sh)
./tests/test_setup.sh
uv run --native-tls pytest -p no:django tests/ --tracing=retain-on-failure -v --cov=netbox_kea --cov-report=xml
See CHANGELOG for version history.
License
Apache-2.0 — original code by Devon Mar, fork maintained by Marcin Zieba.
Metadata
Release files for netbox-kea-ng 3.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| netbox_kea_ng-3.1.0.tar.gz | 968.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| netbox_kea_ng-3.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.4 MB
Release files / netbox_kea_ng-3.1.0.tar.gz
| Download URL | netbox_kea_ng-3.1.0.tar.gz |
|---|---|
| Size | 968.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
77bc53fea810824537ddb1e358c6a2144e7a30c69a37ec8dd9bd5a90d9a1b5a1
|
|
BLAKE2b-256 checksum How to use checksums |
a46b1d1b3a673b679a54a1f4a234b896491ab2735ca2b5a7f680a0182940ddfc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / netbox_kea_ng-3.1.0-py3-none-any.whl
| Download URL | netbox_kea_ng-3.1.0-py3-none-any.whl |
|---|---|
| Size | 414.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e6434e1c13179a2d93be8ed794f44a5d5bda2e5e4f6a9ce16736f456f1665a75
|
|
BLAKE2b-256 checksum How to use checksums |
62bb8597810c7cd5811d626f31898bb2bcaf9d9d6deb5622fe15a7bd7e5da8f4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for netbox-kea-ng 3.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.