This guide shows you how to detect when a tenant’s expected DNS configuration drifts from what’s actually resolved in Node.js. You’ll write a script that compares a tenant’s desired DNS records (like A, AAAA, CNAME, MX) against live DNS lookups, and reports mismatches. You’ll need Node.js 18+ and the `dns` module (built-in) or the `dns-promises` API.
What You’ll Need
- Node.js 18 or newer (for built-in fetch and promises)
- A list of tenant IDs and their expected DNS records (e.g., from a database or config file)
- Access to the DNS servers you want to query (default system resolvers are fine)
- Basic familiarity with async/await and error handling
Step-by-Step
- Set up your project. Create a new directory and run
npm init -y. Install no external packages—Node’s built-indns/promisesis enough. - Define the expected DNS configuration. Create a JSON file or an array in your script that maps tenant IDs to their expected records. Example:
{ "tenant1": { "A": ["192.0.2.1"], "CNAME": ["app.example.com"] }, "tenant2": { "MX": ["mail.tenant2.com"] } } - Import the DNS promises module. At the top of your script:
const dns = require('dns').promises;orimport { promises as dns } from 'dns';if using ES modules. - Write a function to resolve a given record type. Use
dns.resolve(hostname, rrtype). For A records it returns an array of IPs; for CNAME it returns a single string; for MX it returns an array of objects withpriorityandexchange. Wrap it in try/catch to handle NXDOMAIN or other DNS errors. - Normalize the resolved data. Convert the resolved records into a comparable format—sort arrays, lowercase hostnames, and for MX sort by priority. This avoids false positives due to ordering.
- Compare expected vs actual. Write a function that takes the expected record and the actual record, and returns true if they match (deep equality after normalization), false otherwise. For MX, compare the sorted list of
exchangevalues (and optionally priorities). - Loop through all tenants. For each tenant, for each record type defined in the expected config, resolve the tenant’s hostname (e.g.,
tenant1.example.com) and compare. Collect mismatches into an array. - Handle missing records. If a DNS lookup throws (e.g., ENOTFOUND), treat it as a mismatch: expected records exist but none are found.
- Output the results. Log each mismatch with tenant ID, record type, expected value, and actual value. If no mismatches, log “All tenants in sync”.
- Add a CLI wrapper. Use
process.argvto accept a path to the config file, or just hardcode for a quick test. Optionally add a--jsonflag for machine-readable output. - Run the script. Execute with
node check-dns.js. Ensure your system can resolve the hostnames—test withnslookupfirst if unsure.
Common Mistakes
- Not handling DNS errors properly. A failed lookup is not the same as “no records”. Use try/catch and treat errors as mismatches unless the error is clearly a timeout (then retry).
- Ignoring record ordering. DNS responses are not guaranteed to be in the same order every time. Always sort arrays before comparing.
- Forgetting TTL. DNS results are cached. If you run the check too frequently, you might see stale data. Consider using
resolve4with{ ttl: true }to get TTL, or just accept some delay. - Assuming CNAME is an array.
dns.resolvereturns a string for CNAME, not an array. Normalize it to an array for uniform comparison. - Not lowercasing hostnames. DNS is case-insensitive, but your expected config might have uppercase. Normalize to lowercase.
Tips
- Use a custom DNS server. For testing, you can pass a server to
dns.setServers()to point to a test DNS server. - Add concurrency limits. If you have many tenants, use
Promise.allwith a concurrency limit (e.g., via a simple counter) to avoid overwhelming the DNS server. - Log with timestamps. Include
new Date().toISOString()in each mismatch log for auditing. - Write a test config. Create a dummy tenant with known-good and known-bad records to verify your script works.
- Consider using a library like
dns2if you need more control over the wire protocol. For most cases, built-in is fine.
Troubleshooting
- Script throws ENOTFOUND for every tenant. Check your network connection and DNS server settings. Try
nslookup example.comto verify resolution works. - Results are always mismatched even when they shouldn’t be. Check your normalization—maybe you’re comparing arrays with different order. Log the actual and expected values to inspect.
- MX records show as mismatched due to priority. If you don’t care about priority, only compare the exchange hostnames. If you do, ensure you sort by priority.
- Script hangs. DNS timeouts can be long. Set a timeout using
AbortControllerordns.resolvewith a timeout option (not directly supported, so use a wrapper withsetTimeout). - CNAME records appear as an array with one element. That’s fine—just normalize to a string or compare the first element.
Frequently Asked Questions
What types of DNS records can I check?
Any type supported by dns.resolve: A, AAAA, CNAME, MX, NS, TXT, SRV, PTR, SOA. The comparison logic may need adjustment per type (e.g., TXT is an array of strings).
How do I handle a tenant that has no DNS records at all?
If the expected config has no records for a tenant, you can skip it or check that the hostname returns NXDOMAIN. If you expect records but get NXDOMAIN, that’s a drift.
Can I run this on a schedule?
Yes, wrap the script in a cron job or use setInterval in a long-running process. Be mindful of rate limits on your DNS server.
What if I need to check DNS from a different resolver?
Use dns.setServers(['8.8.8.8']) before resolving. This changes the resolver for all subsequent queries.
How do I compare TXT records?
TXT records are arrays of strings. Sort them and compare as arrays. Be aware that TXT records can be split into multiple strings; you may need to join them.
Is this method reliable for production?
It’s a good baseline, but for production consider using a dedicated DNS monitoring service. Also, account for caching and propagation delays (TTL).
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Increase Internal Memory Xiaomi Phones: How-To
- Nvidia Display Settings Are Not Available: Fixes
- Gmod Keeps Crashing? Fixes That Actually Work
- Cloudflare cf CLI: How Coding Agents Discover & Run Commands
- Protect AI from Model Extraction & Distillation
- Net Code Analysis Workflow: A Reliable Setup Guide