DEV Community

ThomasMoore157
ThomasMoore157

Posted on

Node.js Hostnames Across Multiple Brands: DNS TTLs, Cutovers, and Mail Reputation

Short answer: keep customer-facing hostnames in a shared DNS operating model, but isolate each brand's mail-authentication policy and reporting. A zone-per-brand boundary is justified when a brand needs independent delegation, incident response, or DMARC enforcement; it is unnecessary for ordinary web hostnames. Pick the boundary from the failure you need to contain, not from the number of CNAME records.

In an edtech product, a school may point learn.northstar.edu at the same application that serves courses.riverbend.org. The web route is shared. The email identity is not. A registrar transfer, a low DNS TTL, and a DMARC policy change can all happen in the same week, but they have different blast radii and different SLOs.

The zone boundary is an email control plane

DNS is often treated as a lookup database for custom domains. That model is too small once the product sends enrollment notices, password resets, and teacher reports for several brands. SPF, DKIM selectors, and DMARC records sit in DNS, and DMARC evaluates alignment between the visible From domain and authenticated mail streams. RFC 7489 describes the policy and aggregate-reporting model; it does not require one zone layout, so the layout remains an operational decision.

The useful separation is between web ownership and mail ownership. A central platform team can provision app.brand.example and status.brand.example from one workflow while each brand retains control of _dmarc.brand.example, its DKIM selectors, and the mailbox that receives rua reports. Delegation can be explicit, or a central team can enforce a change review; the important part is that an accidental web cutover cannot silently change mail policy.

Three signals tell me the shared model has become unsafe: a brand asks for a different DMARC enforcement date, a provider needs an emergency DKIM rotation without touching other tenants, or aggregate reports cannot be attributed to one sending stream. Those are ownership signals, not record-count signals.

How should multiple brands, many hostnames, and mail reputation fit one DNS plan?

Start with a matrix rather than a slogan. “One DNS zone” and “zone per brand” are not complete designs until delegation, credentials, and rollback are written down.

Decision area Shared operational zone Delegated zone per brand
Web hostname rollout One pipeline and one approval path Brand-specific pipeline or handoff
Mail policy blast radius A mistaken record can affect many brands Policy changes stay inside one brand
Cutover speed Fast when the platform owns records Slower when approvals cross teams
Incident response Central on-call can restore routing Brand owner can contain mail changes
Build burden Fewer integrations and templates More delegation, access, and audit work

For a small set of brands, I would keep web records centrally managed and give mail records a separate ownership path, even if both paths publish into the same authoritative zone. For a regulated school network, an independent brand zone is easier to defend in an audit because the delegation itself is evidence of control. Your mileage may vary: a zone boundary does not repair poor DKIM key handling or an unmonitored p=none policy.

Do not confuse DNS isolation with sender isolation. If every brand sends through the same envelope domain and IP pool, separate zones alone will not create separate mail reputations. Align the From domain, DKIM d= value, and sending identity; then use DMARC aggregate reports to verify what receivers actually see. The DNS design should make that evidence easy to attribute.

A cutover runbook for custom hostnames

The safe sequence is boring, which is exactly what you want during a term-time release.

First, inventory the current owner, TTL, validation status, and intended target for every hostname. Include MX, TXT, CNAME, and delegated NS records; a web-only inventory misses mail dependencies. Export the inventory to version control and attach a change identifier to each record.

Next, lower TTLs ahead of the change, but set an explicit restore date. A 300-second TTL does not guarantee a five-minute cutover because recursive resolvers can retain answers under their own rules and users may be behind caches you do not control. I plan against the observed propagation distribution, not the nominal TTL.

Then publish the new target while the old target still serves traffic. For an edtech custom domain, that means the Node.js edge route accepts both the old and new certificate mappings during the overlap. Validate with authoritative queries and several public resolvers, and check TLS for the exact hostname. DNS success without certificate success is still an outage.

Consider a Monday morning cutover for learn.northstar.edu, where 4,000 students are expected to sign in between 08:00 and 09:00. At 07:30, publish the new CNAME and leave the old edge route serving the same application. At 07:35, query the authoritative nameservers, then query resolvers in the regions where the school operates; at 07:45, fetch the hostname over HTTPS and verify the certificate chain and a real login redirect. Keep a counter for requests that still arrive at the old edge, because that is direct evidence of cache lag. If the new edge has a bad certificate or an incorrect tenant mapping, route the hostname back to the old target, stop the rollout, and leave the TTL unchanged until the cause is understood. After the class period, compare the resolver observations with sign-in errors and support tickets. Only then should the old mapping be retired. This sequence costs one overlap window and a few extra probes, but it turns an invisible cache tail into an observable release condition; that is a better trade than asking a teacher to explain why one classroom sees the old product while another sees the new one.

The mail path gets its own gate. Verify SPF syntax, DKIM selector resolution, and DMARC alignment before moving from monitoring to enforcement. RFC 7489's aggregate reports are delayed evidence, so keep the overlap long enough to observe a representative class schedule; an immediate p=reject change is a risky shortcut.

Here is a small Go check that keeps the release pipeline honest. It does not pretend to be a DNS provider client; it resolves the records the receiver will use and fails the change when the expected values are absent.

package main

import (
    "context"
    "fmt"
    "net"
    "os"
    "strings"
    "time"
)

func contains(values []string, want string) bool {
    for _, value := range values {
        if strings.TrimSuffix(value, ".") == strings.TrimSuffix(want, ".") {
            return true
        }
    }
    return false
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
    defer cancel()

    host := os.Getenv("CUSTOM_HOST")
    target := os.Getenv("EXPECTED_CNAME")
    if host == "" || target == "" {
        panic("CUSTOM_HOST and EXPECTED_CNAME are required")
    }

    resolver := net.Resolver{}
    answers, err := resolver.LookupCNAME(ctx, host)
    if err != nil {
        panic(fmt.Sprintf("CNAME lookup failed: %v", err))
    }
    if !contains([]string{answers}, target) {
        panic(fmt.Sprintf("%s points to %s, expected %s", host, answers, target))
    }
    fmt.Printf("%s is ready for cutover\n", host)
}
Enter fullscreen mode Exit fullscreen mode

The check belongs beside certificate and application health checks, not in a one-off laptop script. Keep the old endpoint available until resolver observations, TLS checks, and application probes agree.

Keep the overlap.

Then remove the old record only after the rollback window closes.

Verification, rollback, and the SLO you can defend

Define two SLOs separately: hostname cutover convergence and mail-policy safety. The first can be measured as the percentage of sampled resolvers returning the new target; the second can be measured as the percentage of DMARC reports with aligned SPF or DKIM for each brand. A single “DNS is green” check hides both failure modes.

During the window, record resolver, timestamp, answer, certificate subject, and HTTP status. If the new target is wrong, restore the previous record and keep serving the old endpoint. Do not raise the TTL until the rollback decision is made. A rollback that depends on every recursive cache expiring is not a rollback plan; it is a hope.

The catch is operational overhead. Per-brand zones mean more delegation records, more credentials, and more renewal checks, and they are not suitable when one small team cannot staff those controls. Stick with a centrally managed zone when brands share an on-call rotation and need fast, coordinated web releases; split delegation when independent mail policy and incident containment are worth the extra work.

I’m not sure any team can predict propagation precisely from TTL alone. Measure it in your audience's resolvers, document the uncertainty, and make the release gate tolerate a slow tail. That is a capacity-planning problem for on-call attention as much as it is a DNS problem.

References

Top comments (0)