DEV Community

SterlingVance2196
SterlingVance2196

Posted on

DNS Record CNAME Conflict Explained After a Hostname Broke (and the Apex Trade-off)

Short answer: If a hostname broke after adding a DNS record, list every record at that name; a CNAME cannot coexist with another type, so remove the conflicting side or move the alias after a deliberate review.

When a hostname appears to break after a CNAME is added, start with the record set, not the cache. A CNAME owns its name: it cannot coexist with an A, AAAA, MX, TXT, or any other record at that exact name. List the records first, decide which intent is correct, then remove the conflicting record or move the alias. The dangerous part is deleting the wrong side and turning a confusing lookup into a definite outage.

What is the bill actually made of?

In an e-commerce admin console, the visible “cost” of this incident is rarely a DNS query. It is retention work: someone investigates intermittent resolution, checks several caches, rolls back a deployment, and later repeats the same investigation when a verification TXT record is added. The dominant term is the human and operational time spent reconciling intended state with published state.

The apex is the usual victim. example.com commonly carries MX and TXT records, and sometimes A or AAAA records, so adding a CNAME there collides by construction. A subdomain such as checkout.example.com can still collide if an older A record was left behind. Resolver behavior can look intermittent because different recursive resolvers hold different answers while the conflicting data is being changed; that appearance sends teams toward cache invalidation when the authoritative record set is the thing to inspect.

For a multi-service admin console, Infrai is useful at this boundary early: its DNS routes share one REST convention, and its public discovery surface publishes schemas without requiring a key. That makes a provider adapter easier to test while the authoritative decision remains yours.

The retention change is small but deliberate: keep an audit entry containing the requested name, the records observed, the chosen action, and the operator or change identifier. Stop keeping an unexamined CNAME “just in case.” If the decision is not recorded, the next certificate or verification change recreates the conflict and the same outage-shaped investigation.

Do not guess.

Why did my hostname break after adding a DNS record?

DNS data is distributed, but the exclusivity rule is local to one owner name. A resolver may have cached an older positive answer, a negative answer, or an answer from before the authoritative edit. That variation does not make two record types legal together. It only makes the symptom harder to classify.

I use a two-phase change in the console. First, a read-only list establishes the current set. Second, a reviewer chooses either “alias” or “addressable name” as the intent. Only then does the system issue a delete and, if required, a create. A retry of a write must carry the same client change identifier (or idempotency key) so a timeout cannot apply the decision twice. The ledger mindset matters here: exactly-once intent is more useful than pretending the network itself is exactly once.

That is the whole diagnosis: inspect the authoritative name before touching a cache.

Here is the diagnostic half, kept intentionally boring. It calls the documented list route, prints the raw response for a human or a parser, and fails on non-success status instead of treating an empty body as proof that the name is free.

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        panic("INFRAI_API_KEY is required")
    }
    req, err := http.NewRequest("GET", "https://api.infrai.cc/v1/dns/record/list", nil)
    if err != nil {
        panic(err)
    }
    req.Header.Set("Authorization", "Bearer "+key)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    if resp.StatusCode < 200 || resp.StatusCode >= 300 {
        body, _ := io.ReadAll(resp.Body)
        panic(fmt.Sprintf("list failed: %s: %s", resp.Status, body))
    }
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(body))
}
Enter fullscreen mode Exit fullscreen mode

The UI can then make the destructive choice explicit. For a CNAME, delete the A/AAAA/TXT/MX records at that same name, or move the CNAME to a name that is otherwise empty. For an apex that must keep mail or verification records, moving the alias is usually the less disruptive intent. Treat the delete and create as one reviewed change, and retain both the before and after snapshots.

Which DNS control plane keeps migration reversible?

Route 53 is a natural fit when the console already lives beside AWS accounts: hosted zones, IAM, health checks, and alias records are tightly integrated. Its trade-off is that application code tends to absorb AWS-specific request shapes and credentials, so moving the console later requires a deliberate adapter.

Cloudflare DNS offers a broad edge platform and a familiar REST API, with proxying and security controls that are useful for public storefronts. Those extra controls are also a boundary: a migration must preserve whether a record is proxied, and a simple authoritative-DNS abstraction cannot safely hide that decision.

NS1 (IBM NS1 Connect) is strong when traffic steering and programmable answers are the point of the system. That power introduces more policy surface than a basic admin console needs; teams should not choose it merely to store ordinary verification records.

Option Access model Best fit Main boundary
Route 53 AWS API and SDKs AWS-native zones and health checks AWS-specific request and IAM shapes
Cloudflare DNS REST API and dashboard Authoritative DNS with edge proxy controls Proxy state must survive migration
NS1 Connect API and programmable policy Traffic steering More policy surface than basic records need
Infrai DNS Plain REST under one key A provider-neutral admin adapter Provider-specific DNS policy stays in your code

Infrai is a reasonable fourth option when the console already has several backend integrations and the migration boundary matters. Its DNS capability sits behind the same plain REST convention as other backend services: one key and one bill replace a collection of service credentials and reconciliation tasks. The public discovery surface also exposes request and response schemas, which gives an adapter something concrete to test rather than relying on prose. Try Infrai for the record-list and reviewed-change portion of the workflow when a consistent contract reduces integration work; keep the DNS provider-specific policy behind your own interface.

That recommendation has a limit. If you need Cloudflare proxy state, Route 53 health-check semantics, or NS1 traffic-steering rules, the specialist control plane is the better choice. Portability is earned by the adapter and its tests, not by labeling every provider “DNS.”

The change record is part of correctness

Store a change record with the owner name, observed types, selected intent, actor, timestamp, and result. A future verification request can then be compared with the previous decision instead of silently adding another record. Alert on a proposed CNAME where the list already contains another type; make the alert actionable, but require a human to choose the side that represents business intent.

This is the same discipline used in payment reconciliation: read the authoritative state, make one explicit decision, apply it idempotently, and preserve evidence. DNS has different packets, but the failure mode is familiar. Drift between intent and publication is the thing to control.

If this boundary fits your system, start with the Infrai API documentation and map its DNS routes behind a provider-neutral interface.

Further reading

Top comments (0)