DIY Debian Router - Part 4: DHCPv4 configuration with Kea

Oct 8, 2025

Introduction

This is Part 4 of the DIY Debian Router series. See Part 1 for the series introduction and links to all parts.

DHCP (Dynamic Host Configuration Protocol) automates IPv4 network configuration for LAN clients, assigning IP addresses, gateway, DNS servers, and other network parameters. This part covers the deployment of ISC Kea, a modern DHCP server designed as the successor to the now-EOL ISC DHCP daemon.

We will cover:

  • Dynamic address assignment with lease tracking
  • DHCP option distribution (gateway, DNS, domain name)
  • Lease database persistence
  • Logging and troubleshooting

Disable unused Kea services (DHCPv6 and DDNS)

To start, we'll disable some Kea services I don't intend to use. As discussed in Part 5, I'll be using SLAAC for IPv6 not DHCPv6. Dynamic DNS (DDNS) is also disabled (for now, may revisit in the future), as local hostname resolution is handled manually in Unbound (see Part 3).

Disable DHCPv6 and DDNS services:

systemctl disable --now kea-dhcp6-server kea-dhcp-ddns-server

Kea DHCPv4 configuration

Kea's configuration file is /etc/kea/kea-dhcp4.conf, and uses JSON format. The configuration below is what I currently use:

/etc/kea/kea-dhcp4.conf

Expand to view config
{
"Dhcp4": {
    "interfaces-config": {
        "interfaces": ["br0"],
        // Due to https://gitlab.isc.org/isc-projects/kea/-/issues/3188
        "service-sockets-max-retries": 200000,
        "service-sockets-retry-wait-time": 5000,
        // "dhcp-socket-type": "udp"
    },
    "control-socket": {
        "socket-type": "unix",
        "socket-name": "/run/kea/kea4-ctrl-socket"
    },
    "lease-database": {
        "type": "memfile",
        "lfc-interval": 3600
    },
    "expired-leases-processing": {
        "reclaim-timer-wait-time": 10,
        "flush-reclaimed-timer-wait-time": 25,
        "hold-reclaimed-time": 3600,
        "max-reclaim-leases": 100,
        "max-reclaim-time": 250,
        "unwarned-reclaim-cycles": 5
    },
    "renew-timer": 900,
    "rebind-timer": 1800,
    "valid-lifetime": 3600,
    "option-data": [
    ],
    "subnet4": [
        {
            "id": 1,
            "subnet": "192.168.0.0/24",
            "pools": [ { "pool": "192.168.0.150 - 192.168.0.250" } ],
            "option-data": [
                {
                    "name": "routers",
                    "data": "192.168.0.1"
                },
                {
                    "name": "domain-name-servers",
                    "data": "192.168.0.1"
                },
                {
                    "name": "domain-name",
                    "data": "absurdum.ca"
                },
                {
                    "name": "domain-search",
                    "data": "absurdum.ca"
                }
            ],
            "reservations": [
                // Example static reservation:
                // {
                //     "hw-address": "1a:1b:1c:1d:1e:1f",
                //     "ip-address": "192.168.0.100"
                // }
            ]
        }
    ],
    "loggers": [
    {
        "name": "kea-dhcp4",
        "output-options": [
            {
                "output": "syslog",
                "pattern": "%-5p %m\n"
            }
        ],
        // Supported values: FATAL, ERROR, WARN, INFO, DEBUG
        "severity": "WARN",
        "debuglevel": 0
    }
  ]
}
}

Key configuration aspects:

  • Interface binding: Listens on LAN bridge (br0) with extended socket retry parameters (200k retries, 5s wait) to handle race conditions during boot when interface may not be ready
  • Lease management: Memfile backend (/var/lib/kea/kea-leases4.csv) with hourly compaction, 1-hour lease lifetime (15 min renew, 30 min rebind), expired lease reclamation runs every 10s processing up to 100 leases/cycle
  • Address allocation: Dynamic pool 192.168.0.150-192.168.0.250, remainder reserved for static assignments
  • DHCP options: Gateway and DNS point to router (192.168.0.1), domain name and search domain configured to my domain
  • Control and logging: Unix socket at /run/kea/kea4-ctrl-socket for runtime management via kea-shell, syslog output with WARN severity (increase to DEBUG for troubleshooting)

Enable and start the Kea DHCPv4 server:

systemctl enable --now kea-dhcp4-server
systemctl status kea-dhcp4-server

Testing and verification

Check logs for startup errors:

journalctl -u kea-dhcp4-server -n 50

Connect a client device (laptop, phone) to the LAN. The device should automatically receive:

  • IP address in the range 192.168.0.150 - 192.168.0.250
  • Gateway: 192.168.0.1
  • DNS: 192.168.0.1

View active leases on the router:

cat /var/lib/kea/kea-leases4.csv

Verify DHCP options are correctly formatted in option-data. Test with tcpdump to capture DHCP responses:

tcpdump -i br0 -n port 67 or port 68

Trigger a DHCP request from a client and inspect the DHCPACK packet for option 3 (router) and option 6 (DNS).

Next steps

With DHCP working, LAN clients can automatically obtain IPv4 addresses and configuration. However, IPv6 addressing remains unconfigured. Part 5 covers IPv6 deployment via SLAAC (Stateless Address Autoconfiguration) using systemd-networkd's native DHCPv6-PD and Router Advertisement support, providing dual-stack connectivity without the hassle of DHCPv6.

RSS
https://yusefkarim.absurdum.ca/posts/feed.xml