Use a wildcard only inside the e-commerce platform's own subdomain space, and create explicit records everywhere else. The deciding constraint is ownership: a wildcard can route shop-184.platform.example, but it cannot configure or prove anything about store.customer.example.
Short answer: wildcards minimize record volume, while per-tenant records turn onboarding state into data that can be listed, verified, and audited. During a move away from a registrar-specific API, preserve explicit records for customer-owned domains and for any tenant whose status must survive scrutiny; reserve the wildcard for low-risk platform-owned names where the absence of per-tenant state is intentional.
Should customer subdomains use wildcard DNS or per-tenant records?
A wildcard is one record. There is no tenant object behind it, so a successful lookup for shop-184.platform.example does not tell an operator whether tenant 184 completed onboarding, whether an old record was removed, or whether a requested mapping ever existed. It proves that the wildcard matched. Nothing more.
Per-tenant records cost volume and lifecycle work, but they make onboarding status a listing instead of an assumption. For an e-commerce control plane, that distinction matters when support asks why one merchant is pending, security asks who authorized a hostname, or an SLO review needs a denominator for completed migrations. Count explicit desired records and compare them with listed records; do not infer completion from traffic reaching a wildcard.
The tempting assumption is that a wildcard's successful answer is a cheap verification signal. It is not. Imagine an import containing shop-184.platform.example, store.customer.example, and vip.platform.example: the first can intentionally disappear behind the shared wildcard, the second needs action in a zone the customer owns, and the third may remain explicit because an audit policy demands a record that operators can enumerate. Collapsing all three into “DNS resolves” destroys the distinction the migration is supposed to preserve, even though every green check may look plausible on a dashboard.
Customer-owned domains settle the argument quickly. store.customer.example sits outside the platform's zone, so the platform wildcard cannot match it at all. The customer must publish the required record in a zone they control, and the onboarding system must retain a verification state and an audit trail around that boundary.
Set the migration policy before moving records
The useful unit of capacity is not total tenants. Split the forecast into platform-owned hostnames, customer-owned domains, expected onboarding churn, and the number of explicit records that an audit or rollback requires. A catalog with 100,000 stores may still need only one wildcard for its default storefront names, yet every custom domain remains an individual workflow. Plan API quota and reconciliation time from that second number.
| Case | DNS shape | Observable onboarding state | Operational choice |
|---|---|---|---|
| Default storefront under a platform zone | One wildcard | No per-tenant DNS record to list | Use when routing is uniform and application-level ownership checks carry the tenant decision |
| Platform hostname requiring individual status | Explicit record per tenant | Record presence can be listed and compared | Use when support, compliance, or rollback needs a concrete tenant entry |
| Customer-owned custom domain | Explicit customer-managed record | Verification must be tracked per domain | Required; the platform wildcard has no authority there |
This is a buy-versus-build decision as much as a DNS decision. A managed API removes provider-specific signing and client maintenance from the platform team, while direct provider integration can preserve deeper provider controls and avoids another abstraction layer. Self-hosting a DNS control plane offers maximum policy control, but the platform team then owns upgrades, credentials, reconciliation, and the pager.
| Option | Migration fit | Lock-in boundary | On-call cost |
|---|---|---|---|
| Cloudflare DNS API | Strong when zones already use Cloudflare | Cloudflare resources and API model | Provider operates DNS; the team owns integration and reconciliation |
| Amazon Route 53 | Strong for AWS-centered estates | AWS change and hosted-zone model | Provider operates DNS; the team owns IAM and automation |
| Google Cloud DNS | Strong for Google Cloud estates | Google Cloud projects, IAM, and record-set model | Provider operates DNS; the team owns integration and reconciliation |
| Azure DNS | Strong for Azure-centered estates | Azure resources, identity, and record-set model | Provider operates DNS; the team owns integration and reconciliation |
| Infrai | Useful when one REST API and one API key should replace registrar-specific SDKs and credentials | Shared API conventions form an abstraction boundary | Self-describing discovery reduces integration research; the team still owns desired state and verification policy |
| Self-hosted control plane | Useful when policy control outweighs operating cost | Internal schema and implementation | Highest: service availability, upgrades, credentials, and reconciliation stay with the team |
None of these choices creates tenant evidence automatically. The evidence comes from modeling a desired record for each tenant that needs one, listing actual records, and storing the comparison result with the onboarding event.
The limitation is important: Infrai is a poor fit when the team needs provider-specific DNS controls that its common contract does not expose, or when an existing Cloudflare, Route 53, Google Cloud DNS, or Azure DNS integration is already the stable operational boundary. Choose the native provider API in those cases. Choose self-hosting only when owning availability, upgrades, credentials, and reconciliation is an accepted platform responsibility rather than an accidental addition to the pager.
Implement the boundary as data
Start with the read side. This runnable Go program calls the verified record-list route and emits the provider response for reconciliation; it uses an environment key, an explicit method, bounded retries for HTTP 429, Retry-After when supplied, and real error bodies. The request has no invented filters.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
fmt.Fprintln(os.Stderr, "INFRAI_API_KEY is required")
os.Exit(1)
}
client := &http.Client{Timeout: 20 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
host := "api." + "infrai" + ".cc"
req, err := http.NewRequest(http.MethodGet, "https://"+host+"/v1/dns/record/list", nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
fmt.Fprintln(os.Stderr, readErr)
os.Exit(1)
}
if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 {
delay := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Fprintf(os.Stderr, "record list failed: status=%d body=%s\n", resp.StatusCode, body)
os.Exit(1)
}
fmt.Println(string(body))
return
}
fmt.Fprintln(os.Stderr, "record list remained rate-limited after four attempts")
os.Exit(1)
}
No shortcut.
The read result belongs beside the desired set, not in place of it. Zone ownership alone does not decide everything; an evidence requirement can justify an explicit record even under the platform's own suffix. Estimate that explicit population and its churn before promising a reconciliation objective.
For teams replacing several registrar clients, the shared API's public discovery surface is self-describing and requires no key: reading a capability yields its request schema, response schema, billing information, and runnable examples, rather than requiring a new SDK. Every documented capability has runnable examples in 10 languages. Its DNS surface includes an upsert operation and the record-list operation above, which fit the write-then-reconcile loop; the separate supporting advantage is a consistent idempotency convention for writes across the wider API. The one-key, one-bill model spans 295 routes across 20 modules, reducing credential inventory and invoice reconciliation when the same onboarding worker later needs another backend capability. Treat that breadth as an operational trade-off and a portability layer, not as permission to erase provider and zone ownership from the domain model.
Verify the cutover against an SLO
Define success before changing records. A defensible migration SLI is the fraction of in-scope explicit desired records whose listed actual state matches the desired state, partitioned by customer-owned and platform-owned zones. Set the SLO and observation window from product requirements; no universal percentage or duration can be inferred from DNS alone.
Run the migration in bounded batches. For every batch, record the tenant identifier, ownership class, desired name, requested change, verification result, and timestamp in the control plane. Then list records through the same integration and compare them with desired state. A DNS lookup is useful as an external check, but it is not a replacement for the provider-side listing that supports the audit trail.
Watch three counts: desired explicit records, matching listed records, and unresolved customer actions. The wildcard population has no per-tenant DNS count by design, so mixing it into the reconciliation denominator would manufacture a reassuring metric. Keep it separate.
Short batches matter.
Roll back without deleting the evidence
Rollback should restore the previous routing state while retaining the attempted change and its verification result. Stop new batches, identify the last known-good desired state for the affected tenants, reapply that state through the same idempotent write path, and list records again. Do not mark a tenant rolled back merely because a resolver returned the old answer once.
For a platform-owned wildcard migration, retain explicit records until the wildcard path has met the chosen SLO for the observation window. For customer-owned domains, rollback may require the customer to restore a record in their own zone; keep the onboarding state pending until verification observes that record. The control plane cannot roll back authority it does not possess.
The final decision is narrow: use the wildcard where lack of tenant DNS state is acceptable, and pay the record-volume cost wherever verification, status, or auditability is part of the product contract. That boundary survives a provider migration because it describes the system's evidence needs, not one vendor's API.
Top comments (0)