TL;DR: Treat a DNS zone identifier as an opaque control-plane key, never as a domain name, and keep it in a different field from the tenant hostname. Before any write, resolve the authoritative zone, retain both its human-readable name and provider-issued identifier, verify that the requested owner name belongs under that zone, and log the mapping used for the mutation. For a media platform assigning every tenant a subdomain, this boundary matters more than a clever retry policy: a validation rejection is usually deterministic, so retrying the same malformed request only spends error budget without creating a record.
The operational recommendation is to stop the write path before it reaches the DNS API whenever zone_id == record_name or the zone identity is absent. Use the provider's identifier only in the control-plane selector; use the fully qualified domain name only in record data. Then prove the published result independently, especially when the subdomain also carries mail-authentication records whose presence is part of deliverability evidence.
How should a rejected DNS record write select its zone?
The string may be a perfectly valid domain and still be invalid for the field receiving it. A record mutation commonly has two namespaces in play: the zone is selected with a provider-scoped identifier, while the owner name describes where the record sits in DNS. They can both arrive from a configuration file as strings, which makes an accidental swap compile cleanly and fail only at the API boundary.
That is the trap.
Retries cannot fix that.
In a tenant onboarding flow, suppose the intended owner is daily-wire.media.example, the managed zone is media.example, and the control plane has assigned that zone an opaque ID. Sending media.example where the ID belongs does not ask the service to infer the zone; it violates the request contract. Sending daily-wire.media.example in that field is worse conceptually because it confuses a record owner with the container that owns it. Neither case becomes transient just because the response arrived during a deployment.
Classify the rejection before touching retries. Authentication and authorization failures need credential or policy work. Rate limiting calls for backoff. A zone-identifier validation error calls for correcting request construction. Those branches have different owners and different recovery times, and combining them under a generic "DNS failed" alert makes the on-call engineer rediscover the interface while tenants wait.
Keep identity, name, and intent separate
The safest request model makes an invalid combination awkward to express. Separate the provider's zone key, the DNS zone name, the record owner, and the onboarding intent even if every value is serialized as text. Typed wrappers do not prove that a zone exists, but they prevent the most common positional mix-up and give review tools meaningful names to inspect.
package dnschange
import (
"context"
"errors"
"fmt"
"strings"
)
type ZoneID string
type DNSName string
type Zone struct {
ID ZoneID
Name DNSName
}
type RecordChange struct {
ZoneID ZoneID
Owner DNSName
Type string
Value string
}
type Writer interface {
Upsert(ctx context.Context, change RecordChange) error
}
func BuildChange(zone Zone, owner DNSName, recordType, value string) (RecordChange, error) {
zoneName := strings.TrimSuffix(strings.ToLower(string(zone.Name)), ".")
ownerName := strings.TrimSuffix(strings.ToLower(string(owner)), ".")
if zone.ID == "" || zoneName == "" || ownerName == "" {
return RecordChange{}, errors.New("zone ID, zone name, and owner are required")
}
if string(zone.ID) == ownerName || string(zone.ID) == zoneName {
return RecordChange{}, errors.New("zone ID must not be a DNS name")
}
if ownerName != zoneName && !strings.HasSuffix(ownerName, "."+zoneName) {
return RecordChange{}, fmt.Errorf("owner %q is outside zone %q", ownerName, zoneName)
}
return RecordChange{
ZoneID: zone.ID,
Owner: DNSName(ownerName),
Type: strings.ToUpper(recordType),
Value: value,
}, nil
}
This guard is deliberately narrow. It catches missing identities, obvious field swaps, and owners outside the selected zone; it does not pretend to replace authoritative discovery or provider validation. The zone lookup should happen once at a controlled boundary, and its result should be passed forward as the Zone pair. Do not reconstruct an ID from a domain later in the pipeline.
Idempotency belongs here too. A worker may receive the same tenant event more than once, so the desired record set should be derived from stable tenant state and applied as an upsert or reconciled against observed state. The exact write primitive depends on the control plane, but the invariant does not: repeated processing must converge on the same owner, type, and value rather than minting a new identity.
Make deliverability evidence part of acceptance
For a media tenant, "the API accepted it" is an incomplete success condition. The useful evidence is that an independent DNS lookup observes the intended data after publication, while the automation can tie that observation back to the tenant, zone name, zone ID fingerprint, and change attempt. Keep the raw identifier out of broad log fields if it is sensitive; a stable fingerprint is enough for correlation.
Mail-related subdomains need a more specific check. DMARC defines a DNS-published policy record and a reporting mechanism for message authentication results. If tenant onboarding includes a DMARC policy, preserve the exact owner and value chosen by the mail design, then query for that record as a separate verification step. A successful unrelated address record does not establish that the DMARC record is present, and the write response alone is not deliverability evidence.
Capacity planning should use stages, not one blended success counter. Track accepted mutations, independently observed records, and mail-policy observations as separate events. An illustrative SLO might require 99.9% of valid onboarding requests to become independently observable within the platform's declared propagation window, but the target and window must come from the platform's actual dependency behavior and tenant promise, not from this example. Validation rejections belong in a request-quality indicator; they should not be hidden by retries until they age into latency failures.
Record structured error classes rather than full request dumps:
-
invalid_zone_identityfor an absent ID or a domain placed in the ID field -
owner_outside_zonewhen the requested name does not descend from the selected zone -
write_rejectedwhen the control plane rejects a structurally valid request -
observation_timeoutwhen acceptance is not followed by independent visibility within the declared window
Four labels are enough to route the first investigation without turning tenant names or record values into high-cardinality metrics.
Choose the ownership boundary deliberately
This failure often appears in a managed-versus-self-hosted discussion, but the important choice is who owns each mapping and who gets paged when it is wrong. Buying a control plane does not remove the application's obligation to distinguish zone identity from DNS names. Building one does not make that mapping free.
| Decision area | Managed control plane | Self-hosted control plane |
|---|---|---|
| Zone identity | Consume and persist the service-issued ID | Define, store, and migrate an internal identity |
| Validation | Adapt to the external request contract | Specify and enforce the contract in every writer |
| On-call load | Diagnose integration, permission, and publication boundaries | Also operate storage, reconciliation, upgrades, and availability |
| Lock-in | Concentrated in identifiers and mutation semantics | Concentrated in the team's data model and operational tooling |
| Deliverability evidence | Build independent observation around accepted writes | Build both the mutation system and independent observation |
The decision rule is blunt: choose the boundary your team can support through an incident, then keep a small internal interface so tenant onboarding does not pass loosely named strings into that boundary. Cost belongs in the review, but expected page volume, recovery ownership, migration difficulty, and evidence quality are stronger primary axes. A lower invoice cannot compensate for a control path nobody can debug under an SLO clock.
This pattern has limits. A typed adapter is not suitable as the source of truth for delegated zones, and a small team should not self-host a control plane merely to avoid an external identifier; in that case, use a managed boundary and retain independent verification. A platform with strict data-residency requirements or unusual reconciliation semantics may reasonably build the control plane instead, accepting the additional on-call and migration burden. The trade-off is operational ownership, not syntax.
Verify, canary, and roll back the mapping
Start with one synthetic tenant whose expected zone and owner are known. Resolve the zone, construct the typed change, write it, and verify it through an independent resolver. Next, canary a small tenant cohort and compare all four stages: request accepted, mutation accepted, DNS observed, and any required mail-policy record observed. Only then expand the cohort.
Rollback should disable new mutations first. Preserve the mapping and evidence from completed writes so the reconciler does not oscillate, then restore the last known-good zone-resolution configuration or worker release. Deleting records as an automatic rollback is risky because a published tenant name may already be in use; deletion needs an explicit lifecycle decision, not a deployment reflex.
Before reopening the gate, replay the failed input through request construction without issuing a mutation. Confirm that the zone ID came from discovery or stored zone state, that the zone name remains available for containment checks, and that the record owner is below that zone. Then allow the reconciler to converge. Do not manually edit one tenant and declare the pipeline repaired; that creates evidence for an exception while leaving the faulty mapping in place.
The durable fix is a contract: opaque identifiers select control-plane resources, DNS names describe DNS data, and independent observation proves publication. Keep those roles visible in types, logs, alerts, and rollback state, and a zone-validation rejection becomes a bounded configuration defect instead of an open-ended DNS incident.
References
- RFC 7489, Domain-based Message Authentication, Reporting, and Conformance (DMARC): https://datatracker.ietf.org/doc/html/rfc7489
Top comments (0)