vinnymarquez.dev/tutorials/migrating off legacy dhcp, to kea
August 5, 20265 min readcheat sheet#networking#homelab#dhcp

Migrating Off Legacy DHCP, to Kea

ISC dhcpd is end-of-life upstream. moving to Kea, field by field, on a flat network — no VLANs required first.

before touching VLANs at all — see From Flat LAN to Segmented Homelab if you’re here for that — there was a smaller, easier migration sitting in front of it.

ISC dhcpd, the classic DHCP server most router/firewall OSes shipped with for years, is end-of-life upstream. no more security fixes. Kea is the actively maintained replacement, and on most modern router OSes it’s already sitting there, usually already the recommended default.

this one’s a good first project because it’s low-stakes: no new hardware, no VLANs, runs on whatever flat network you already have.

before you start

document your current scope — subnet, range, gateway, DNS servers (and their order, if you run a primary/fallback resolver setup), any static mappings. this is your rollback reference if anything goes sideways.

check for anything that doesn’t map 1:1:

legacy feature Kea equivalent what to do
free-text “additional options” structured option picker re-enter each option explicitly, raw text doesn’t translate
PXE boot fields network-booting fields, if present verify the field exists in your version first
failover peer config Kea High Availability separate reconfiguration, not a checkbox flip
dynamic DNS registration Kea DDNS tab re-verify against your resolver setup
static ARP / deny-unknown “authoritative” flag / reservation-only mode behavior differs, re-test after cutover

version check first — exact field names drift between releases. Kea went from opt-in to default over a few versions, and some installs might already have the legacy backend fully removed.

the actual steps

1. backup

full config export before touching anything. this is your rollback reference if any of the next steps go sideways.

2. enable Kea, general settings

one page, not two — don’t confuse this with a similarly-named “Settings → General” page some router OSes have, which controls the Kea Control Agent (API access for HA/clustering) rather than the DHCP service itself. leave that one alone for a single-node setup.

fields worth setting deliberately rather than accepting defaults on:

field what to set it to why
enable on turns on the Kea process — doesn’t disable legacy dhcpd yet, that’s a separate step
manual config off raw JSON override, not needed for a standard setup
interface(s) whichever interface legacy dhcpd was actually serving this is what Kea listens on
valid lifetime 24h Kea’s built-in default is often much shorter (an hour or less) than legacy dhcpd’s implicit behavior — 24h is a saner homelab default unless you have a specific reason for shorter
firewall rules leave enabled lets the router auto-manage the packet-filter rules DHCP itself needs — don’t turn this off
socket type leave default only change if you hit NIC-driver issues later
everything else on this page leave at default nothing else here has a legacy equivalent worth porting over

3. recreate the subnet

field by field, not just “re-enter what you had”:

field what to set it to why
subnet same as legacy usually pre-filled correctly from the interface
pool range your actual legacy range, typed explicitly check this one. it commonly auto-fills to the entire available range on save, not the range you actually used — if you’re relying on a low range staying free for static reservations, this silently breaks that
match client-id match whatever legacy’s “ignore client UIDs” setting was unchecking this changes how returning clients get matched to their existing lease
auto-collect option data uncheck it this pulls gateway/DNS from the interface automatically, which usually doesn’t produce the values you actually want (especially if you run a resolver that doesn’t itself forward). unchecking exposes the fields below so you can set them explicitly
routers (gateway) your actual gateway IP, typed explicitly legacy setups commonly leave this blank and rely on an implicit default — don’t assume the new system defaults the same way, enter it
DNS servers same servers, same order as legacy order matters if you run a primary/fallback resolver setup
everything else (PXE fields, NTP, domain search, static routes) leave blank unless legacy actually had it set most homelab setups don’t use these

4. recreate static reservations

per device: subnet, IP, MAC, hostname. there’s usually a second “option data” block on the reservation form that looks identical to the subnet-level one above — leave every field in it blank unless that specific device genuinely needs something different from the rest of the subnet. it exists to override the subnet defaults per-host, not to duplicate them.

5. re-enter custom options

anything flagged in the feature-gap audit above that actually applies to your setup. most homelab configs have none.

6. disable the legacy backend

same interface — both can’t serve DHCP on it at once.

7. apply, check the log

check the DHCP service’s own log, and the system log, for startup errors before assuming it worked.

8. validate

release/renew a test client, confirm IP/gateway/DNS all come back correct. confirm the lease shows up in the new system’s lease list — lease history doesn’t carry over from the old backend’s lease file, expected, not a bug.

Kea subnet edit form

the gotcha that got me

the pool range field defaulted to the entire available range on save, not the range i’d actually used with legacy dhcpd. easy to miss, and it matters if you’re relying on a specific low range staying free for static reservations.

also worth checking: some Kea UIs hide the manual router/DNS fields behind an “auto-collect option data” toggle — if you don’t see the fields you expect, that’s probably why.

rollback

re-enable the legacy backend, disable Kea for that interface. both configs stay stored independently, so toggling back is immediate. full restore from the step-1 backup if it’s worse than that.

this one took about twenty minutes end to end, and it’s the kind of infrastructure work that’s invisible when it’s working — which is exactly the point.

▌ comments