How to Set Up a DHCP Server on Linux
Install and configure ISC DHCP or Kea on Linux: define scopes, set static reservations, inspect leases, open firewall ports, and hook into dynamic DNS.
Before you start
- ▸Root or sudo access on the server
- ▸A static IP already configured on the server's LAN interface
- ▸No other DHCP server active on the same network segment
- ▸Basic familiarity with systemd service management
A DHCP server hands out IP addresses, gateways, DNS servers, and other network parameters automatically to clients on your LAN. On Linux, two solid choices exist: the venerable ISC DHCP (isc-dhcp-server) and the modern replacement Kea, also from ISC. ISC DHCP is still widely deployed and well-documented; Kea is its actively developed successor with a REST API and better scalability. This guide covers both, including scopes, static reservations, lease inspection, and a basic DDNS hook.
Prerequisites and Planning
Before touching a package manager, decide:
- Subnet and range — e.g.,
192.168.10.0/24, dynamic pool192.168.10.100–200 - Router (gateway) IP — the host's LAN interface address or a dedicated router
- DNS servers — upstream resolvers or your own (e.g.,
192.168.10.1) - Which interface to listen on — run
ip linkto confirm (eth0,enp3s0, etc.)
Only one DHCP server should be authoritative per broadcast domain. If your router already runs DHCP, disable it before starting this server.
Option A — ISC DHCP Server
1. Install isc-dhcp-server
Debian/Ubuntu:
sudo apt update && sudo apt install isc-dhcp-server -y
Fedora / RHEL 8+ / Rocky:
sudo dnf install dhcp-server -y
Arch Linux:
sudo pacman -S dhcp
2. Bind to the Correct Interface
On Debian/Ubuntu, edit /etc/default/isc-dhcp-server and set the interface:
INTERFACESv4="enp3s0"
On Fedora/RHEL/Arch the interface is declared inside dhcpd.conf itself, so this step is Debian-specific.
3. Write the Main Configuration
Edit /etc/dhcp/dhcpd.conf (Debian/Ubuntu/Arch) or /etc/dhcp/dhcpd.conf (Fedora/RHEL — same path). Replace values to match your network:
# Global options
default-lease-time 86400; # 24 hours
max-lease-time 172800; # 48 hours
authoritative;
option domain-name "home.lan";
option domain-name-servers 192.168.10.1, 8.8.8.8;
# Subnet scope
subnet 192.168.10.0 netmask 255.255.255.0 {
range 192.168.10.100 192.168.10.200;
option routers 192.168.10.1;
option broadcast-address 192.168.10.255;
}
# Static reservation (match by MAC address)
host printer01 {
hardware ethernet aa:bb:cc:dd:ee:ff;
fixed-address 192.168.10.50;
option host-name "printer01";
}
The authoritative; directive tells the daemon to send DHCPNAK when a client requests an address outside the defined scope — essential on a production LAN.
4. Enable and Start the Service
sudo systemctl enable --now isc-dhcp-server # Debian/Ubuntu
sudo systemctl enable --now dhcpd # Fedora/RHEL/Rocky/Arch
5. Verify Leases
Active leases are written to /var/lib/dhcp/dhcpd.leases (Debian/Ubuntu) or /var/lib/dhcpd/dhcpd.leases (Fedora/RHEL). Read them directly or use dhcp-lease-list if installed:
cat /var/lib/dhcp/dhcpd.leases
Output will show blocks like lease 192.168.10.105 { ... } with timestamps and client MAC addresses.
Option B — Kea DHCP Server
Kea is the ISC's modern replacement. It uses JSON configuration, supports a REST API, and separates DHCPv4, DHCPv6, and DDNS into individual daemons.
1. Install Kea
Debian/Ubuntu (22.04+):
sudo apt install kea -y
Fedora 38+ / RHEL 9 via EPEL:
sudo dnf install epel-release -y
sudo dnf install kea -y
Arch Linux (AUR):
yay -S kea
2. Configure kea-dhcp4
The primary config lives at /etc/kea/kea-dhcp4.conf. A minimal working example:
{
"Dhcp4": {
"interfaces-config": {
"interfaces": ["enp3s0"]
},
"lease-database": {
"type": "memfile",
"persist": true,
"name": "/var/lib/kea/kea-leases4.csv"
},
"valid-lifetime": 86400,
"max-valid-lifetime": 172800,
"option-data": [
{ "name": "domain-name-servers", "data": "192.168.10.1, 8.8.8.8" },
{ "name": "domain-name", "data": "home.lan" }
],
"subnet4": [
{
"subnet": "192.168.10.0/24",
"pools": [ { "pool": "192.168.10.100 - 192.168.10.200" } ],
"option-data": [
{ "name": "routers", "data": "192.168.10.1" }
],
"reservations": [
{
"hw-address": "aa:bb:cc:dd:ee:ff",
"ip-address": "192.168.10.50",
"hostname": "printer01"
}
]
}
]
}
}
3. Validate and Start Kea
sudo kea-dhcp4 -t /etc/kea/kea-dhcp4.conf # dry-run syntax check
sudo systemctl enable --now kea-dhcp4
4. Inspect Kea Leases
Kea writes a CSV lease file rather than the flat text format used by ISC DHCP:
cat /var/lib/kea/kea-leases4.csv
Columns include address, hwaddr, client_id, valid_lifetime, expire, hostname, and state. You can also query the live daemon via its control socket if the control-socket stanza is configured.
Firewall Rules
DHCP uses UDP ports 67 (server) and 68 (client). Allow inbound on the server:
nftables:
sudo nft add rule inet filter input udp dport 67 accept
firewalld (Fedora/RHEL):
sudo firewall-cmd --permanent --add-service=dhcp
sudo firewall-cmd --reload
ufw (Ubuntu/Debian):
sudo ufw allow 67/udp
Integrating with DNS (Dynamic DNS)
When a client gets an address, you can update a local DNS server (e.g., BIND or Unbound) automatically. With ISC DHCP, add a ddns-update-style directive and generate a TSIG key shared with BIND:
# Generate a TSIG key
tsig-keygen -a hmac-sha256 dhcp-ddns-key | sudo tee /etc/dhcp/ddns.key
# In dhcpd.conf, add:
ddns-update-style interim;
ddns-domainname "home.lan.";
ddns-rev-domainname "in-addr.arpa.";
include "/etc/dhcp/ddns.key";
zone home.lan. {
primary 127.0.0.1;
key dhcp-ddns-key;
}
zone 10.168.192.in-addr.arpa. {
primary 127.0.0.1;
key dhcp-ddns-key;
}
With Kea, DDNS is handled by the separate kea-dhcp-ddns daemon and configured in /etc/kea/kea-dhcp-ddns.conf. Enable it alongside kea-dhcp4:
sudo systemctl enable --now kea-dhcp-ddns
Troubleshooting
- Clients get no address — Confirm the daemon is running (
systemctl status isc-dhcp-serverorkea-dhcp4), the interface name in config matches reality (ip link), and the firewall allows UDP 67. - Config syntax errors — ISC DHCP logs to
journalctl -u isc-dhcp-server -e. Kea errors appear in/var/log/kea/kea-dhcp4.logor journald. - Address pool exhausted — Check
dhcpd.leasesor the Kea CSV for stale entries. Reducedefault-lease-timeor expand the pool range. - Reservation not working — MAC address must match exactly, including case. Use
ip link showon the client or check lease logs to confirm the address the server sees. - Two DHCP servers on same segment — Use
tcpdump -i enp3s0 port 67 or port 68to identify rogue DHCP offers. Kill or disable any conflicting server.
Frequently asked questions
- Should I use ISC DHCP or Kea for a new deployment?
- Prefer Kea for new setups. ISC DHCP entered maintenance-only mode; Kea receives active development, supports a REST API, and handles high-availability configurations more cleanly.
- Can I run a DHCP server on the same machine as my router or firewall?
- Yes, and it's common. Just ensure the daemon binds only to the internal LAN interface and that your firewall rules allow UDP 67 inbound on that interface only.
- How do I see which MAC address a client is using without physical access?
- Run 'sudo tcpdump -i enp3s0 -n port 67 or port 68' on the server while the client requests an address; the DHCP DISCOVER packet contains the client's MAC.
- What is the difference between a lease time and a reservation?
- A lease time controls how long a dynamically assigned address is valid before the client must renew. A reservation permanently maps a specific MAC to a specific IP regardless of lease duration.
- My Kea config change isn't taking effect after a reload. Why?
- Kea does not re-read its config file on SIGHUP like ISC DHCP does. You must fully restart the service with 'systemctl restart kea-dhcp4', or use the control socket to issue a config-reload command via kea-shell.
Related guides
Build a Mesh VPN with Nebula
Build a fully self-hosted mesh VPN with Nebula: create a CA, sign node certs, configure lighthouses, enforce group-based firewall rules, and run as a systemd service.
Common Linux Network Ports Reference
Learn Linux port ranges, read /etc/services, find what's listening with ss and nmap, and apply solid firewall rules to expose or block the right ports.
How to Configure a Static IP on Linux
Configure a static IP on Linux using Netplan, NetworkManager (nmcli), or systemd-networkd across Ubuntu, Fedora, Debian, and Arch with verified steps.
Expose a Service with Cloudflare Tunnel
Expose local services to the internet without port-forwarding using Cloudflare Tunnel. Install cloudflared, create a named tunnel, configure ingress rules, and run as a systemd service.