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
- backup. full config export before touching anything.
- enable Kea, general settings. tick the interface it should listen on, set a real lease lifetime — Kea’s built-in default is often much shorter than legacy dhcpd’s implicit behavior, and 24h is a saner homelab default.
- recreate the subnet. subnet, pool range — check this doesn’t silently expand to the entire available range, gateway, DNS servers in the same order as before.
- recreate static reservations. subnet + IP + MAC + hostname per device. leave option overrides blank unless that one device genuinely needs something different from the rest of the subnet.
- re-enter custom options found in the audit above, if any actually apply.
- disable the legacy backend on the same interface — both can’t serve it at once.
- apply, then check the log for startup errors.
- validate — release/renew a test client, confirm IP/gateway/DNS, confirm the lease shows up (lease history doesn’t carry over from the old file, expected).
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.