How to Configure Built-In DHCPv4 Server in Ubuntu Using systemd-networkd

When configuring an Ubuntu workstation or server as a local network gateway, isolated container host, virtual switch bridge, or wireless access point, administrators typically install third-party DHCP daemons such as isc-dhcp-server, kea, or dnsmasq. While effective, deploying external daemons introduces extra package dependencies, background service overhead, and duplicate configuration syntax. Systemd provides a native alternative: systemd-networkd includes an embedded, high-performance DHCPv4 server built directly into the network management daemon. With a few declarative directives in a .network profile, administrators can distribute IP leases, push DNS resolvers, and manage pool reservations without installing third-party utilities.

Understanding systemd-networkd Embedded DHCPv4 Server Architecture

The native DHCPv4 server in systemd-networkd integrates directly into the kernel netlink socket layer:

  • Zero External Footprint: Runs entirely within the existing systemd-networkd.service process, consuming no additional memory or background compute threads.
  • Declarative Interface Binding: The DHCP server is activated per-interface within its corresponding .network configuration file. It will not leak or respond to discover packets on interfaces where it has not been explicitly enabled.
  • Automatic Lease State Tracking: Manages lease states in memory and writes active lease descriptors to transient runtime storage under /run/systemd/netif/leases/.
  • Supported DHCP Options: Natively emits Subnet Mask (Option 1), Router / Default Gateway (Option 3), DNS Servers (Option 6), Domain Name (Option 15), NTP Servers (Option 42), and Classless Static Routes (Option 121).

Configuring Interface Addressing and Activating the DHCP Server

To deploy the DHCP server on a target internal interface (e.g., eth1, enp4s0, or a virtual bridge like br0), the interface must first be assigned a static IPv4 address. Create a network configuration file under /etc/systemd/network/20-dhcp-server.network:

# Create and edit the network definition file
sudo nano /etc/systemd/network/20-dhcp-server.network

Insert the following configuration snippet:

[Match]
Name=eth1

[Network]
# Static gateway IP for this interface
Address=192.168.50.1/24
# Activate the embedded DHCPv4 server
DHCPServer=yes
# Enable IPv4 packet forwarding through this node
IPForward=yes

[DHCPServer]
# Start assigning addresses at offset 100 (.100)
PoolOffset=100
# Allocate a pool size of 50 addresses (.100 to .149)
PoolSize=50
# Push DNS server addresses to clients
EmitDNS=yes
DNS=1.1.1.1 8.8.8.8
# Push default gateway (defaults to the interface Address)
EmitRouter=yes
Router=192.168.50.1
# Configure lease duration (in seconds, e.g., 24 hours)
DefaultLeaseTimeSec=86400
MaxLeaseTimeSec=172800

Configuration breakdown:

  • DHCPServer=yes: Instructs systemd-networkd to bind a DHCP server socket to UDP port 67 on eth1.
  • PoolOffset=100: Calculates the start of the dynamic range relative to the network subnet (e.g., 192.168.50.100).
  • PoolSize=50: Binds the address range from 192.168.50.100 through 192.168.50.149.
  • EmitRouter=yes: Directs connecting clients to use the Ubuntu server (192.168.50.1) as their default internet gateway.

Configuring Static DHCP IP Reservations

To assign fixed, predictable IP addresses to specific servers, printers, or lab nodes based on their physical MAC addresses, add [DHCPServerStaticLease] sections to the same configuration file:

# Append static reservation for a local lab server
[DHCPServerStaticLease]
MACAddress=52:54:00:12:34:56
Address=192.168.50.10

# Append static reservation for a network printer
[DHCPServerStaticLease]
MACAddress=00:11:22:33:44:55
Address=192.168.50.20

Applying Configuration and Starting the Server

Restart systemd-networkd to initialize the static address and engage the embedded DHCP server:

# Apply configuration and restart daemon
sudo systemctl restart systemd-networkd

# Confirm operational status
systemctl status systemd-networkd

Verify that the interface is actively listening on DHCP UDP port 67:

# Confirm UDP port 67 is bound
sudo ss -u -l -n -p | grep 67

Configuring Firewall Rules for DHCP Traffic

If Uncomplicated Firewall (UFW) or nftables is active on Ubuntu, permit incoming DHCP discovery packets on the internal interface:

# Allow DHCP broadcast and unicast packets on eth1
sudo ufw allow in on eth1 to any port 67 proto udp comment "Allow DHCP Clients"

# Reload UFW rules
sudo ufw reload

Auditing and Validating Active DHCP Leases

To inspect connected clients and review issued IP addresses, examine the lease files maintained by systemd-networkd:

# Query active lease files for the interface
sudo ls -l /run/systemd/netif/leases/

# Inspect specific lease attributes (MAC, IP, expiration)
sudo cat /run/systemd/netif/leases/*

You can also query networkctl to inspect interface operational parameters:

# Query interface operational status
networkctl status eth1

The output reports DHCPServer: yes, the active pool boundaries, and issued leases.

Capturing DHCP Handshake Exchanges

To verify the four-step DORA (Discover, Offer, Request, Acknowledge) handshake on the wire, execute tcpdump while a test client connects to the network:

# Capture DHCP packet transactions on eth1
sudo tcpdump -i eth1 -nn -vvv "port 67 or port 68" -c 10

The trace confirms that systemd-networkd immediately transmits BOOTP/DHCP-Reply (2) offers with the configured pool addresses and DNS parameters.

Configuring the built-in DHCPv4 server in systemd-networkd provides a minimalist, robust, and zero-maintenance solution for IP address provisioning across local Ubuntu networks.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.