Stale IPs on a Home Network? Let dnsmasq Clean Up the Mess
If you’ve ever pinged a device that had moved to a new address and the reply came from an old IP, you’ve probably been frustrated by the same stale DNS entries that linger in the resolver cache. On a typical Ubuntu desktop or server, systemd‑resolved is the default DNS stub. It’s great for most use cases, but it can keep outdated records for a long time, especially when your router’s DHCP server changes leases frequently. The result? Your own machine keeps trying to resolve a dead address until the cache finally expires.
The fix is simple: replace the stub resolver with a lightweight local caching DNS server that you control. dnsmasq is a proven, battle‑tested choice. It can act as a DHCP server, a DNS forwarder, and a DNS cache all in one. In this post I’ll walk through installing dnsmasq on Ubuntu 24.04, configuring it to forward to your upstream nameservers, disabling systemd‑resolved, and a few tricks to keep the cache fresh and secure.
Why dnsmasq over systemd‑resolved?
| Feature | systemd‑resolved | dnsmasq |
|---|---|---|
| DNS cache TTL | 1 h (default) | configurable per‑record |
| DHCP server | No | Yes |
| Local domain handling | Limited | Full support |
| Cache invalidation | Manual systemd-resolve --flush-caches |
Automatic on lease change |
| Security | Uses systemd’s sandbox | Runs as dnsmasq user, minimal privileges |
systemd‑resolved is fine for most desktop users, but when you’re running a home lab or a small server that relies on local hostnames, the stale cache can break services. dnsmasq gives you fine‑grained control over TTLs and lets you drop the stale entries as soon as the DHCP lease changes.
Step 1 – Install dnsmasq
sudo apt update
sudo apt install dnsmasq
The package ships with a default configuration in /etc/dnsmasq.conf. It’s intentionally minimal; we’ll override the parts we need.
Step 2 – Disable systemd‑resolved
systemd‑resolved listens on /run/systemd/resolve/stub-resolv.conf and on the loopback interface. To avoid conflicts, stop and mask it:
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo systemctl mask systemd-resolved
Next, edit /etc/resolv.conf to point to the local dnsmasq instance:
sudo nano /etc/resolv.conf
Replace its contents with:
nameserver 127.0.0.1
If you’re on a system that still uses resolvconf, you can also create a hook to keep the file in sync, but for a single‑node setup the static file is fine.
Step 3 – Configure dnsmasq
Open /etc/dnsmasq.conf for editing:
sudo nano /etc/dnsmasq.conf
Add or uncomment the following lines:
# Forward all queries to the router’s DNS
server=192.168.1.1
# Use the router’s DHCP server for local network
dhcp-range=192.168.1.100,192.168.1.200,12h
# Short TTL for local names to avoid stale entries
local-service
local=/home/
Explanation
server=tells dnsmasq where to forward queries it can’t resolve locally. Replace192.168.1.1with your router’s IP.dhcp-range=givesdnsmasqa DHCP pool. If you already use your router for DHCP, comment this out. If you wantdnsmasqto handle DHCP, keep it.local-servicelimits DNS queries to the local network, which is a small security hardening step.local=/home/tells dnsmasq that any name ending in.home.is local. You can use any domain you prefer.
If you’re only using dnsmasq as a cache, you can leave the DHCP section out entirely. In that case, add:
no-dhcp-interface=
to prevent it from listening on any interface for DHCP.
Step 4 – Restart and Test
sudo systemctl restart dnsmasq
Check the status:
sudo systemctl status dnsmasq
Now test resolution:
dig @127.0.0.1 mydevice.home
If you see a response from the router’s DNS, you’re good. To verify that stale entries are gone, try pinging a device that just changed IP:
ping -c 3 mydevice.home
The reply should come from the new address immediately.
Step 5 – Fine‑Tuning TTLs
dnsmasq lets you set TTLs per‑record. For example, to force a 30‑second TTL for all local names:
local=/home/
local-ttl=30
If you need longer TTLs for external domains, add:
server=8.8.8.8
server=8.8.4.4
and keep the default TTL for those.
Security Considerations
- Run dnsmasq as a non‑root user – The package already does this (
dnsmasquser). If you’re running it manually, use--user=dnsmasq --group=dnsmasq. - Limit query scope –
local-serviceandlisten-address=127.0.0.1prevent external hosts from querying your cache. - Disable DNSSEC validation –
dnsmasqdoes not validate DNSSEC by default. If you need validation, installdnssec-rootand addtrust-anchor=...lines, but that adds complexity. - Keep the package updated –
dnsmasqis actively maintained. Ubuntu’s package is usually up to date, but you can also install from the upstream repo on GitHub if you need the latest features.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
dig @127.0.0.1 returns SERVFAIL |
dnsmasq not listening | sudo systemctl restart dnsmasq |
| Stale IP persists | systemd-resolved still active |
Verify systemctl status systemd-resolved |
| No resolution for external domains | Wrong upstream server | Check server= lines |
| DHCP conflicts | Two DHCP servers running | Disable DHCP in dnsmasq or router |
Use journalctl -u dnsmasq for detailed logs. If you see “Failed to bind to 127.0.0.1:53”, another process is already listening on port 53. That’s usually systemd-resolved. Make sure it’s fully stopped.
When to Keep systemd‑resolved
If you’re on a multi‑node network where the router’s DNS is authoritative and you rarely change IPs, the default resolver is fine. dnsmasq shines when:
- You run a local server that relies on hostnames that change (e.g., a Raspberry Pi that reboots).
- You want to host a local domain (e.g.,
*.home) without touching the router. - You need to serve DHCP and DNS from a single machine.
Quick Reference
# Install
sudo apt install dnsmasq
# Disable systemd-resolved
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo systemctl mask systemd-resolved
# Point resolv.conf
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
# Basic dnsmasq config
cat <<EOF | sudo tee /etc/dnsmasq.conf
server=192.168.1.1
local=/home/
local-ttl=30
EOF
# Restart
sudo systemctl restart dnsmasq
Tags
dnsmasq, dns, systemd-resolved, ubuntu, networking
See also
- Why my nightly rsync backup disappeared after switching to a systemd timer – and how I fixed it with 'Persistent=true
- Taming My Daily Backup Script’s CPU Spikes with `nice` and `ionice`
- Running Podman as a Non-Root User on a Headless Server
- Taming the systemd Boot Process: My Journey to Fixing Slow Boot Times on My Linux Laptop
- Troubleshooting Slow DNS Lookups with systemd-resolved on My Linux Homelab Server