dnsmasq: lightweight DHCP + DNS for LAN
dnsmasq does DHCP, DNS forwarding, TFTP, and local cache in one 200 KB binary. Essential for OpenWrt routers, modest enterprise LANs, PXE provisioning, and labs.
Introduction
dnsmasq:
- IPv4 + IPv6 DHCP server (DHCPv6, SLAAC)
- DNS forwarder + cache (not authoritative)
- Integrated TFTP support (PXE)
- Automatic LAN hostname resolution
- Ultra-simple config (one file)
- Available on Linux, BSD, OpenWrt
Use cases:
- Router/firewall (often integrated, e.g. OpenWrt, pfSense)
- Small office/lab/datacenter LAN
- PXE boot
- Simple DHCP with static reservations
Note: dnsmasq is not an authoritative DNS server. For that, see BIND9 or NSD.
Prerequisites
- Linux VPS / server Debian / Ubuntu
- Root access
- Dedicated LAN interface (not in same broadcast as your public VPS!)
Step 1: Installation
sudo apt update
sudo apt install -y dnsmasq dnsmasq-utils
dnsmasq --version
⚠️ On Ubuntu, systemd-resolved already listens on 53:
sudo systemctl disable --now systemd-resolved
sudo unlink /etc/resolv.conf
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
Step 2: Base config
sudo nano /etc/dnsmasq.d/lan.conf
interface=ens4
bind-interfaces
except-interface=ens3
domain-needed
bogus-priv
no-resolv
server=9.9.9.9
server=1.1.1.1
cache-size=10000
no-negcache
log-queries
log-dhcp
Step 3: DHCP
dhcp-range=192.168.50.100,192.168.50.200,12h
dhcp-option=option:router,192.168.50.1
dhcp-option=option:dns-server,192.168.50.1
dhcp-option=option:domain-name,example.lan
dhcp-option=option:netbios-ns,192.168.50.1
dhcp-leasefile=/var/lib/misc/dnsmasq.leases
Step 4: Static reservations
dhcp-host=AA:BB:CC:DD:EE:01,192.168.50.10,web01
dhcp-host=AA:BB:CC:DD:EE:02,192.168.50.11,db01
dhcp-host=AA:BB:CC:DD:EE:03,192.168.50.12,proxy01,infinite
infinite = infinite lease.
Step 5: Local domain
local=/example.lan/
domain=example.lan
expand-hosts
If a DHCP client is named laptop-alice, you can ping laptop-alice.example.lan.
Step 6: Start and test
sudo systemctl restart dnsmasq
sudo systemctl status dnsmasq
sudo journalctl -u dnsmasq -f
On LAN client:
sudo dhclient -r eth0 && sudo dhclient eth0
sudo cat /var/lib/misc/dnsmasq.leases
Step 7: Host mappings
For manual DNS entries (non-DHCP):
address=/internal-app/192.168.50.50
address=/grafana.example.lan/192.168.50.51
Or use standard /etc/hosts (dnsmasq reads it automatically):
192.168.50.50 internal-app internal-app.example.lan
192.168.50.51 grafana.example.lan
Step 8: TFTP / PXE Boot
For PXE (see tutorial 28):
enable-tftp
tftp-root=/srv/tftp
dhcp-boot=pxelinux.0
Very useful: dnsmasq does DHCP + TFTP + DNS in one shot, perfect for a lab.
Step 9: Advanced DHCP options
# Force Pi-hole DNS for all DHCP clients
dhcp-option=option:dns-server,192.168.50.2
# NTP
dhcp-option=option:ntp-server,192.168.50.1
# Tag by MAC or IP
dhcp-host=set:office,192.168.50.20-192.168.50.49
# Tag by vendor class
dhcp-vendorclass=set:windows,MSFT
# Windows-specific
dhcp-option=tag:windows,option:netbios-ns,192.168.50.1
Step 10: IPv6 DHCP (SLAAC + DHCPv6)
dhcp-range=fd12:3456::,ra-only,infinite
dhcp-range=fd12:3456::100,fd12:3456::200,64,12h
ra-param=ens4
enable-ra
Step 11: Logs and stats
sudo killall -SIGUSR1 dnsmasq
sudo journalctl -u dnsmasq | tail -20
Shows:
- Cache size, hits, misses
- Active DHCP leases
- Top queries
For Prometheus: dnsmasq_exporter.
Step 12: DNS resolver only mode (no DHCP)
For a fast local DNS cache:
no-dhcp-interface=ens3,ens4
interface=lo
bind-interfaces
no-resolv
server=1.1.1.1
server=9.9.9.9
cache-size=10000
/etc/resolv.conf:
nameserver 127.0.0.1
Local DNS is very fast thanks to cache.
Troubleshooting
"dnsmasq: failed to create listening socket"
Port 53 already taken:
sudo ss -tunlp | grep :53
Often systemd-resolved. Disable it.
Clients don't get DHCP lease
Check:
- Correct interface in
interface=... - No firewall blocking (UDP 67, 68)
- Network isolated (no conflicting DHCP)
sudo tcpdump -i ens4 port 67 or port 68
Slow resolution / cache not used
sudo killall -SIGUSR1 dnsmasq
sudo journalctl -u dnsmasq | grep cache
If cache-size=0, increase. If no-cache active, remove.
"no such file" on leasefile
sudo touch /var/lib/misc/dnsmasq.leases
sudo chown dnsmasq:dnsmasq /var/lib/misc/dnsmasq.leases
Conflict with another DHCP
Two DHCP responses on LAN = conflict. Kill the other server.
Useful commands
sudo systemctl status dnsmasq
sudo systemctl restart dnsmasq
sudo dnsmasq --test
sudo killall -SIGUSR1 dnsmasq
sudo journalctl -u dnsmasq | tail -30
sudo cat /var/lib/misc/dnsmasq.leases
# Format: timestamp_expire MAC IP hostname client-id
sudo dhcpdump -i ens4
dig @192.168.50.1 google.com
nslookup web01.example.lan 192.168.50.1
Conclusion
dnsmasq is the all-in-one tool for simple LANs:
- DHCP + DNS + TFTP in one binary
- Very light (RAM, CPU)
- One-file configuration
- Auto LAN hostname resolution
Limits:
- Not authoritative DNS (for public domain serving, use BIND9/NSD/PowerDNS)
- Not for large infrastructure (use ISC DHCP / Kea)
Going further:
- Combine with Unbound or Pi-hole for resolver layer
- For enterprise DHCP, look at ISC Kea (successor to ISC DHCP)
- For DDNS, use nsupdate or Knot DNS
Resources
- Official docs: https://thekelleys.org.uk/dnsmasq/doc.html
- Debian wiki: https://wiki.debian.org/Dnsmasq
- OpenWrt best practices: https://openwrt.org/docs/guide-user/base-system/dhcp
- Source: https://thekelleys.org.uk/dnsmasq/

















