Migrate ISC DHCP Server to Kea DHCP

Convert an existing dhcpd.conf to Kea JSON with KeaMA, correct unsupported directives, test reservations and leases on an isolated lab, compare DHCP offers, and cut over from ISC DHCP to Kea with a tested rollback path.

Published

Updated

Read time 14 min read

Reviewed byDeepak Prasad

Migrate ISC DHCP server to Kea DHCP banner with conversion flow and network icons
Tested on Rocky Linux 9 (Plow); Rocky Linux 10.2 (Red Quartz)
Package dhcp-server 4.4.2-20.b1.el9.rocky.0.1
kea 3.0.3-1.el10_2
Applies to RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora
Privilege sudo or root
Scope End-to-end ISC DHCP to Kea migration: inventory, KeaMA conversion, manual fixes, reservation and lease handling, isolated testing, offer comparison, cutover, and rollback. Does not cover greenfield Kea install, Kea HA, or DHCP-DDNS design from scratch.
Related guides Configure Kea DHCP server on Rocky Linux
tcpdump command
dnf command
nmcli command examples for CentOS and RHEL
firewalld cheat sheet

If you still run ISC dhcpd on Rocky Linux 9 or RHEL 9, migration to Kea is increasingly a when question, not an if. Enterprise Linux 10 ships Kea instead of dhcp-server, and ISC DHCP is in maintenance mode. This walkthrough assumes you already have a working dhcpd.conf and need a safe path to Kea JSON: convert with KeaMA, fix what the tool cannot translate, test on an isolated network, and cut over without address conflicts.

IMPORTANT
Run every migration test on an isolated network with only one DHCP server active at a time. VirtualBox host-only adapters, libvirt default networks, home routers, and NetworkManager shared connections all answer DHCP. Disable the built-in DHCP service on your host-only segment before you start.

Audit the existing ISC DHCP deployment

Before you touch Kea, inventory what the current server actually does. Back up configuration, leases, and any include files, then validate syntax with the ISC tool you already trust.

On the legacy ISC DHCP server (isc-dhcp in the lab), copy these paths:

text
/etc/dhcp/dhcpd.conf
/etc/dhcp/*.conf
/var/lib/dhcpd/dhcpd.leases

Validate the live configuration on Rocky Linux 9:

bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf

Sample output:

output
Internet Systems Consortium DHCP Server 4.4.2b1
Copyright 2004-2019 Internet Systems Consortium.
Config file: /etc/dhcp/dhcpd.conf
Database file: /var/lib/dhcpd/dhcpd.leases

A clean dhcpd -t run ends without an error line. Warnings about global host declarations or LDAP settings are common; note them, but fix only what blocks conversion or changes behavior you rely on.

Lab layout

The procedure below uses three VMs on a VirtualBox host-only network (192.168.56.0/24) with the adapter's built-in DHCP service disabled:

VM Platform Address Role
isc-dhcp Rocky Linux 9 192.168.56.5/24 Legacy ISC DHCP server
kea-server Rocky Linux 10.2 192.168.56.10/24 Target Kea server
dhcp-client Rocky Linux 10.2 DHCP Lease and option testing

Features that need manual redesign

Walk through dhcpd.conf and note anything KeaMA may translate only partially:

Area Why it matters
DHCPv4 and DHCPv6 Separate KeaMA runs (-4 and -6)
Included files KeaMA can follow include directives
Subnets, pools, shared networks JSON structure differs; scope must be correct
Host reservations Global versus subnet placement (-N flag)
Client classes and match if Often needs expression rewrite
allow / deny rules No one-to-one Kea equivalent for every ISC expression
Custom options May need option-def in Kea
PXE boot options Verify next-server and boot filename after conversion
Dynamic DNS Separate Kea DHCP-DDNS (D2) design
Failover peers Becomes Kea HA—not a direct copy
OMAPI or on commit / execute Hooks, Control Agent, or external automation

Record representative test clients before conversion:

Test client MAC / identity Purpose
Dynamic client 52:54:00:aa:bb:cc Pool allocation and standard options
Reserved client 52:54:00:10:20:30 Reservation matching
PXE client 52:54:00:10:20:40 Boot server and boot filename
Classified client Vendor class MSFT 5.0 Class-based pool or option selection

Sample dhcpd.conf for the lab

The article uses one realistic IPv4 configuration with two subnets, reservations, a client class, and global options:

conf
option domain-name "lab.example.com";
option domain-name-servers 192.168.56.10;
default-lease-time 3600;
max-lease-time 7200;

authoritative;

subnet 192.168.56.0 netmask 255.255.255.0 {
    range 192.168.56.100 192.168.56.150;
    option routers 192.168.56.1;
    option subnet-mask 255.255.255.0;

    host web01 {
        hardware ethernet 52:54:00:10:20:30;
        fixed-address 192.168.56.20;
        option host-name "web01";
    }

    host pxe01 {
        hardware ethernet 52:54:00:10:20:40;
        fixed-address 192.168.56.30;
        filename "pxelinux.0";
        next-server 192.168.56.10;
    }
}

class "workstations" {
    match if substring (option vendor-class-identifier, 0, 9) = "MSFT 5.0";
}

subnet 192.168.57.0 netmask 255.255.255.0 {
    range 192.168.57.50 192.168.57.100;
    option routers 192.168.57.1;
}

Keep a pristine copy under /root/backup/dhcpd.conf before any conversion experiments.


Install Kea and the Kea Migration Assistant

Install Kea on the target Rocky Linux 10 server first. Package installs on Enterprise Linux use dnf command.

bash
sudo dnf install kea

Confirm the build you will run in production:

bash
kea-dhcp4 -V

Sample output:

output
3.0.3 (3.0.3 (tarball))
premium: no
linked with:
- log4cplus 2.1.1
- OpenSSL 3.5.5 27 Jan 2026
lease backends:
- Memfile backend 3.0
bash
rpm -q kea

Sample output:

output
kea-3.0.3-1.el10_2.x86_64

KeaMA is distributed separately from the kea package. Add ISC's Cloudsmith repository and install the tool on a workstation that can reach your configs—ideally the same admin host, not a public upload:

bash
curl -1sLf 'https://dl.cloudsmith.io/public/isc/keama/setup.rpm.sh' | sudo -E bash

If dnf install keama reports no match on your EL version, build from the ISC DHCP source tree under keama/ or run the tool in a container. The upstream build instructions live in the ISC DHCP keama directory.

Verify the CLI:

bash
keama -4 -i /etc/dhcp/dhcpd.conf -o /tmp/test.out 2>&1 | head -1 || keama 2>&1 | head -2

Sample output:

output
address family must be set using -4 or -6
Usage: keama [-4|-6] [-D] [-N] [-r {perform|fatal|pass}\n [-l hook-library-path] [-i input-file] [-o output-file]

The usage line confirms the binary is present. A full conversion requires -4 or -6.

WARNING
Do not upload production dhcpd.conf files that contain internal domains, address plans, TSIG keys, or shared secrets to the public KeaMA web interface at dhcp.isc.org. Use local KeaMA for real configurations.

Fresh Kea installation and first subnet setup belong in Configure Kea DHCP server on Rocky Linux—this article picks up once Kea packages are available on the target host.


Convert dhcpd.conf with KeaMA

Run KeaMA once per address family. IPv4 and IPv6 require separate conversions and separate Kea config files.

Convert DHCPv4:

bash
keama -4 -i /etc/dhcp/dhcpd.conf -o /root/kea-dhcp4.converted.conf

For DHCPv6, point at dhcpd6.conf and use -6:

bash
keama -6 -i /etc/dhcp/dhcpd6.conf -o /root/kea-dhcp6.converted.conf

When host reservations should live inside matching subnets instead of a global reservations list, add -N:

bash
keama -4 -N -i /etc/dhcp/dhcpd.conf -o /root/kea-dhcp4.converted.conf

In the lab, KeaMA exited with status 6—one count per conversion issue it logged. That is normal for a first pass; the exit code is a scorecard, not a success signal.

Search the output file for diagnostic comments before you edit anything else:

bash
grep '///' /root/kea-dhcp4.converted.conf

Sample output:

output
/// This configuration declares some subnets but has no interfaces-config
  /// Reference Kea #245
        /// from: match if (substring(option dhcp.vendor-class-identifier, 0, 9)) = 'MSFT 5.0'

Each /// line marks something KeaMA could not finish automatically. The reference numbers point to Kea GitLab migration notes. Treat the generated JSON as a draft: conversion completing without a parser fatal error does not mean the file is production-ready.

KeaMA handles include files in ISC syntax. Parsing warnings and errors from KeaMA are fatal to the conversion run—fix the ISC file with dhcpd -t first, then re-run KeaMA.


Map ISC DHCP directives to Kea JSON

The table below covers the directives you will see most often during migration. It is not an exhaustive ISC-to-Kea dictionary.

ISC DHCP Kea JSON
default-lease-time valid-lifetime
max-lease-time max-valid-lifetime
subnet ... netmask ... subnet4 with CIDR (192.168.56.0/24)
range poolspool
option routers option-datarouters
option domain-name-servers option-datadomain-name-servers
shared-network shared-networks
host reservations
hardware ethernet hw-address
fixed-address ip-address
Custom option definition option-def
Custom option value option-data
Classes and match if client-classes with test expressions—manual review
failover peer Kea HA hook library—not a direct translation
ISC DDNS settings Kea DHCP-DDNS and D2 configuration
OMAPI workflows Kea Control Agent or hook libraries
on commit / execute Hook libraries or external automation

The hard parts are usually semantic, not syntax:

  • Option inheritance differs between ISC stanzas and Kea subnet or shared-network scope.
  • Reservation scope (global versus per-subnet) changes matching behavior.
  • Client classification expressions use a different language in Kea.
  • ISC failover and Kea HA share goals but not configuration blocks.
  • DDNS in Kea splits between the DHCP server and the D2 daemon.

Correct incomplete translations

After KeaMA runs, add the pieces every production Kea file needs but KeaMA omits:

json
"interfaces-config": {
  "interfaces": [ "enp0s8" ]
},
"lease-database": {
  "type": "memfile",
  "persist": true,
  "name": "/var/lib/kea/kea-leases4.csv"
},
"loggers": [
  {
    "name": "kea-dhcp4",
    "output_options": [ { "output": "stdout" } ],
    "severity": "INFO"
  }
]

Replace enp0s8 with the NIC attached to your DHCP segment. On the lab kea-server VM, that is the host-only adapter facing 192.168.56.0/24.

Client classes and permit/deny rules

KeaMA translated the lab workstations class to:

json
"client-classes": [
  {
    "name": "workstations",
    "test": "substring(option[60].hex,0,9) == 'MSFT 5.0'"
  }
]

Compare ISC logic with the Kea expression on real clients. Test which class matches, which pool becomes eligible, and which options return. Kea does not replicate every ISC allow / deny combination.

ISC failover versus Kea HA

Do not paste failover peer blocks into Kea JSON. Document which peers served each subnet, whether the old deployment used load balancing or hot standby, and how leases will synchronize on Kea partners. Full HA wiring is a separate project.

DDNS, events, and automation

List ddns-* settings, on commit, on release, and OMAPI-driven workflows from the ISC config. Map each to Kea D2, a hook library, Control Agent commands, or external log processing. Address allocation working does not prove DNS updates work.


Migrate and verify host reservations

Compare one reservation before and after conversion.

ISC DHCP:

conf
host web01 {
    hardware ethernet 52:54:00:10:20:30;
    fixed-address 192.168.56.20;
    option host-name "web01";
}

Kea (with keama -4 -N):

json
"reservations": [
  {
    "hostname": "web01",
    "hw-address": "52:54:00:10:20:30",
    "ip-address": "192.168.56.20",
    "option-data": [
      {
        "name": "host-name",
        "data": "web01"
      }
    ]
  }
]

Verify each reservation against this checklist:

  • Global versus subnet-level placement
  • Reserved address belongs to the intended subnet
  • MAC address versus client-identifier matching
  • Reservation inside or outside the dynamic pool
  • Duplicate reserved addresses across subnets
  • Host-specific options and hostname behavior for DDNS
  • Clients that may roam between subnets or shared networks

Reservation outside the subnet (parser failure)

A common post-conversion mistake is leaving a reservation in the wrong subnet scope. Kea rejects addresses that do not belong to the enclosing subnet4:

bash
sudo kea-dhcp4 -t /etc/kea/kea-dhcp4.conf

Sample output when 10.0.0.50 was placed under 192.168.56.0/24:

output
Error encountered: specified reservation '10.0.0.50' is not within the IPv4 subnet '192.168.56.0/24'

Move the reservation into the correct subnet4 block (or fix the address) and re-run -t.

Reservation present but client gets a pool address

If JSON validates but the client receives a dynamic address, the reservation exists in the file but Kea did not match it. Typical causes:

  • Reservation placed in a global list while the client lands on a different subnet.
  • Client sends a client identifier but the reservation lists only hw-address (or the reverse).
  • Shared-network subnet selection differs between ISC and Kea.

Test with the actual client MAC on the isolated lab network—not only kea-dhcp4 -t.


Validate and test Kea safely

Work through two validation levels before you let Kea answer production clients.

Syntax check:

bash
sudo kea-dhcp4 -t /etc/kea/kea-dhcp4.conf

A successful syntax check ends without an Error encountered: line. Warnings about multi-threading or reservation lookup are common on Kea 3.x.

Extended check (validates database and hook configuration without requiring open DHCP sockets on a live server):

bash
sudo kea-dhcp4 -T /etc/kea/kea-dhcp4.conf

Both commands should complete on the lab kea-server before you start kea-dhcp4.service on the test network.

Isolated lab test cycle

On the isolated three-VM network:

  1. Stop ISC DHCP on isc-dhcp.
  2. Start Kea on kea-server bound to the lab interface only.
  3. Renew the client connection (NetworkManager or dhclient on dhcp-client).
  4. Capture the DORA exchange.
  5. Verify the received address and options.
  6. Stop Kea before restarting ISC DHCP for the next test pass.

Restrict Kea to the lab NIC:

json
"interfaces-config": {
  "interfaces": [ "enp0s8" ]
}

Capture traffic from the server or client:

bash
sudo tcpdump -ni enp0s8 -s 0 -vvv 'udp port 67 or udp port 68'

Only one server should answer during each test window. If both ISC and Kea run, clients may see conflicting offers—exactly the failure mode you are trying to avoid in production.


Compare ISC DHCP and Kea responses

Capture one transaction from each server using the same client identity on the isolated lab network. In testing, ISC DHCP on 192.168.56.5 offered a dynamic address to MAC 52:54:00:aa:bb:cc:

output
192.168.56.5.bootps > 255.255.255.255.bootpc: BOOTP/DHCP, Reply, length 300
  Your-IP 192.168.56.110
  Client-Ethernet-Address 52:54:00:aa:bb:cc
  DHCP-Message Option 53, length 1: Offer
  Server-ID Option 54, length 4: 192.168.56.5
  Lease-Time Option 51, length 4: 3600
  Subnet-Mask Option 1, length 4: 255.255.255.0
  Default-Gateway Option 3, length 4: 192.168.56.1
  Domain-Name-Server Option 6, length 4: 192.168.56.10
  Domain-Name Option 15, length 15: "lab.example.com"

After you stop ISC and start Kea with the converted config, repeat the capture with the same MAC. Fill in a comparison table from your lab output:

Item ISC result Kea result
Offered address 192.168.56.110 (from your Kea capture)
Subnet mask 255.255.255.0
Default gateway 192.168.56.1
DNS servers 192.168.56.10
Domain name lab.example.com
Lease lifetime 3600
Server identifier 192.168.56.5 192.168.56.10
Reservation matched (52:54:00:10:20:30) 192.168.56.20
PXE next server / boot file 192.168.56.10 / pxelinux.0
DDNS update observed (if applicable)

Exercise more than a dynamic lease. Migrations often look fine until reservations, PXE, relayed subnets, classes, or DDNS are tested.


Decide how to handle active leases

KeaMA converts configuration only. It does not import /var/lib/dhcpd/dhcpd.leases.

Option A: Convert active leases

Use the experimental keama-leases utility (dhcp2kea.py) on a copy of the lease file:

bash
python3 dhcp2kea.py "subnet_id=1" -v /var/lib/dhcpd/dhcpd.leases

The script writes dhcpd-leases-kea.csv alongside the input. Sample output:

output
address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context
192.168.56.110,52:54:00:aa:bb:cc,,3600,1784712600,1,1,1,dyn01,0,

Validate IP address, client identifier or MAC, expiration, subnet ID, and remaining lifetime before you install the CSV. For the memfile backend, install the converted file only while kea-dhcp4 is stopped, and point lease-databasename at the CSV path. ISC failover state does not migrate into Kea HA.

Option B: Cut over without importing leases

For smaller deployments or when duplicate-address risk is acceptable short term:

  1. Reduce ISC DHCP lease times well before migration (days ahead, not minutes).
  2. Allow clients to renew under the shorter lifetime.
  3. Stop ISC DHCP.
  4. Start Kea with an empty lease database.
  5. Monitor for declines or duplicate-address warnings.
  6. Restore normal lease times after the subnet is stable.

Clients keep their previously granted lease until they renew—they do not pick up a shorter timer until the next renewal cycle.

Back up both files before cutover:

text
/var/lib/dhcpd/dhcpd.leases
/var/lib/kea/kea-leases4.csv

Perform the cutover and rollback

Cutover checklist

Step Action
1 kea-dhcp4 -t and kea-dhcp4 -T on the final config
2 Back up ISC config and leases
3 Back up prepared Kea config
4 sudo systemctl stop dhcpd on the ISC server
5 Final copy of dhcpd.leases
6 Convert and install leases (if using lease migration)
7 Update DHCP relay destinations if relays aim at the old server
8 sudo systemctl start kea-dhcp4
9 Monitor journalctl -u kea-dhcp4 -f and packet captures
10 Test dynamic, reserved, PXE, and relayed clients
11 Verify DNS updates if DDNS is in scope
12 Watch pool usage and renewal rate

Useful commands on cutover night:

bash
sudo systemctl stop dhcpd
bash
sudo systemctl start kea-dhcp4
bash
sudo journalctl -u kea-dhcp4 -f

Rollback

If validation fails after cutover, return to ISC in this order:

  1. Stop Kea (sudo systemctl stop kea-dhcp4).
  2. Restore relay destinations to the ISC server.
  3. Restore the ISC configuration and lease snapshot from immediately before cutover.
  4. Start ISC DHCP (sudo systemctl start dhcpd).
  5. Renew representative clients.
  6. Confirm with tcpdump that only ISC answers on UDP 67.

Never start ISC while Kea is still listening. In the lab, after stopping Kea and restarting ISC, the same dynamic client again received offers from the legacy server—confirming a clean rollback path.

Keep the old server, configuration, and lease files until clients have renewed through Kea for your agreed observation period.


Troubleshooting

Symptom Likely cause Fix
KeaMA stops with a parsing error Invalid ISC config or broken include dhcpd -t; fix ISC syntax first
Generated Kea file fails kea-dhcp4 -t JSON syntax or wrong reservation scope Read line/column in error; fix subnet placement
Config parses but Kea will not start Interface, hook, or database problem kea-dhcp4 -T; read journalctl -u kea-dhcp4
Reservation receives a dynamic address Wrong scope, identifier, or subnet selection Match reservation to client packet capture
Subnet reservation prevents startup Reserved IP outside subnet CIDR Move reservation or fix ip-address
Converted class does not match ISC expression needs manual rewrite Test Kea client-classes with real clients
Clients get an address but wrong options Option inheritance differs from ISC Compare captured offers side by side
PXE clients stop booting Missing next-server or boot file Inspect offer options 66/67
DHCP works but DNS is not updated D2/DDNS not migrated Configure Kea DHCP-DDNS separately
HA does not work after migration ISC failover copied literally Design Kea HA as a new deployment
Duplicate addresses after cutover Leases not imported or not yet expired Compare lease files; shorten timers earlier next time
Kea responds during shadow testing Both servers running or block misconfigured Stop ISC; verify only one server on UDP 67
Rollback clients see conflicting offers Kea still running Stop Kea before starting ISC
Kea works locally but not through relay Relay or giaddr subnet mismatch Verify relay target and Kea subnet definitions

References


Summary

Migrating from ISC DHCP to Kea is a configuration translation plus a validation project. KeaMA gives you a draft JSON file; you supply interfaces, lease storage, corrected reservations, and any HA, DDNS, or class logic the tool flags. Test on an isolated network, compare offers from both servers with the same client identity, decide whether to import leases or expire them, then cut over with ISC stopped and Kea alone on UDP 67. Keep a tested rollback until clients have renewed cleanly on Kea.


Frequently Asked Questions

1. Does KeaMA convert active leases from dhcpd.leases?

No. KeaMA converts configuration only. Use the experimental keama-leases (dhcp2kea.py) utility on a copy of the lease file, or shorten ISC lease times before cutover and let clients renew on Kea with an empty lease database.

2. Can I upload my production dhcpd.conf to the public KeaMA website?

No. Use the local keama tool or a private container. Production files often contain internal domains, TSIG keys, and address plans you should not send to a third-party web form.

3. Why does my migrated reservation still get a dynamic address?

Kea matches reservations by scope, identifier, and selected subnet. A reservation placed globally when it belongs in a subnet, a client-id versus MAC mismatch, or shared-network selection differences are common causes—verify with a packet capture, not JSON syntax alone.

4. Does Kea support ISC DHCP failover peer blocks?

No. ISC failover is not a direct translation. Plan Kea high availability as a separate design with partner servers, heartbeat settings, and lease synchronization—not a copied failover peer stanza.

5. What replaces dhcpd -t for Kea?

Run kea-dhcp4 -t for syntax checking and kea-dhcp4 -T when you need extended validation of lease backends and hook libraries without opening DHCP sockets on a running server.
Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on …