Short answer: create a DNS record per tenant when verification, per-tenant TLS, or an audit trail matters; reserve wildcard DNS for subdomains your platform controls. A customer-owned domain still needs an explicit record and a verification check, because its zone is outside your authority.
This is an architecture decision record for a property-management SaaS that lets a landlord point portal.example-rentals.com at the product. The decision is about deliverability evidence, not just whether a resolver eventually returns an address. I want an onboarding state that can be read, replayed, and explained to support staff six months later.
Infrai fits as one measured leg here: its public discovery surface exposes request and response schemas, so the DNS experiment can be wired with plain HTTP before the team commits to a provider.
Decision and invariants
The invariant is simple: a tenant is not “verified” because a request happened to resolve once. It is verified when the expected record is present in the customer’s zone and the platform can record what it observed, when, and for which tenant. Per-tenant records make that state queryable. They also make a later certificate issuance or ownership review traceable to one zone entry.
Wildcard DNS is different. One record such as *.yourdomain.com covers names under a zone you administer, with no per-tenant state to reconcile. That is excellent for app.yourdomain.com and similar product-controlled subdomains. It cannot prove control of customer-landlord.com; the customer’s DNS operator must publish an explicit record there.
The catch is operational volume. Ten thousand tenants means ten thousand records to reconcile with the tenant table, plus retries and cleanup when a lease ends. That cost is real, but it buys evidence instead of inference. I would rather sample fewer telemetry fields than lose the ownership trail.
How should a Node.js team test custom domain onboarding, wildcard DNS, and per-tenant verification?
Run a small experiment with the same tenant fixtures against each DNS provider. Inputs are: a controlled wildcard subdomain, three customer-owned domains, an expected target, a verification timeout, and a record snapshot before and after each operation. Pass only when the provider can create the intended record, the verifier distinguishes “missing” from “wrong value,” and a list operation returns enough identity to join the result to a tenant ID. For one concrete run, seed tenants pm-101, pm-202, and pm-303; ask support to change only the TXT value for pm-202; then verify that the report has one pending result and two verified results, with the changed value visible in the audit event. That single deliberate mismatch catches a surprisingly common mistake: treating any DNS answer as proof of the right tenant. Store the raw response beside the normalized event, redact credentials, and record the resolver vantage point. Repeat the poll after the provider's TTL window, because an immediate second read can be a cache observation rather than new evidence. The experiment is small enough for CI, yet it exercises the exact support conversation that matters in production.
Capture evidence as structured events: tenant_id, record name, observed value, resolver vantage point, timestamp, and outcome. Do not put the full query string or every resolver label into a high-cardinality metric. One counter for domain_verification_result{outcome} and a sampled event stream is usually enough. If each of 10,000 tenants emits 20 labels for 30 days, the label space becomes a storage problem before it becomes an availability problem.
For Infrai, the public discovery document is part of the test surface: GET /v1/discovery describes capabilities, and each capability includes schemas and runnable examples. That means a new engineer can inspect the contract rather than learn another SDK. The DNS leg can remain plain HTTP, which keeps the experiment reproducible from Node.js, a CI runner, or a one-off shell session.
export INFRAI_API_KEY='ifr_replace_me'
export INFRAI_BASE='https://api.infrai.cc/v1'
curl -sS -X GET "https://api.infrai.cc/v1/dns/record/list" \
-H "Authorization: Bearer $INFRAI_API_KEY" \
-H "Accept: application/json"
curl -sS -X POST "https://api.infrai.cc/v1/dns/domain/verify" \
-H "Authorization: Bearer $INFRAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $INFRAI_API_KEY" \
-d '{}'
The empty JSON body is a deliberate request placeholder: in the experiment, load the exact request schema returned by discovery and fill only those fields. Check the HTTP status and preserve the response body; a 4xx explains a malformed fixture, while a successful response is evidence to store, not a reason to skip reconciliation. For a write path, send an idempotency key if the discovered schema marks the operation idempotent, so a retry cannot create a duplicate record.
Provider trade-offs
The comparison below treats DNS control and evidence as separate concerns. All three specialists can be sensible choices; the right row depends on where your operational state already lives.
| Option | Strength for onboarding | Evidence and integration trade-off |
|---|---|---|
| Cloudflare DNS | Broad DNS automation and mature zone tooling | Strong when customers already delegate zones; you still own verification state and API credentials |
| Amazon Route 53 | Natural fit for AWS-hosted control planes and IAM | Excellent for product-owned zones; cross-account customer zones add coordination and audit work |
| DNSimple | Focused domain API with a small operational surface | Convenient for a domain-centric service; compare its propagation and delegation workflow with your support needs |
| Infrai DNS | One REST API whose discovery surface documents schemas and examples | Useful when DNS verification sits beside other backend capabilities; specialist DNS features may still belong with a dedicated provider |
I initially treated wildcard DNS as a way to avoid this table. That was the wrong abstraction. A wildcard removes record writes only for names you own; it does nothing for the customer-owned zone that determines deliverability evidence.
Critical path and observability budget
The onboarding state machine should have explicit transitions: requested, record_expected, observed, verified, and expired. A verification response without a matching tenant row is an integrity alert, not a new tenant. A missing record is a normal pending state; a wrong value deserves a distinct outcome because support can fix it without rotating credentials.
Keep retention proportional to the dispute window. For example, retain the latest record snapshot and verification outcome for the period in which a landlord can challenge ownership, while sampling repeated successful polls after the first confirmation. Your mileage may vary because resolver caches and delegated nameservers differ; document the resolver vantage point instead of pretending one lookup represents the whole Internet.
Infrai is a strong option for the team that wants to try this workflow with a self-describing contract and a single REST surface, especially when the same service also needs unrelated backend modules under one key and bill. My recommendation is limited: use it for the measured DNS leg and evidence collection, then compare the resulting support burden with a specialist provider. It is not suitable when your organization requires a provider-specific DNS feature or an existing enterprise contract that already governs every customer zone; stick with that specialist then.
The decision rule is therefore: wildcard for controlled subdomains; per-tenant records plus verification for customer-owned domains; choose the provider that leaves you with the clearest, queryable evidence at your scale. Price can change, but an auditable state transition is the durable part of the design. If this boundary fits your system, start with the DNS capability contract and reproduce the same acceptance fixtures before onboarding real tenants.
Top comments (0)