DNS for Developers: A, CNAME, MX, and TXT Records with dig
Configure the four essential DNS records for a real domain and verify resolution, TTLs, and propagation with dig.
What you'll build
You'll configure the four DNS records every developer touches — A, CNAME, MX, and TXT — for a domain you own, then prove each one resolves correctly with dig and confirm worldwide propagation with online lookup tools.
Prerequisites
- A registered domain and access to its DNS management panel. Any provider works (Cloudflare, Route 53, Namecheap) — the record values are identical everywhere; only the dashboard differs.
dig, part of ISC's BIND utilities. It's preinstalled on macOS and most Linux distros; on Debian/Ubuntu runsudo apt install bind9-dnsutils. On Windows, use WSL. Verified against DiG 9.10.6 (macOS) and the BIND 9.20 docs.- A server IP to point at. I'll use
203.0.113.10(a reserved documentation IP) andexample.comas the domain — substitute your own values in every command.
1. Point your domain at a server with an A record
An A record maps a name to an IPv4 address. In your provider's DNS panel, add:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | @ |
203.0.113.10 |
300 |
@ means the domain apex (example.com itself). TTL is how long resolvers may cache the answer, in seconds — use 300 while you're setting things up so mistakes expire in five minutes, and raise it to 3600+ once the record is stable.
2. Alias www with a CNAME record
A CNAME says "this name is an alias for that name" — resolvers restart the lookup at the target, so www automatically follows wherever the apex points:
| Type | Name | Value | TTL |
|---|---|---|---|
| CNAME | www |
example.com |
300 |
Two rules bite people here: a name with a CNAME can hold no other records, and the spec forbids a CNAME at the apex — that's why providers reject @ here or offer flattened ALIAS/ANAME types instead.
3. Route mail with an MX record
MX records tell sending mail servers where to deliver mail for your domain. The value is always a hostname, never an IP. Your mail provider supplies it — Google Workspace, for example, uses a single record:
| Type | Name | Value | Priority | TTL |
|---|---|---|---|---|
| MX | @ |
smtp.google.com |
1 |
3600 |
Priority orders multiple MX records: lowest number wins, higher numbers are fallbacks. With one record the value barely matters, but the field is required.
4. Publish policy and proofs with TXT records
TXT records hold arbitrary strings, used for domain-verification tokens and email policy. Add Google's recommended SPF record, which lists who may send mail as your domain:
| Type | Name | Value | TTL |
|---|---|---|---|
| TXT | @ |
v=spf1 include:_spf.google.com ~all |
3600 |
Swap the include: for your provider's SPF domain if you're not on Google. A domain can hold many TXT records, but only one starting with v=spf1.
Verify it works
Query each record type. +noall +answer strips dig's output down to the answer section:
dig +noall +answer example.com A
dig +noall +answer www.example.com CNAME
dig +noall +answer example.com MX
dig +noall +answer example.com TXT
Expected output (name, TTL remaining, class, type, value):
example.com. 300 IN A 203.0.113.10
www.example.com. 300 IN CNAME example.com.
example.com. 3600 IN MX 1 smtp.google.com.
example.com. 3600 IN TXT "v=spf1 include:_spf.google.com ~all"
A TTL below what you configured means you got a cached answer counting down — that's normal. To bypass your local cache and ask a specific public resolver, prepend @:
dig @1.1.1.1 +short example.com A
203.0.113.10
For a second opinion from the browser, use Google Admin Toolbox Dig, and check propagation across dozens of countries at DNSChecker — new records typically appear worldwide within minutes, but resolvers that cached the old state serve it until the old TTL expires.
Troubleshooting
;; connection timed out; no servers could be reached — dig couldn't reach a resolver at all. You're offline, a firewall is blocking port 53, or the @server you specified is wrong. Try dig @1.1.1.1 example.com to isolate it.
status: NXDOMAIN in the header — the name doesn't exist anywhere. Check for a typo, and confirm your registrar actually points at the nameservers where you added records: dig +short example.com NS.
status: NOERROR but ANSWER: 0 — the name exists but has no record of the type you asked for. You added the wrong type, or the record hasn't reached that resolver yet. Ask an authoritative nameserver directly, since it never serves stale data: dig @$(dig +short example.com NS | head -1) example.com A.
Old value keeps coming back — caching. Resolvers hold the previous answer for its full original TTL, and you can't flush caches you don't own. Wait it out, and set TTLs to 300 before future migrations.
Next steps
Run dig +trace example.com to watch delegation walk from the root servers down — the best way to internalize how resolution actually works. Then add AAAA (IPv6) records, finish your email hygiene with DKIM and DMARC TXT records, and look into DNSSEC to sign your zone.
Sources & further reading
- BIND 9 Manual Pages (dig) — bind9.readthedocs.io
- Set up MX records for Google Workspace — knowledge.workspace.google.com
- Set up SPF — knowledge.workspace.google.com
- Google Admin Toolbox Dig — toolbox.googleapps.com
- bind9-dnsutils package — launchpad.net
Emeka has spent over a decade tracking threat actors, vulnerability disclosures, and the evolving landscape of application security, bringing a sharp continent-spanning perspective to his reporting. He's known for translating dense CVE advisories into clear, actionable context that developers and security teams alike actually read.
Discussion 1
been there with route53 where a DNS change looked instant locally but cost us three hours and a few hundred bucks in misdirected traffic while propagation caught up. dig is your friend for catching these things, but also worth setting reasonable TTLs upfront so you're not eating egress fees on failed lookups across regions.