| Tested on | RHEL 10.2 (Coughlan) — vm1.lab.example (192.168.56.116) |
|---|---|
| Package | dnsmasq-2.90-7.el10_2.1bind-utils-9.18.33-15.el10_2.12NetworkManager-1.56.0-1.el10 |
| Applies to | RHEL 10, Rocky Linux 10, AlmaLinux 10, Oracle Linux 10, CentOS Stream 10 |
| Privilege | sudo or root |
| Scope | Run dnsmasq as a local DNS cache on 127.0.0.1 for the same RHEL host (not a LAN-wide caching resolver). Covers standalone dnsmasq.service, upstream files, NetworkManager, and verification. Does not cover dnsmasq DHCP, TFTP, or PXE — keep those as a separate design (see Kea DHCP). |
| Related guides | Configure BIND on Linux DNS troubleshooting with Wireshark dhclient removed in RHEL 10 systemctl command nmcli examples |
dnsmasq is a small forwarder that answers DNS on the host, caches responses, and sends cache misses to upstream nameservers you choose. In this guide, “DNS caching server” means a resolver cache for this RHEL machine only. Because dnsmasq listens on 127.0.0.1, other hosts on the network cannot query your cache unless you deliberately widen listen-address (not covered here).
Quick answer
Without a local cache, repeated DNS queries normally go to an upstream resolver. dnsmasq stores successful and negative responses for their TTL. As long as the record is still cached, dnsmasq can answer it locally instead of asking the upstream DNS server again. That does not extend TTL beyond what the upstream zone published unless you change TTL behavior on purpose.
For a local DNS cache on RHEL 10:
- Install
dnsmasqandbind-utils. - Configure dnsmasq to listen only on
127.0.0.1. - List upstream DNS servers in
/etc/resolv.dnsmasq. - Start
dnsmasqand verify127.0.0.1:53is listening. - Point the host resolver at
127.0.0.1through NetworkManager. - Verify forwarding and cache hits with
dig(and optionaltcpdump).
Application
↓
127.0.0.1:53
↓
dnsmasq cache
↓ cache miss
Upstream DNSThis guide runs standalone dnsmasq.service. RHEL can also let NetworkManager start its own dnsmasq instance (dns=dnsmasq under [main]); that is a different setup. Use one approach or the other — do not run both on the same loopback port.
Test environment
| Item | Value |
|---|---|
| Host | RHEL 10.2, vm1.lab.example, 192.168.56.116 |
| Upstream DNS | Active Directory DNS 192.168.56.128 (lab.example) |
| Lab interface | enp0s8 (connection profile name matches device in this lab) |
| dnsmasq | 2.90-7.el10_2.1 |
| NetworkManager | 1.56.0-1.el10 |
Standalone dnsmasq vs NetworkManager dnsmasq mode
This guide enables dnsmasq.service directly and manages upstreams in /etc/resolv.dnsmasq.
RHEL also supports NetworkManager-controlled dnsmasq: set dns=dnsmasq in NetworkManager configuration, and NetworkManager starts dnsmasq on loopback and can place 127.0.0.1 in /etc/resolv.conf. That mode is useful when you want NetworkManager to own split-DNS forwarding.
Do not enable NetworkManager’s dnsmasq plugin and a separate system dnsmasq service that both bind to 127.0.0.1:53.
Prerequisites
- Outbound reachability to at least one upstream resolver you trust.
- Only one listener on
127.0.0.1:53for the cache role. libvirt, PXE/dhcp drop-ins, or a seconddnsmasqprocess can conflict (see Troubleshooting). - An active RHEL subscription or configured repository before
dnf install. See Subscription Manager or fix disabled repos if installs fail.
Install dnsmasq and dig
Install the caching forwarder and dig from bind-utils:
sudo dnf install -y dnsmasq bind-utilsInstalled:
dnsmasq-2.90-7.el10_2.1.x86_64
Complete!If dnsmasq is already installed, dnf may report Nothing to do. — that is fine.
Check the package versions:
rpm -q dnsmasq bind-utilsdnsmasq-2.90-7.el10_2.1.x86_64
bind-utils-9.18.33-15.el10_2.12.x86_64Choose upstream DNS servers
Put upstream DNS servers in /etc/resolv.dnsmasq using the same nameserver syntax as /etc/resolv.conf. That keeps upstreams easy to change without editing the rest of the dnsmasq configuration.
Use one upstream resolver (recommended starting point)
For many labs and AD-backed networks, point dnsmasq at your domain DNS server and let that server resolve or forward public names:
sudo tee /etc/resolv.dnsmasq <<'EOF'
nameserver 192.168.56.128
EOFReplace 192.168.56.128 with your BIND, Active Directory, or corporate resolver.
If the upstream is Active Directory DNS, prefer only that server here. Do not add a public resolver such as 1.1.1.1 as a generic “backup” for an AD namespace unless you understand split-DNS behavior — dnsmasq may send queries to any configured upstream according to its server-selection rules, and public DNS must not answer private AD-only names.
Check the file before continuing:
cat /etc/resolv.dnsmasqnameserver 192.168.56.128Optional: different DNS servers for different domains
If different domains must use different DNS servers (for example lab.example to AD DNS and everything else to a public resolver), use explicit server= lines and no-resolv instead of /etc/resolv.dnsmasq:
no-resolv
server=/lab.example/192.168.56.128
server=1.1.1.1Names under lab.example go to 192.168.56.128; other queries go to 1.1.1.1.
This is an alternative to the /etc/resolv.dnsmasq setup above. With no-resolv, do not also use resolv-file=/etc/resolv.dnsmasq in the same drop-in. For this article’s main walkthrough, keep the single-upstream file; use the server= form only when you specifically need split DNS.
Configure dnsmasq as a local cache
RHEL ships defaults in /etc/dnsmasq.conf. Add a drop-in under /etc/dnsmasq.d/ so package updates do not overwrite your settings.
sudo tee /etc/dnsmasq.d/local-cache.conf <<'EOF'
# Listen only on this host
listen-address=127.0.0.1
bind-interfaces
# Upstream DNS servers
resolv-file=/etc/resolv.dnsmasq
no-poll
# Local cache
cache-size=1000
EOFHere is what each setting changes:
listen-address=127.0.0.1— accept DNS only on loopback so LAN clients do not use this cache by accident.bind-interfaces— bind specifically to that address instead of dnsmasq’s default wildcard socket on Linux. Without it,ssmay show0.0.0.0:53even thoughlisten-addressfilters which clients are served; withbind-interfaces, the listener inssmatches the intended loopback-only scope.resolv-file— read upstream servers from/etc/resolv.dnsmasq, not from/etc/resolv.conf.no-poll— do not monitor the resolver file for changes. If you edit/etc/resolv.dnsmasq, reload or restart dnsmasq yourself.cache-size=1000— cache up to 1000 entries (default is 150). For one workstation or small server, 1000 is a reasonable example; a huge cache is not required.
Optional hardening (not required for basic caching):
domain-needed— do not forward single-label names such asprinterupstream when dnsmasq has no local answer.bogus-priv— do not forward reverse lookups for private ranges when there is no local data. Skip this if you rely on AD for RFC1918 PTR records.
If other drop-ins under /etc/dnsmasq.d/ enable DHCP, PXE, or extra listen-address values, dnsmasq may also bind on LAN interfaces. Keep PXE/DHCP configuration separate from this loopback-only cache example when you can.
Check the configuration for syntax errors
Validate the drop-in before you start the service:
sudo dnsmasq --testdnsmasq: syntax check OK.Start dnsmasq
Start the resolver before you point NetworkManager at 127.0.0.1, so the host is not left with no working DNS on port 53.
Enable and start the service with systemctl:
sudo systemctl enable --now dnsmasqsystemctl enable --now is quiet on success, so check that the daemon is running:
systemctl is-active dnsmasqactiveVerify UDP and TCP on loopback
DNS uses UDP for most queries and TCP when needed. With listen-address=127.0.0.1 and bind-interfaces, dnsmasq should listen on both protocols on loopback only:
sudo ss -luntp | grep ':53'udp UNCONN 0 0 127.0.0.1:53 0.0.0.0:* users:(("dnsmasq",pid=98556,fd=9))
tcp LISTEN 0 32 127.0.0.1:53 0.0.0.0:* users:(("dnsmasq",pid=98556,fd=10))If you see 0.0.0.0:53 without bind-interfaces, dnsmasq may still be filtering by listen-address — add bind-interfaces so ss matches what you intend. If you see a LAN address such as 192.168.56.116:53, another drop-in (DHCP/PXE) is binding extra interfaces; scope or disable that snippet for a loopback-only cache.
Point RHEL at the local cache with NetworkManager
Hand-editing /etc/resolv.conf is fragile on RHEL 10 because NetworkManager regenerates it from connection profiles.
Find the NetworkManager connection profile
List active connections. Use the value from the NAME column, not necessarily the DEVICE value:
nmcli -t -f NAME,DEVICE connection show --activeenp0s8:enp0s8On another host you might see Wired connection 1:enp0s8 — then modify 'Wired connection 1', not enp0s8.
Set 127.0.0.1 as the only resolver
Point applications at the cache. dnsmasq itself uses /etc/resolv.dnsmasq for upstreams:
sudo nmcli connection modify enp0s8 ipv4.dns "127.0.0.1" \
ipv4.dns-search "lab.example" ipv4.ignore-auto-dns yesipv4.ignore-auto-dns yes stops DHCP from injecting nameservers that bypass dnsmasq.
Adding the upstream DNS server as a second nameserver in NetworkManager gives the host a fallback if dnsmasq is down, but DNS can then bypass the local cache. If you want every DNS query from this host to go through dnsmasq, keep only 127.0.0.1 in /etc/resolv.conf.
Apply the profile:
sudo nmcli connection up enp0s8Reactivating a connection can briefly interrupt that interface. If you change DNS on a remote system over SSH, keep another access path available or schedule the change during a maintenance window.
Verify /etc/resolv.conf
Check the resulting resolver configuration:
cat /etc/resolv.confsearch lab.example
nameserver 127.0.0.1More nmcli patterns: nmcli cheat sheet.
Verify DNS forwarding
Query through the cache explicitly:
dig +noall +answer +stats www.example.org @127.0.0.1After a cache flush or restart, the first lookup often shows a higher Query time while dnsmasq contacts upstream:
www.example.org. 274 IN A 172.66.157.237
;; Query time: 58 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)If you use internal DNS, test an internal hostname as well:
dig +short vm1.lab.example @127.0.0.1192.168.56.116Verify the DNS cache
Run the same query again before the TTL expires:
dig +noall +answer +stats www.example.org @127.0.0.1A much lower Query time is a good indication that dnsmasq used its cache, but timing alone is not proof — a fast upstream can also answer quickly on a second miss.
;; Query time: 0 msecConfirm the second query was not forwarded upstream
Forwarded queries leave through a real interface, not only lo. Capture on all interfaces or on the NIC that reaches your upstream DNS:
sudo tcpdump -ni any port 53In another session:
- Run the first
dig— you should see the client query to127.0.0.1and dnsmasq’s query to the upstream resolver. - Run the same
digagain before TTL expiry — you should see the client query, but no new upstream query for a genuine cache hit.
Alternatively, capture only the external interface (example enp0s8):
sudo tcpdump -ni enp0s8 port 53Install tcpdump only when you need this check:
sudo dnf install -y tcpdumpClear the dnsmasq cache
Restarting dnsmasq clears the cache:
sudo systemctl restart dnsmasqThe dnsmasq man page also documents clearing cache with SIGHUP. On RHEL, use systemctl restart dnsmasq unless you have verified your unit’s reload behavior sends that signal.
After a restart, the next lookup must contact an upstream resolver again.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
dnsmasq fails to start |
Port 53 in use or bad config |
sudo ss -luntp | grep ':53'; sudo dnsmasq --test |
dnsmasq runs but DNS fails |
Upstream unreachable or wrong /etc/resolv.dnsmasq |
dig @192.168.56.128 (or your upstream); fix upstream file |
| Applications skip the cache | Second nameserver in /etc/resolv.conf or DHCP DNS |
cat /etc/resolv.conf; set only 127.0.0.1 and ipv4.ignore-auto-dns yes |
| Internal AD names fail | Public DNS used for AD zones | Use AD DNS only in /etc/resolv.dnsmasq, or server=/domain/upstream |
| Repeated queries still hit upstream | Low TTL, different QNAME each time, or cache cleared | Repeat the exact name; check cache-size; use tcpdump |
| Extra listeners on LAN IP | DHCP/PXE drop-ins in /etc/dnsmasq.d/ |
Separate PXE design from loopback cache |
For NXDOMAIN and timeout behavior on the wire, see DNS troubleshooting with Wireshark.
Summary
dnsmasq gives RHEL 10 a local DNS cache without running a full authoritative server. Install the package, list upstreams in /etc/resolv.dnsmasq, add a small loopback-only drop-in, and set NetworkManager to nameserver 127.0.0.1 so applications always hit the cache first.
The problems you are most likely to hit are another process on port 53 and a second nameserver in /etc/resolv.conf that bypasses dnsmasq. After systemctl enable --now dnsmasq, use dig twice and optional tcpdump on any or your external NIC to confirm caching — not only query time.
For DHCP or PXE from dnsmasq, plan interfaces and listen-address separately; on RHEL 10, Kea is the long-term DHCP direction.
References
- dnsmasq man page — cache,
server=, andSIGHUP - Red Hat Enterprise Linux 10 — Configuring and managing networking — NetworkManager DNS and dnsmasq
Frequently Asked Questions
Does RHEL cache DNS by default?
No. You enable caching by running dnsmasq (standalone or through NetworkManager’s dnsmasq mode) or another resolver that caches.
Does dnsmasq replace BIND?
No. dnsmasq forwards and caches. Use BIND or directory DNS when you need authoritative zones or enterprise DNS features.
Should dnsmasq listen on 127.0.0.1?
For a host-local cache, yes — it keeps the cache off the LAN. Widen listen-address only when you intentionally offer DNS to other machines.
Can dnsmasq use Active Directory DNS?
Yes. Point /etc/resolv.dnsmasq at your AD DNS server, or use server=/your.domain/AD-DNS-IP for split forwarding.
How do I clear the dnsmasq DNS cache?
Restart the service: sudo systemctl restart dnsmasq.
Can NetworkManager run dnsmasq automatically?
Yes. RHEL documents dns=dnsmasq under NetworkManager. Do not combine that with a separate dnsmasq.service on the same loopback port.
Can other machines use this DNS cache?
Not with listen-address=127.0.0.1. Other hosts would need dnsmasq to listen on a LAN address, which is a different design.

