DNS Configuration skill for Codex
DNS changes checked at the source, not the cache. Codex queries the authoritative nameservers with dig before and after every change, lowers TTLs ahead of cutovers, tests new hosts with curl --resolve, and diffs entire zones before switching providers. It handles DNSSEC during moves, apex CNAME limits, the SPF ten-lookup cap and a staged DMARC rollout, and it confirms record diffs with you before applying them.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- dns-configuration Installs to ~/.agents/skills/dns-configuration/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install dns-configuration The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project dns-configuration Installs to .agents/skills/dns-configuration/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/dns-configuration.zipunzip -o dns-configuration.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) dns-configuration PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $dns-configuration, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Plans and verifies DNS changes - A, AAAA, CNAME, ALIAS and apex flattening, MX, TXT, CAA, NS, SRV - plus email authentication records (SPF, DKIM, DMARC, MTA-STS) and zero-downtime migrations between DNS providers or hosts, using dig against authoritative servers instead of trusting caches. Use when pointing a domain at a new host, a record "has not propagated", email lands in spam or fails DMARC, moving a zone from GoDaddy, Route 53 or Cloudflare to another provider, enabling or removing DNSSEC, or auditing a zone for dangling records.
Use it when
- Pointing a domain or subdomain at a new host
- Moving a zone between DNS providers or registrars
- Mail fails SPF, DKIM or DMARC, or lands in spam after adding a sender
- Auditing a zone for dangling CNAMEs and missing CAA records
Use something else when
- DNS is correct and the problem is certificate issuance or trust: use tls-certificates
- You need sending infrastructure and templates for app email: use transactional-email
How to check the result
Query every authoritative nameserver for the changed names, confirm public resolvers return new values after the old TTL, and check Gmail message headers for spf, dkim and dmarc pass.
The mistake it prevents
Lowering the TTL at the same moment as the change, so resolvers keep the old answer for the full original TTL anyway.
Done well means: every change is checked against the authoritative nameservers before and after, TTLs were lowered in advance for planned cutovers, no record is lost in a migration, and email authentication passes for every service that sends as the domain.
DNS edits are outward-facing and can take a site or mail offline for the length of a TTL. Show the exact record diff and get the user’s confirmation before applying it, and keep the previous values in the report so they can be restored.
Inspect first
dig +short NS example.com # who is authoritative
dig @ns1.provider.net example.com A +norecurse # ask the source of truth, not a cache
dig example.com SOA +short # serial, negative-cache TTL (last field)
dig +trace www.example.com # walk delegation from the root
dig @1.1.1.1 www.example.com +noall +answer # what a public resolver has cached (TTL counts down)
dig example.com TXT +short ; dig _dmarc.example.com TXT +short
whois example.com | grep -iE 'name server|registrar' # delegation at the registrar
All of these need network access; in a Codex sandbox with network off, ask for approval
or have the user run them. dig is in bind9-dnsutils / bind-utils; on Windows use
Resolve-DnsName -Server.
“Propagation” is cache expiry. A resolver that cached the old answer keeps it until the TTL it saw runs out. Failed lookups (NXDOMAIN) are cached too, for the SOA minimum value, so querying a record before creating it can delay it.
Record rules that bite
| Rule | Consequence |
|---|---|
| A CNAME cannot coexist with any other record at the same name | No CNAME at the apex (it has SOA and NS); use the provider’s ALIAS/ANAME/flattening or A/AAAA |
| MX and NS targets must be hostnames with A/AAAA, never CNAMEs or IPs | Some mail servers reject or misroute |
| Only one SPF TXT record per name | Two v=spf1 records = SPF permerror, mail fails |
| TXT values over 255 chars are split into quoted strings | Paste DKIM keys as the provider expects; check with dig TXT that the joined value is intact |
| Trailing dot means fully qualified in zone files | www CNAME app.example.com without the dot becomes app.example.com.example.com |
| PTR (reverse DNS) belongs to whoever owns the IP | Set it at the hosting provider, not in your zone |
| CAA restricts which CAs may issue | Add the CA before switching certificate providers |
Changing where a site points
- [ ] 1. Record current values and TTLs from the authoritative servers
- [ ] 2. At least one old-TTL ahead, lower TTL to 300 (wait the OLD TTL before step 4)
- [ ] 3. Bring the new target up; test it with curl --resolve before DNS points at it
- [ ] 4. Confirm the diff with the user, then change the record(s)
- [ ] 5. Verify on authoritative servers, then on 1.1.1.1 and 8.8.8.8
- [ ] 6. Keep the old target serving until old TTL plus a margin has passed
- [ ] 7. Raise TTL back (3600 or more) once stable
curl -sv --resolve www.example.com:443:203.0.113.20 https://www.example.com/ -o /dev/null
--resolve tests the new host with the real hostname, SNI and certificate before any DNS
change. Both A and AAAA must point at the new host; a stale AAAA sends IPv6 clients to the
old server.
Moving a zone to another DNS provider
- Export the full zone from the old provider (zone file or API). Provider UIs often hide
records created by integrations; check for
_domainkey,_acme-challenge, verification TXT records, SRV records and wildcard entries. - Import into the new provider. Convert provider-specific types (Cloudflare proxied records, Route 53 alias records) to what the new provider supports.
- Compare every name and type between old and new authoritative servers before touching the registrar:
OLD=ns1.oldprovider.net; NEW=ns1.newprovider.net
for name in example.com www.example.com api.example.com _dmarc.example.com \
selector1._domainkey.example.com; do
for type in A AAAA CNAME MX TXT CAA; do
a=$(dig +short @"$OLD" "$name" "$type" | sort); b=$(dig +short @"$NEW" "$name" "$type" | sort)
[ "$a" != "$b" ] && printf 'DIFF %s %s\n old: %s\n new: %s\n' "$name" "$type" "$a" "$b"
done
done; echo "compare done"
- Lower the NS TTL at the old provider if it allows. Note the parent (TLD) NS TTL is often 1 to 2 days and cannot be changed.
- With confirmation, change nameservers at the registrar. Keep the old zone intact and unchanged for at least 48 hours; resolvers keep using it until the delegation expires.
- Make no record edits during the overlap, or make them in both zones.
DNSSEC during a move
If the zone is signed, a naive NS change breaks resolution for validating resolvers (including 1.1.1.1, 8.8.8.8 and many ISP resolvers) because the DS record at the registrar matches the old keys. Safe path: remove the DS record at the registrar, wait for the parent DS TTL to expire, move nameservers, re-enable signing at the new provider, then publish the new DS. A multi-signer key handover avoids the unsigned window but needs both providers to support it.
Email authentication
Every service that sends mail as the domain (Google Workspace, Microsoft 365, the transactional provider, the CRM, the helpdesk) must pass SPF or DKIM with alignment, or DMARC fails. Gmail and Yahoo require SPF, DKIM and DMARC for bulk senders.
example.com. TXT "v=spf1 include:_spf.google.com include:amazonses.com -all"
s1._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." (or CNAME to provider)
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:[email protected]; adkim=r; aspf=r"
- SPF allows at most 10 DNS-querying mechanisms (
include,a,mx,exists,redirect), counted recursively. Exceeding it is apermerror. Drop unused includes, or send bulk mail from a subdomain with its own SPF. ~all(softfail) while inventorying senders;-allonce every sender is listed. DMARC is what actually enforces policy.- DMARC rollout:
p=nonewithruareports for 2 to 4 weeks, fix every legitimate source in the reports, thenp=quarantine, thenp=reject. Moving straight to reject drops mail from forgotten senders. - Domains that never send mail:
v=spf1 -all,_dmarcwithp=reject, and a null MX (MX 0 .) to stop spoofing. - MTA-STS (
_mta-stsTXT plus a policy file athttps://mta-sts.example.com/.well-known/mta-sts.txt) and TLS-RPT (_smtp._tlsTXT) enforce TLS for inbound mail; start intestingmode.
Verify with real headers: send to a Gmail address, open “Show original” and check
spf=pass, dkim=pass, dmarc=pass. Sending setup and deliverability practice belong to
$transactional-email.
Hygiene and security
- Dangling records: a CNAME to a deleted cloud resource (S3 bucket, Heroku app, Azure site) lets someone else claim it and serve content on your name. When decommissioning, remove the DNS record first, then the resource.
- Add CAA for the CAs you use, plus
iodeffor violation reports. - Lock the domain at the registrar (transfer lock, registry lock for critical domains) and enable 2FA on the registrar and DNS accounts.
- Keep zones in code where the provider supports it (Terraform, OctoDNS, DNSControl) so changes are reviewed.
Verification
- Authoritative answer matches the intended value on every nameserver:
for ns in $(dig +short NS example.com); do dig +short @"$ns" www.example.com A; done. - Public resolvers return the new value after the old TTL; if not, check the TTL they report rather than flushing caches you do not control.
- For mail: headers show SPF, DKIM and DMARC pass; DMARC aggregate reports show no failing legitimate source.
- After a zone move: the compare loop prints no DIFF lines, and
dig +traceends at the new provider.
Change report template
**Zone:** example.com (authoritative: Cloudflare)
| Name | Type | Before (TTL) | After (TTL) |
|---|---|---|---|
| www | CNAME | old-lb.example.net (3600) | app.fly.dev (300) |
| @ | A | 198.51.100.7 (3600) | 203.0.113.20 (300) |
**Pre-checks:** TTL lowered 2025-06-02 10:00 UTC (old TTL 3600 elapsed); curl --resolve OK
**Post-checks:** all 4 NS return new values; 1.1.1.1 and 8.8.8.8 updated by 11:12 UTC
**Rollback:** restore the "Before" values above; old host stays up until 2025-06-04 What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "DNS Configuration"
short_description: "DNS changes checked at the source, not the cache"
default_prompt: "$dns-configuration Move example.com from GoDaddy DNS to Cloudflare without breaking email" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.