DHCP & DNS Server with dnsmasq

dnsmasq is a lightweight daemon that provides DHCP, DNS forwarding, and TFTP services in a single process. It is ideal for small to mid-size networks -- home labs, development environments, and branch offices. This guide covers installation, DHCP range and static-lease configuration, upstream DNS forwarding, local DNS overrides, PXE network booting, and a brief comparison with ISC dhcpd.

Back to the Networking hub. Related guides: DNS Guide | Network Configuration.

Installing dnsmasq

# Debian / Ubuntu
sudo apt install dnsmasq

# RHEL / Fedora / CentOS
sudo dnf install dnsmasq

# Start and enable
sudo systemctl enable --now dnsmasq

If systemd-resolved is running it may conflict because it binds to port 53 on 127.0.0.53. Either disable it or tell resolved to stop listening:

# Option A: Disable resolved entirely
sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf

# Option B: Tell resolved not to bind its stub listener
sudo mkdir -p /etc/systemd/resolved.conf.d
cat <<'EOF' | sudo tee /etc/systemd/resolved.conf.d/no-stub.conf
[Resolve]
DNSStubListener=no
EOF
sudo systemctl restart systemd-resolved

Core configuration

The main config file is /etc/dnsmasq.conf. Below is a practical example covering the most common options:

# /etc/dnsmasq.conf

# Listen only on the LAN interface
interface=eth1
bind-interfaces

# ---- DHCP ----

# Issue addresses from .100 to .200, with a 12-hour lease
dhcp-range=10.0.0.100,10.0.0.200,12h

# Set the default gateway
dhcp-option=option:router,10.0.0.1

# Set DNS servers handed to clients
dhcp-option=option:dns-server,10.0.0.1

# Set the domain name
dhcp-option=option:domain-name,example.lan
domain=example.lan

# ---- Static leases ----
# Tie a MAC address to a fixed IP and hostname
dhcp-host=aa:bb:cc:dd:ee:01,10.0.0.10,fileserver
dhcp-host=aa:bb:cc:dd:ee:02,10.0.0.11,printer
dhcp-host=aa:bb:cc:dd:ee:03,10.0.0.12,nas

# ---- DNS ----

# Upstream DNS servers (forwarding)
server=1.1.1.1
server=8.8.8.8

# Local DNS overrides (respond without forwarding)
address=/git.example.lan/10.0.0.30
address=/wiki.example.lan/10.0.0.31

# Read additional hosts from /etc/hosts
expand-hosts

# Block a domain (returns NXDOMAIN)
address=/ads.example.com/

# ---- Logging ----
log-queries
log-dhcp
log-facility=/var/log/dnsmasq.log

dhcp-range in detail

# Basic range
dhcp-range=10.0.0.100,10.0.0.200,12h

# With a specific netmask
dhcp-range=10.0.0.100,10.0.0.200,255.255.255.0,12h

# Tag-based: give a different range to a VLAN interface
dhcp-range=set:vlan200,172.16.200.100,172.16.200.200,12h
interface=eth1.200

# IPv6 SLAAC + DHCPv6
dhcp-range=::100,::200,constructor:eth1,ra-names,64,12h

dhcp-host (static leases)

# By MAC address
dhcp-host=aa:bb:cc:dd:ee:01,10.0.0.10,fileserver

# Ignore a specific MAC (never give it an address)
dhcp-host=aa:bb:cc:dd:ee:ff,ignore

# Set a per-host lease time
dhcp-host=aa:bb:cc:dd:ee:02,10.0.0.11,printer,infinite

Upstream DNS (server=)

# Forward everything to Cloudflare and Google
server=1.1.1.1
server=8.8.8.8

# Forward a specific domain to an internal DNS server
server=/corp.example.com/10.1.0.53

# Forward reverse lookups for 10.0.0.0/24 to an internal server
server=/0.0.10.in-addr.arpa/10.1.0.53

Local DNS overrides (address=)

# Point a hostname at an IP (any query type returns this)
address=/myapp.dev.local/10.0.0.40

# Wildcard: all subdomains of dev.local resolve to one IP
address=/.dev.local/10.0.0.40

# Block a domain entirely (returns 0.0.0.0 / ::)
address=/ads.doubleclick.net/

PXE network boot (dhcp-boot)

dnsmasq can serve a PXE boot file to network-booting clients:

# Enable the built-in TFTP server
enable-tftp
tftp-root=/srv/tftp

# Tell PXE clients to load pxelinux.0 from TFTP
dhcp-boot=pxelinux.0

# For UEFI clients, match the architecture tag
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-boot=tag:efi-x86_64,grubx64.efi

Place the boot files in /srv/tftp:

sudo apt install pxelinux syslinux-common
sudo cp /usr/lib/PXELINUX/pxelinux.0 /srv/tftp/
sudo cp /usr/lib/syslinux/modules/bios/ldlinux.c32 /srv/tftp/
sudo mkdir /srv/tftp/pxelinux.cfg

Reloading and testing

# Check the syntax
dnsmasq --test

# Restart after config changes
sudo systemctl restart dnsmasq

# Watch the log
sudo journalctl -fu dnsmasq

# On a client, release and renew to test DHCP
sudo dhclient -r eth0 && sudo dhclient eth0

# Test DNS resolution through dnsmasq
dig @10.0.0.1 fileserver.example.lan

ISC dhcpd as an alternative

For larger deployments or when you need features like failover pools, ISC dhcpd is the traditional choice:

sudo apt install isc-dhcp-server
# /etc/dhcp/dhcpd.conf
subnet 10.0.0.0 netmask 255.255.255.0 {
    range 10.0.0.100 10.0.0.200;
    option routers 10.0.0.1;
    option domain-name-servers 10.0.0.1;
    option domain-name "example.lan";
    default-lease-time 43200;
    max-lease-time 86400;
}
host fileserver {
    hardware ethernet aa:bb:cc:dd:ee:01;
    fixed-address 10.0.0.10;
}

dnsmasq is generally simpler to configure and combines DHCP with DNS in one service, while ISC dhcpd offers more advanced DHCP features like failover and conditional classes.