When a host suddenly resolves a domain to an old IP, the first instinct is to restart NetworkManager or reboot the machine. That works, but it’s heavy‑handed and can disrupt services. In a production environment, a brief outage can trigger SLA violations or expose stale data to clients. Below is a concise, hands‑on guide to flushing the systemd‑resolved cache safely, with a focus on security‑aware operators who need minimal downtime.
Why the Cache Persists
systemd‑resolved keeps a local cache of DNS answers to speed up lookups and reduce upstream traffic. The cache is governed by the TTL returned by the authoritative server. If a record’s TTL is long (e.g., 86400 s), the cache will hold the answer until it expires. A mis‑configured or stale record can therefore linger for hours, even days.
In 2025, many distributions shipped a systemd-resolved version that added a --flush-caches flag to resolvectl. That flag was later deprecated in favor of the flush-caches subcommand, which is still available in 2026 releases. Knowing the correct syntax for your distro is key.
Quick‑Check: Is the Cache the Culprit?
# Verify the current DNS resolver
systemd-resolve --status | grep -A 5 'DNS Servers'
# Inspect the cached entry for a specific host
resolvectl query example.com
If the resolvectl query output shows an IP that no longer matches the authoritative zone, you’re dealing with a stale cache.
Step 1: Flush the Global Cache
The simplest command clears all cached records for all interfaces:
sudo resolvectl flush-caches
Trade‑off
- Pros: Immediate effect, no service interruption.
- Cons: All cached queries are lost, so the next lookup will hit upstream DNS, adding a small latency spike.
If you’re on a system that still uses the older systemd-resolve, the equivalent is:
sudo systemd-resolve --flush-caches
Both commands are idempotent; running them repeatedly is safe.
Step 2: Targeted Cache Flush (Optional)
When you only need to clear a specific domain, you can use the --cache option:
sudo resolvectl flush-caches example.com
This removes only the cached entries for example.com. It’s useful when you know the problem is isolated to a single host, and you want to keep other cached lookups intact.
Caveat--cache is not supported on all systemd versions. If the command fails, fall back to the global flush.
Step 3: Verify the Result
resolvectl query example.com
The output should now show the new IP address. If it still shows the old one, the upstream DNS server may still be serving the stale record. In that case, wait for the TTL to expire or contact the authoritative DNS administrator.
Step 4: Harden the Cache Behavior
Stale caches can be a subtle attack vector. An adversary could poison a DNS cache and then wait for the TTL to expire before the cache is refreshed. To mitigate this:
Enable DNSSEC
sudo resolvectl dnssec-enable yes sudo resolvectl dnssec-validation yesSystemd‑resolved will reject unsigned or invalid records, reducing the risk of cache poisoning.
Reduce Default TTL
Edit
/etc/systemd/resolved.confand set a lowerCachevalue (seconds). For example:[Resolve] Cache=3600This forces the resolver to re‑query upstream every hour, limiting how long a stale record can persist.
Use a Local DNS Forwarder
Deploy
dnsmasqorunboundas a local forwarder with strict ACLs. These tools can be configured to drop queries from untrusted networks, adding an extra layer of protection.
Step 5: Automate Cache Management (Optional)
If you frequently encounter stale entries, consider a cron job that flushes the cache at a low‑impact time:
# /etc/cron.d/flush-dns-cache
30 3 * * * root resolvectl flush-caches
This runs nightly at 3 AM, a window that typically sees low traffic. Adjust the schedule to match your environment.
Common Pitfalls and Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
resolvectl query still shows old IP | Cache not flushed (wrong command) | Ensure you’re using the correct flush-caches syntax for your systemd version |
| DNS queries hang after flush | NetworkManager not re‑initializing sockets | Restart NetworkManager only if the flush fails; otherwise, use systemctl restart systemd-resolved |
| Stale records persist beyond TTL | Authoritative server still serving old record | Verify the zone file; use dig @<nameserver> example.com to confirm |
| Security alerts about DNSSEC failures | DNSSEC validation disabled or misconfigured | Re‑enable DNSSEC as shown in Step 4 |
Real‑World Example: A Homelab Scenario
I run a small media server behind a pfSense router. The router’s DNS cache was holding an old IP for my internal media.local zone after I migrated the server to a new machine. Restarting pfSense was not an option because it also hosts VPN services.
- On the media server, I ran
sudo resolvectl flush-caches. - Verified with
resolvectl query media.localthat the new IP appeared. - Added a cron job to flush the cache every 30 minutes, ensuring the server never served stale media URLs to clients.
The result: no downtime, no VPN interruption, and a cleaner DNS environment.
Security Implications
While flushing the cache is a quick fix, it’s also a reminder that DNS is a critical attack surface. By ensuring your resolver validates DNSSEC and by limiting TTLs, you reduce the window an attacker can inject malicious records. Additionally, keeping the resolver’s cache small means fewer stale entries that could be exploited.
Further Reading
- systemd‑resolved documentation: https://systemd.io/RESOLVED/
- DNSSEC best practices: https://docs.kernel.org/networking/dnssec.html
See also
- When ss Shows No Open TCP Ports Even Though Services Are Running
- When nftables silently drops HTTP traffic: a step‑by‑step port‑filter investigation
- Why My Nginx Service Stops After a Kernel Upgrade: A Systemd Timer Debugging Session in a Home‑Lab Server
- Hardening Serverless Function IAM: A Step‑by‑Step Fix for Over‑Permissive Execution Roles
- The Cost of Over‑Broad Retention: How a 30‑Day Backup Policy Exposed 2 TB of PHI