Adopt DHCP self-registration safely
The self-inflicted outage in every DHCP-DNS story is scavenging configured shorter than the lease renewal cycle: healthy machines’ records quietly deleted by the cleanup meant for dead ones. This tutorial does the adoption in the order that cannot produce that outage: seed first so nothing goes dark, cut DHCP over and prove registration, and only then enable scavenging with a window derived from the lease time, plus the verification drill that proves it spares the living.
1. Create the forward and reverse zones
Section titled “1. Create the forward and reverse zones”Create dyn.corp.example.internal. and the matching reverse zone
(1.0.10.in-addr.arpa. for 10.0.1.0/24). One expectation to set
correctly now: DTM’s automatic PTR maintenance (maintain_reverse) applies
to records written through the API, UI, and Terraform, not to RFC 2136
updates. DHCP-registered PTRs come from the DHCP server updating the
reverse zone itself, which every standard DDNS implementation does; the
reverse zone just has to exist and accept its key.
2. Seed once from the old server
Section titled “2. Seed once from the old server”Export the current zone from Windows DNS (Export-DnsServerZone) and
import it, so every existing hostname resolves from
DTM throughout the transition. Two facts about what you just created:
- Imported records are static: the scavenger never touches them, so the seed cannot be eaten no matter what you configure later.
- A seeded static record converts to dynamic only when the DHCP server deletes and re-creates it; an in-place renewal of the same value leaves it static. Whether that happens depends on your DHCP server’s update style (many, including Kea by default, do not re-send updates on an unchanged renewal), so parts of the estate may stay static indefinitely. That is safe, just worth knowing for step 7.
3. Enable dynamic DNS with TSIG required
Section titled “3. Enable dynamic DNS with TSIG required”Follow dynamic DNS registration: create a TSIG key
and bind it to both zones (forward and reverse), then enable updates
with require_tsig: true (these two switches are the runtime API, no
restart). The danger aside from that page applies verbatim: never run with
unsigned updates accepted.
4. Point the DHCP server at DTM
Section titled “4. Point the DHCP server at DTM”Configure Kea (or dhcpd) DDNS with the TSIG key, the DTM node IPs as the update targets, and both forward and reverse updates on. Verify one lease end to end before touching the estate:
# from a test client: renew the lease, thendig @10.0.250.4 testhost.dyn.corp.example.internal +short # forwarddig @10.0.250.4 -x 10.0.1.57 +short # PTR, written by DHCPcurl -sk "https://10.0.250.4:8443/api/v1/audit?resource=record&limit=10" \ -H "Authorization: Bearer $DTM_TOKEN" # attributed to the key5. Size the scavenging window from the lease
Section titled “5. Size the scavenging window from the lease”Now, and only now, the cleanup. The rule: scavenge_max_age comfortably
above twice the lease time. Eight-hour leases renew at four hours, so
records refresh at least every eight; 86400 (24 hours) gives a 3x margin
and still clears a decommissioned machine within a day:
dynamic_dns: scavenge_max_age: 86400 # seconds; 0 disables scavenge_interval: 3600These two keys are file-or-environment only, with no runtime API; the rolling restart in step 6 is what applies them (configuration reference). The enable and TSIG switches you already set via the API stay where they are.
6. Apply with a rolling restart, knowing what the first restart means
Section titled “6. Apply with a rolling restart, knowing what the first restart means”Edit the file on every node, then restart one node at a time gated on
/readyz returning 200.
7. Watch one full cycle: the living survive, the leftovers surface
Section titled “7. Watch one full cycle: the living survive, the leftovers surface”Over the next lease cycle plus your max age:
- Actively renewing hosts survive: every renewal, even an identical-address one, refreshes the record’s age. Verify a known-active host is still present after the max-age window has elapsed; that dig is the proof the sizing is right.
- Static leftovers are candidates, not corpses: seeded records are
static and never scavenged, and because a same-value renewal does not
convert a record to dynamic, a
"dynamic": falserecord after a full lease cycle may belong to a decommissioned machine or to a live host whose DHCP server never re-created the record. List the static survivors, then verify each before deleting: no active lease on the DHCP server and no query traffic for the name means it is safe to remove.
curl -sk https://10.0.250.4:8443/api/v1/zones/<zone-id>/records \ -H "Authorization: Bearer $DTM_TOKEN" | jq '[.[] | select((.dynamic|not) and (.system|not)) | .name]'# cross-check each against the DHCP lease table before deleting (the extra# .system filter drops DTM's read-only synthesized NS records, which are# not candidates)That list is your inherited-cruft shortlist, made visible instead of guessed at; the lease table is the judge.
8. Cut resolvers over and retire the old target
Section titled “8. Cut resolvers over and retire the old target”Point the office VNet or DHCP option 006 at the DTM nodes, verify a client resolves both directions, then remove the DDNS target and the zone from Windows DNS once its query logs go quiet.
Rollback lever, should you ever need it: setting scavenge_max_age: 0
and rolling the restart disables scavenging entirely without touching a
single record.
Where next
Section titled “Where next”- Dynamic DNS registration: the mechanism reference this journey wraps.
- Migrate Windows DNS and BIND zones: the full-estate migration this office move slots into.
- Active Directory coexistence: where the GSS-TSIG boundary comes from, and what stays on the DCs.
Need a hand? Email [email protected].
Last validated: 2026-07-27