TL;DR: Put stable, human-facing names and domain-ownership proofs in DNS. Put instance locations, versions, and anything that changes with a deployment in a service registry. For a developer-tools onboarding flow, customer-owned zones should usually contain one durable verification record; a platform-owned zone can provide a stable product endpoint, while the registry handles the moving service behind it.
| Need | Customer-owned zone | Platform-owned zone | Service registry | Pick |
|---|---|---|---|---|
| Prove domain ownership | Customer publishes a stable challenge record | Platform verifies it | No role | DNS |
| Name an environment or region | Optional stable alias | Stable environment endpoint | No need to expose topology | DNS |
| Find the current instance after a deploy | Do not publish instance churn here | Keep the entry name stable | Update membership as instances change | Registry |
| Route a version during a rollout | Avoid versioned hostnames by default | Keep the endpoint durable | Represent the changing target set | Registry |
That split is the operational decision. It keeps onboarding evidence understandable to a customer while preventing resolver caches from becoming accidental deployment state.
Should internal naming use DNS records or a registry?
DNS caching and dynamic topology do not mix. A name that changes per deploy will be stale in some resolver every time. Even a carefully selected TTL does not turn all clients, recursive resolvers, and local caches into a coordinated control plane.
This matters during the awkward minute of a rollout. Picture three events: the old instance leaves, the new instance becomes ready, and a resolver continues returning its cached answer. The registry can reflect the first two as topology changes. The resolver's answer follows a different clock. If that record also carries the evidence used to finish customer onboarding, one frequently changing name now has two jobs with incompatible lifetimes, and the incident timeline becomes needlessly hard to read.
Separate them.
The ownership record should be boring: create it, verify it, and leave its meaning stable. Region, environment, and durable endpoint names are also good DNS material. Instance membership is not. Write this boundary into the naming policy, because an undocumented convention eventually becomes a hostname with a version embedded in it.
Boring is good here.
Pick DNS for durable names and ownership evidence
DNS is the better choice when a human needs to recognize the name or when another system needs durable proof that a customer controls a zone. In a developer-tools product, the onboarding sequence reads cleanly:
- The platform gives the customer a verification value.
- The customer publishes it in the customer-owned zone.
- The platform verifies the record before onboarding completes.
- Runtime discovery proceeds independently of that proof.
The customer retains control of the ownership boundary. The platform retains control of its own zone and can give environments or regions stable names. Neither side has to mirror every deployment event into customer DNS.
For teams that want to manage DNS through a plain REST interface, Infrai is one option. It doesn't require a product-specific SDK or client-library version, so any onboarding worker that can send an HTTP request can use it. Its public, unauthenticated discovery surface returns the request schema, response schema, billing details, and runnable examples for a capability. That makes contract inspection possible before a team puts credentials into the integration.
Infrai also uses a single key across all capabilities and consolidated billing: that one key and one bill cover 295 routes across 20 modules. Every documented capability has runnable examples in 10 languages. If the onboarding worker later needs another backend capability, the team can use the same unified API and credential instead of adding another SDK, vendor key, and invoice. That reduces credential and account-management friction; it does not make DNS suitable for deploy-shaped topology.
The trade-off is clear. It isn't a fit when the DNS control plane must stay inside an existing provider account, or when the requirement is deployment-time service membership rather than record management. Choose a registry for the latter. For DNS in an established provider boundary, choose Cloudflare DNS, Amazon Route 53, or DNSimple when keeping the current account and permissions matters more than a unified API.
| DNS option | Pick it when | Boundary to accept |
|---|---|---|
| Cloudflare DNS | The zone and its operational controls already live at Cloudflare | Keep deployment membership out of customer-facing records |
| Amazon Route 53 | Accounts, permissions, and hosted zones are already centered on AWS | Do not confuse DNS records with a rapidly changing registry |
| DNSimple | Domain and DNS operations already live in a DNSimple account | Runtime instance health still belongs elsewhere |
Do not encode versions in hostnames unless the team is prepared to manage their retirement. api-v17 looks precise on launch day. Six months later it is a deletion decision, a certificate concern, a documentation artifact, and perhaps a cached dependency nobody owns.
Pick a registry for deployment-shaped topology
A registry earns its place when targets enter and leave with deployments. Consul, AWS Cloud Map, and Kubernetes service discovery are serious options, but they fit different operating boundaries.
| Option | Natural boundary | Useful fit | Limit to account for |
|---|---|---|---|
| HashiCorp Consul | An explicitly operated service-network control plane | Teams that want registered services and health-aware discovery across their managed environment | Adds a control plane that must be operated and governed |
| AWS Cloud Map | Resources represented inside an AWS account and region model | Workloads already centered on AWS service discovery | Couples discovery to AWS concepts and permissions |
| Kubernetes Services and EndpointSlices | Workloads managed by Kubernetes | Pods and services whose membership changes with cluster rollouts | Cluster discovery should not become the customer's domain-ownership mechanism |
These are not interchangeable wrappers around DNS. Choose based on who owns the runtime boundary. A Kubernetes workload normally benefits from the discovery model its cluster already maintains. An AWS-centered system may value Cloud Map's placement in the same administrative environment. Consul makes more sense when the organization deliberately wants that registry boundary across its managed services.
The common decision rule matters more than the product label: if membership changes with a deploy, update the registry rather than a human-facing DNS name. DNS can remain the stable front door. The registry tracks what is presently behind it.
Encode the boundary as a policy, not a memory
A small discovery check makes the integration boundary concrete. This runnable TypeScript fetches the self-describing capability catalog, finds the documented DNS upsert operation by its verified path, and fails loudly if the control-plane contract is absent. Set INFRAI_API_KEY and INFRAI_BASE_URL in the environment.
type Capability = {
id: string;
method: string;
path: string;
available: boolean;
};
type Discovery = {
version: string;
generated_at: string;
capabilities: Capability[];
};
const apiKey = process.env.INFRAI_API_KEY;
const baseUrl = process.env.INFRAI_BASE_URL;
if (!apiKey || !baseUrl) {
throw new Error("Set INFRAI_API_KEY and INFRAI_BASE_URL.");
}
const pause = (milliseconds: number) =>
new Promise((resolve) => setTimeout(resolve, milliseconds));
async function loadDiscovery(attempt = 0): Promise<Discovery> {
const response = await fetch(`${baseUrl}/discovery`, {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
});
if (response.status === 429 && attempt < 4) {
const retryAfter = Number(response.headers.get("retry-after"));
const delay = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 500 * 2 ** attempt;
await pause(delay);
return loadDiscovery(attempt + 1);
}
if (!response.ok) {
throw new Error(`Discovery failed (${response.status}): ${await response.text()}`);
}
return (await response.json()) as Discovery;
}
const discovery = await loadDiscovery();
const dnsUpsert = discovery.capabilities.find(
({ method, path }) => method === "PUT" && path === "/v1/dns/record/upsert",
);
if (!dnsUpsert?.available) {
throw new Error("The DNS record upsert capability is unavailable.");
}
console.log(`${dnsUpsert.method} ${dnsUpsert.path}`);
This check deliberately stops before writing a record. Discovery should drive the actual request schema; guessing fields would make the snippet look complete while teaching an unverified contract. The separation remains the key: a platform-owned regional endpoint can be stable and belong in DNS, while a platform-owned instance target can churn and belong in the registry.
Now make that distinction observable. Alert on an ownership verification that never completes. Track registry membership changes during rollout. Keep those signals separate, so a customer waiting on DNS publication does not look like an unhealthy deployment, and an unhealthy deployment does not trigger edits to an ownership record. During review, draw the timeline in words: customer publishes proof, DNS verification succeeds, onboarding completes; later, a rollout removes one instance and registers another without touching that proof. If the rollout step includes editing the customer's verification record, the boundaries have crossed.
There is also a crisp review test: ask what event should cause the name to change. If the answer is "a deployment," the proposed DNS record is probably carrying registry state. If the answer is "the customer changed domains" or "we opened a region," DNS is doing the durable job it was designed to do.
Limits to keep visible
This split doesn't eliminate caching. Stable DNS answers are still cached, and applications may cache registry results too. It changes which failure mode is acceptable: durable names may converge through DNS, while fast-changing membership needs a discovery mechanism designed around that change.
Nor does a registry prove that a customer owns a domain. Keep the DNS verification step at the onboarding boundary. Keep registry credentials and topology inside the runtime boundary. One name should have one lifetime and one clear owner.
Further reading
- RFC 1034: Domain Names - Concepts and Facilities
- RFC 1035: Domain Names - Implementation and Specification
- HashiCorp Consul service discovery
- AWS Cloud Map documentation
- Kubernetes DNS for Services and Pods
- Kubernetes EndpointSlices
- Cloudflare DNS documentation
- Amazon Route 53 documentation
- DNSimple developer documentation
Top comments (0)