DNS Management
Domain registrar: Namecheap (registrar only — it just holds the
domain registration and points the nameservers at Cloudflare). DNS records are managed in
Cloudflare (account sean@immersive-app.com, free plan) since
2026-07-23 — see issue #505 for the
migration. Email hosting (Namecheap Private Email, including the catch-all rule that forwards
any-address@immersive-app.com → bonjour@immersive-app.com) is a separate paid service riding
on the MX records below; it was unaffected by the move because the records travelled verbatim.
Proxy status (“orange cloud” vs “DNS only”): a proxied record makes Cloudflare answer with its own edge IPs and relay traffic to the origin (CDN, WAF, hides the origin IP). DNS only means Cloudflare merely publishes the record. Web records are proxied; every mail-related record must stay DNS only — proxying a mail record routes SMTP/verification traffic into Cloudflare’s HTTP proxy, which breaks it.
Web traffic (proxied)
| Host | Type | Value | Proxy |
|---|---|---|---|
@ |
A | 167.233.217.165 (Hetzner VM immersive-prod-01) |
Proxied |
www |
A | 167.233.217.165 (Hetzner VM immersive-prod-01) |
Proxied |
What they mean: an A record maps a hostname to an IPv4 address. @ is the bare domain
(immersive-app.com), www the usual alias; both point at the single Hetzner VM that runs
the whole app (see kamal.md). Because they are proxied, the public internet only ever sees
Cloudflare’s IPs — visitors reach a Cloudflare edge, which fetches from our origin.
Related settings that live with these records:
- SSL/TLS mode: Full (strict) — Cloudflare-to-origin traffic is encrypted and the origin must present a valid certificate. Ours does: kamal-proxy serves a Let’s Encrypt cert, auto-renewed ~30 days before expiry via HTTP-01 (the challenge request passes through the proxy like any other request). If a renewal ever fails post-proxy, the fallback is a free Cloudflare origin certificate installed on the VM.
- Origin firewall: the VM only accepts ports 80/443 from Cloudflare’s published IP
ranges, so nobody can bypass the proxy by hitting the origin IP directly — see the
“Origin firewall” section of
kamal.md. SSH/deploys go to the origin IP and are unaffected. - Client IPs: behind the proxy the raw TCP peer is a Cloudflare edge, so the app uses the
cloudflare-railsgem to restore the real visitor address inrequest.remote_ip— without it, Rack::Attack would throttle whole Cloudflare edges (i.e. everyone at once) instead of individual visitors, and Ahoy analytics would record edge IPs.
Email delivery — Namecheap Private Email (DNS only)
| Host | Type | Priority | Value |
|---|---|---|---|
@ |
MX | 10 | mx1.privateemail.com |
@ |
MX | 10 | mx2.privateemail.com |
What they mean: MX (mail exchanger) records tell the world’s mail servers where to deliver
mail addressed to …@immersive-app.com — here, Namecheap’s Private Email servers (two, same
priority, for redundancy). The catch-all forwarding to bonjour@ is configured inside the
Private Email service, not in DNS; DNS only gets the mail to their servers.
(mail.immersive-app.com is the webmail URL — no extra DNS record needed.)
Email authentication (DNS only)
| Host | Type | Value |
|---|---|---|
@ |
TXT | v=spf1 include:_spf.mailersend.net include:spf.privateemail.com ~all |
default._domainkey |
TXT | v=DKIM1;k=rsa;p=... (public key from MailerSend’s domain verification page) |
_dmarc |
TXT | v=DMARC1; p=none; rua=mailto:sean@immersive-app.com |
What they mean — the three standards receiving servers use to decide whether mail claiming
to be from immersive-app.com is genuine:
- SPF lists which servers are allowed to send on the domain’s behalf — ours authorizes MailerSend (transactional app mail) and Private Email (human mail). Exactly one SPF record is permitted per domain: never add a second, merge new senders into this one, or delivery breaks for everything.
- DKIM publishes the public key that matching mail is cryptographically signed with — receiving servers verify the signature to prove the message wasn’t forged or altered.
- DMARC tells receivers what to do when SPF/DKIM checks fail.
p=noneis monitoring mode: nothing gets blocked, but aggregate reports are mailed tosean@. After a few weeks of clean reports, tighten top=quarantinethenp=rejectto actively block spoofed mail.
MailerSend plumbing (DNS only — see docs/technical/production/mailersend.md)
| Host | Type | Value |
|---|---|---|
mlsend2._domainkey |
CNAME | mlsend2._domainkey.mailersend.net. |
mta |
CNAME | mailersend.net. (return-path/bounce handling) |
What they mean: both delegate a subdomain to MailerSend. The mlsend2._domainkey CNAME is
MailerSend’s second DKIM key (they rotate/verify against it); mta is the bounce/return-path
domain so undeliverable transactional mail routes back through MailerSend for bounce tracking.
Caution from the migration: Cloudflare’s automatic import dropped the mlsend2._domainkey
CNAME (underscore-prefixed CNAMEs are commonly missed) — it was re-added manually. Check both
exist after any future DNS provider change, and keep them DNS only (the import also defaulted
mta to proxied, which had to be switched off).
History
Until 2026-07-23 all records lived at Namecheap’s DNS with deliberately low (1 min) TTLs from the Heroku→Hetzner cutover (issue #288) — which made the Cloudflare migration propagate fast. Before that, the web records were CNAME/ALIAS to Heroku DNS targets — see
docs/technical/production/heroku.mdfor the historical values.
References
- MailerSend domain verification
docs/technical/production/mailersend.md— SMTP/webhook application configdocs/technical/production/heroku.md— historical Heroku DNS targetsdocs/technical/production/kamal.md— current hosting setup, origin firewall- Issue #505 — Cloudflare migration record