You need a local DNS server that actually answers queries
You built a home lab or set up a small office network, and now your devices need a reliable way to resolve hostnames. You pointed your router at your Fedora box, but the queries hang or return NXDOMAIN. You need a DNS server that handles lookups without turning your machine into a maintenance nightmare.
How DNS resolution actually works on your network
DNS is a distributed lookup table with aggressive caching. When a client asks for printer.local, the server checks its cache first. If the answer is missing, it checks its local zones. If that fails, it forwards the question to an upstream resolver. If the service is not listening on the correct interface, or if the firewall drops UDP port 53, the client times out after a few seconds.
BIND and dnsmasq solve the same problem with different architectures. BIND is a full-featured authoritative and recursive server designed for production environments. It separates configuration into strict zones and enforces type safety. dnsmasq is a lightweight resolver and DHCP helper that caches aggressively and reads a flat file for static mappings. It starts in milliseconds and uses less than fifty megabytes of RAM.
Pick the tool that matches your traffic volume. Run the configuration steps below. Verify the output before trusting it with production traffic.
Setting up dnsmasq for lightweight resolution
dnsmasq is the default choice for home labs and small networks. It handles recursive queries, caches responses, and serves static hostnames from a single configuration file.
Install the package and enable the service. The --now flag starts it immediately so you can test the default configuration before making changes.
sudo dnf install dnsmasq # installs the daemon and default config
sudo systemctl enable --now dnsmasq # starts the service and registers it for boot
The default configuration listens on all interfaces and forwards queries to the resolvers listed in /etc/resolv.conf. That works for testing, but it is not safe for a multi-subnet network. You need to restrict the listening address and define explicit upstream resolvers.
Open the main configuration file. Fedora ships the package defaults in /usr/lib/dnsmasq.conf. Never edit files in /usr/lib/. Create or modify /etc/dnsmasq.conf instead. The system merges the two files, and /etc/ always wins.
sudo nano /etc/dnsmasq.conf # edit the user configuration file
Add or modify these lines to lock down the service. Replace 192.168.1.1 with your actual Fedora interface IP.
# Listen only on this specific interface to avoid answering queries from the wrong subnet
interface=eth0
# Do not listen on all interfaces automatically
bind-interfaces
# Use Cloudflare and Quad9 as upstream resolvers for recursive queries
server=1.1.1.1
server=9.9.9.9
# Cache up to 1500 entries to speed up repeated lookups
cache-size=1500
# Read static hostnames from this file instead of /etc/hosts
addn-hosts=/etc/hosts.dnsmasq
Create the static hosts file and add your internal records. Each line maps an IP to a hostname.
sudo nano /etc/hosts.dnsmasq # create the static mapping file
192.168.1.50 printer.local
192.168.1.51 nas.local
192.168.1.100 devbox.local
Restart the service to apply the changes. The daemon reads the new configuration and clears the old cache.
sudo systemctl restart dnsmasq # reloads config and clears the DNS cache
Open the firewall for DNS traffic. DNS uses both UDP and TCP on port 53. TCP is required for zone transfers and responses larger than 512 bytes.
sudo firewall-cmd --permanent --add-service=dns # adds UDP and TCP 53 to the persistent zone
sudo firewall-cmd --reload # applies the rule to the running firewall immediately
Restart the service after firewall changes. Half the time the symptom is gone.
Setting up BIND for authoritative resolution
BIND is the industry standard for authoritative DNS. It uses a strict zone-based architecture and validates records before serving them. It is heavier than dnsmasq, but it handles complex zone transfers, DNSSEC, and high query volumes without breaking.
Install the server and the diagnostic utilities. The bind-utils package provides dig and nslookup, which you will need for verification.
sudo dnf install bind bind-utils # installs the named daemon and query tools
sudo systemctl enable --now named # starts the authoritative server and registers it for boot
BIND refuses to start if the configuration contains syntax errors or if it cannot bind to the requested addresses. The main configuration file lives at /etc/named.conf. Fedora ships a secure default that only listens on localhost. You must modify it to accept queries from your network.
sudo nano /etc/named.conf # edit the primary BIND configuration
Locate the options block and adjust the listen-on and allow-query directives. Replace the subnet with your actual network range.
options {
// Listen on the local network interface instead of only localhost
listen-on port 53 { 127.0.0.1; 192.168.1.1; };
// Allow queries from your internal subnet
allow-query { localhost; 192.168.1.0/24; };
// Forward recursive queries to upstream resolvers
forwarders { 1.1.1.1; 9.9.9.9; };
// Enable query logging for troubleshooting
querylog yes;
};
BIND requires at least one zone definition to start. Create a simple forward zone for your local domain. Add this block to the end of /etc/named.conf.
zone "local" IN {
type master;
file "/etc/named.zones/local.zone";
allow-update { none; };
};
Create the zone directory and the zone file. BIND runs as the named user, so the directory must be readable by that account.
sudo mkdir -p /etc/named.zones # create a dedicated directory for zone files
sudo nano /etc/named.zones/local.zone # create the forward lookup zone
Add the standard SOA and NS records, plus your hostnames. The serial number must increment every time you change the zone.
$TTL 86400
@ IN SOA ns1.local. admin.local. (
2024051501 ; Serial (YYYYMMDDNN)
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
@ IN NS ns1.local.
ns1 IN A 192.168.1.1
printer IN A 192.168.1.50
nas IN A 192.168.1.51
Set the correct ownership and permissions. BIND will refuse to load the zone if the files are world-writable.
sudo chown -R named:named /etc/named.zones # gives the named user read access
sudo chmod 640 /etc/named.zones/local.zone # restricts write access to the owner
Validate the configuration before restarting. The named-checkconf command parses the syntax without starting the daemon.
sudo named-checkconf # verifies syntax and reports line numbers for errors
sudo named-checkzone local /etc/named.zones/local.zone # validates the zone file structure
Restart the service and open the firewall. BIND uses the same port 53 service definition as dnsmasq.
sudo systemctl restart named # applies the new configuration and loads zones
sudo firewall-cmd --permanent --add-service=dns # allows DNS traffic through the firewall
sudo firewall-cmd --reload # commits the firewall rule to the runtime
Run named-checkconf before every change. A single missing semicolon stops the entire server.
Verify the DNS server is answering correctly
Do not trust the service status alone. Query the server directly and watch the response codes. Use dig to test both recursive and authoritative queries.
dig @127.0.0.1 printer.local # queries the local server for a static hostname
dig @127.0.0.1 google.com # queries the local server for an external domain
Look for status: NOERROR in the output. The ANSWER SECTION should contain the IP address you configured. If you see status: SERVFAIL, the upstream resolver is rejecting the query or the zone file has a syntax error. If you see status: REFUSED, the firewall or BIND configuration is blocking the request.
Check the service logs for hidden errors. The journalctl -xe command adds explanatory context and jumps to the end of the log. Most sysadmins type journalctl -xeu <unit> muscle-memory style to isolate the daemon output.
sudo journalctl -xeu dnsmasq.service # shows recent dnsmasq logs with explanations
sudo journalctl -xeu named.service # shows recent BIND logs with explanations
Run dig against the server IP from a different machine. If the query succeeds locally but fails remotely, your firewall or router is dropping the packets.
Run journalctl first. Read the actual error before guessing.
Common pitfalls and what the error looks like
DNS servers fail silently until a client times out. These are the most frequent failure modes on Fedora.
The named-checkconf command will refuse to proceed and print zone local/IN: loaded serial 2024051501. If you see zone local/IN: loading from master file /etc/named.zones/local.zone failed: syntax errors, check the trailing dots on your SOA record. Every domain name in a zone file must end with a period unless it is relative to the $ORIGIN.
If you see [FAILED] Failed to start dnsmasq.service during boot, your configuration probably references a missing interface name. dnsmasq binds to the interface at startup. If NetworkManager renames eth0 to enp3s0, the service crashes. Use the persistent interface name from ip link instead.
SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. BIND and dnsmasq both ship with correct SELinux contexts. If you moved a zone file to /tmp or /home, the policy will block the read operation. Keep zone files in /etc/named.zones or /var/named.
Upstream resolvers like Cloudflare and Google sometimes block recursive queries from non-standard source IPs. If your Fedora box has a public IP and you point it at 1.1.1.1, the resolver may return SERVFAIL. Use a local caching resolver or configure forward-only yes in dnsmasq to force strict forwarding.
Trust the package manager. Manual file edits drift, snapshots stay.
When to use dnsmasq versus BIND
Use dnsmasq when you need a lightweight resolver for a home lab or small office network. Use dnsmasq when you want DHCP and DNS in a single daemon with minimal configuration. Use BIND when you are managing authoritative zones for a public domain or a multi-site enterprise. Use BIND when you require DNSSEC validation, zone transfers, or fine-grained access control. Use systemd-resolved when you only need local hostname resolution for the Fedora machine itself and do not want to expose port 53 to the network.