DNS Management - Operating the fogserv.cloud Zone
Status: Active
Last Updated: 2026-08-26
Category: Networking - DNS Operations
Prerequisites: dns-explained, ip-addressing-subnets
Time: 2 hours
Tags: dns, zones, registrar, naming-conventions, cloudflare, operations
Summary
How we actually run DNS for fogserv.cloud: the split between registrar and DNS host, zone layout and naming conventions for hosts and services, change procedure (TTL lowering, verification), and keeping records in version control via Cloudflare's API.
๐ฏ What You'll Learn
By the end of this article, you'll be able to:
- โ Explain which company does what: registrar vs DNS host vs CDN
- โ Apply our naming convention so new services get consistent names
- โ Execute a safe record-change procedure with TTL management
- โ Audit and export the zone programmatically
Table of Contents
- Context / Why This Matters
- Registrar vs DNS Host
- Zone Layout & Naming Conventions
- Safe Change Procedure
- DNS-as-Code via API
- Troubleshooting & Common Pitfalls
- Next Steps / Ops Actions
- Sources & Related
- Change Log
Context / Why This Matters
DNS is the front door to everything we self-host. Ad hoc record changes ("just add an A record real quick") accumulate into a zone nobody understands and outages nobody can explain. A small set of conventions plus a repeatable change procedure keeps the zone auditable โ especially once Cloudflare proxies traffic in front of it (cloudflare-dns).
Registrar vs DNS Host
Three distinct roles, often confused:
| Role | What it does | Who does it for us |
|---|---|---|
| Registrar | Sells/renews the domain; sets delegation (NS records) at the TLD | Our domain registrar |
| DNS host | Runs authoritative nameservers holding actual zone data | Cloudflare |
| CDN/proxy | Terminates TLS/caches/filters traffic for proxied hostnames | Cloudflare |
The only thing configured at the registrar is which nameservers are authoritative (fogserv.cloud NS โ xxx.ns.cloudflare.com). Everything else lives in the Cloudflare zone. Consequence: moving DNS hosts is easy (change NS, copy records); moving registrars is slower (transfer process, 60-day locks) but doesn't touch resolution if NS stay put.
Zone Layout & Naming Conventions
fogserv.cloud. SOA/NS @ Cloudflare
@ A 203.0.113.10 (proxied) apex โ edge
www CNAME fogserv.cloud
edge A 203.0.113.10 (proxied) the public entry point
<service> CNAME edge.fogserv.cloud grafana, jellyfin, git, ...
vpn A <home IP> (DNS only, low TTL) see dynamic-dns
_acme-challenge TXT managed automatically by DNS-01
Naming rules:
- Hostnames are physical (
edge,web-01,db-01); service names are virtual (git,grafana) and always point at a hostname or proxy, never directly at an IP. - Services never reference hardware. Rebuild web-01 on new hardware and nothing in DNS changes.
- Internal-only names live in the split-horizon view on our LAN resolver (dhcp-dns-split-horizon) โ e.g.
nas.fogserv.cloudresolving to 10.10.0.31 locally, unpublished publicly. - One line per purpose; delete dead records the day a service dies.
Safe Change Procedure
# 0. Snapshot current state
curl -s -H "Authorization: Bearer $CF_TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
| jq -r '.result[] | "\(.type)\t\(.name)\t\(.content)\tproxied=\(.proxied)\ttl=\(.ttl)"' \
> dns-snapshot-$(date +%F).txt
# 1. If repointing a busy name: lower TTL ahead of time (done โฅ old-TTL before change)
# 2. Make the change (dashboard or API)
# 3. Verify from authority AND from big resolvers
dig @$(dig +short NS fogserv.cloud | head -1) grafana.fogserv.cloud +short
dig @1.1.1.1 grafana.fogserv.cloud +short
dig @8.8.8.8 grafana.fogserv.cloud +short
# 4. Raise TTL back; note the change in commit/Change Log
DNS-as-Code via API
Keep the zone reviewed like code. Minimum viable approach: scheduled export committed to the infra repo + changes made by script rather than dashboard clicking.
# Create/update a record idempotently
CF_API="https://api.cloudflare.com/client/v4"
REC='{"type":"CNAME","name":"grafana","content":"edge.fogserv.cloud","proxied":true,"ttl":1}'
EXISTING=$(curl -s -H "Authorization: Bearer $CF_TOKEN" \
"$CF_API/zones/$ZONE_ID/dns_records?type=CNAME&name=grafana.fogserv.cloud" \
| jq -r '.result[0].id // empty')
if [ -n "$EXISTING" ]; then
curl -s -X PUT -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
--data "$REC" "$CF_API/zones/$ZONE_ID/dns_records/$EXISTING" | jq .success
else
curl -s -X POST -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
--data "$REC" "$CF_API/zones/$ZONE_ID/dns_records" | jq .success
fi
Token scope: Zone.DNS Edit on the fogserv.cloud zone only โ never a Global API Key for routine automation.
Troubleshooting & Common Pitfalls
| Problem | Cause | Fix |
|---|---|---|
| Record exists but world can't see it | Typo'd name created grafana.fogserv.cloud.fogserv.cloud-style duplication |
Enter short names carefully; verify with dig against authoritative NS |
| Zone transfer/move broke email or ACME | Records forgotten during migration | Always migrate from an API export, not memory |
| Two records claim the same service | Dashboard edits bypassed conventions | Quarterly audit: diff live export against documented inventory |
| Locked out of DNS during registrar issue | Single operator, MFA on personal device | Recovery codes stored per ../security/password-management |
| Automation token stopped working | Token rotated/expired | Scoped tokens expire; document expiry date next to the secret ref |
Next Steps / Ops Actions
- Configure the zone at Cloudflare properly: cloudflare-dns
- Automate home-IP updates: dynamic-dns
- Serve internal overrides: dhcp-dns-split-horizon
Sources & Related
External references consulted:
- https://developers.cloudflare.com/api/resources/dns/
- https://www.rfc-editor.org/rfc/rfc1912 (common DNS operational errors)
Related knowledge-base articles:
Change Log
2026-08-26
- Initial creation: role separation, naming conventions, safe change procedure, API examples.