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:


Table of Contents

  1. Context / Why This Matters
  2. Registrar vs DNS Host
  3. Zone Layout & Naming Conventions
  4. Safe Change Procedure
  5. DNS-as-Code via API
  6. Troubleshooting & Common Pitfalls
  7. Next Steps / Ops Actions
  8. Sources & Related
  9. 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:

  1. 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.
  2. Services never reference hardware. Rebuild web-01 on new hardware and nothing in DNS changes.
  3. Internal-only names live in the split-horizon view on our LAN resolver (dhcp-dns-split-horizon) โ€” e.g. nas.fogserv.cloud resolving to 10.10.0.31 locally, unpublished publicly.
  4. 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

Sources & Related

External references consulted:

Related knowledge-base articles:

Change Log

2026-08-26

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse