Skip to main content
DNS hosts authoritative zones for your domains. Every zone is signed with DNSSEC from the moment it exists, records can fail over on a health check, and a zone can be made private so it answers only inside your VPCs. The service is global — one endpoint, dns.basaltic.sh, no region segment. The public namespace is not regional.

Create and delegate

Proving you own the domain, and the two ways to do it.

Records

RRsets, the values array, and the one rule that breaks zones silently.

Health checks

Priority tiers, what gets answered, and what served tells you.

Geo routing

Answer variants scoped to client prefixes, and how they stay DNSSEC-valid.

DNSSEC

Always on. What to paste at your registrar to close the chain.

Creating a zone

The SOA and apex NS records are generated for you against the platform nameserver list, and the zone is queryable on success. The response carries a nameservers array — that is what you copy into your registrar to delegate the domain.
A new zone with no zone above it on the platform does not resolve until you prove you own the domain. Creating it is not claiming it.

Proving ownership

The proof exists so nobody can create a zone for a subdomain of a domain they do not own and sit inside your subtree. Which method you use depends on where the domain points right now.
The zone’s ownership block names a TXT record. Publish it at your current provider — before you delegate anything — then call verify:
The call is idempotent, so you can poll it.
The proof is a lease, not a deed. It is re-confirmed periodically for as long as the zone is served, so a domain that lapses or changes hands stops being served from here.

When re-confirmation starts failing

A zone whose periodic re-proof is failing grows an ownership.recheck_deadline — the instant it stops answering unless the check passes again. Absent means healthy. Reaching that date takes sustained failure, not one bad afternoon: every pass re-checks a failing zone, so a transient resolver problem clears itself. Your organization’s owner is emailed when the deadline is set, and again if the zone does stop resolving — a zone is never taken off the air before that message has gone out. To clear it deliberately, republish the challenge (or point the delegation back at our nameservers) and call verify-ownership again. That is the one case where an already-verified zone can answer 400, because there the call runs the check for real.

Records

A record is an RRset: one name, one type, and a values array. Updating a record replaces the value list wholesale.
A value can be disabled: true — retained but not served, which is useful for staging a change before it goes live.
A CNAME cannot share a name with any other record, and cannot sit at the zone apex. The API refuses both, because the breakage is silent otherwise.The signer stamps each name’s NSEC with a bitmap of the types that name holds. A CNAME beside an A yields a bitmap reading “CNAME and A”, and a validating resolver treats that owner as bogus and answers SERVFAIL. The zone loads, the records look correct in the API, and the name simply stops resolving for anyone who validates. This is RFC 1034 §3.6.2 and RFC 2181 §10.1.At the apex the conflict is unavoidable — the SOA and NS sets are always there — so point the apex at addresses with A/AAAA records.

Platform-managed records

Records with managed: true are read-only: the SOA, the apex NS set, and the whole DNSSEC family. They appear in reads so you can see the zone as it really is, but they cannot be created, updated or deleted through the API.

Importing a zone file

POST /v1/zones/{zone_id}/import takes an RFC 1035 master file — the BIND format every provider exports. Send it as JSON in zone_file, or as the raw body under any other content type, so this works directly:
An RRset in the file replaces the RRset at the same name and type wholesale; an RRset the file does not mention is left exactly as it is.So an import cannot delete a record, and importing the same file twice is a no-op rather than an error — which is what makes it safe to retry.
The whole file is validated before a single record is written: every record faces the same type, name, TTL and rdata checks that creating one does, the same CNAME rule, and the same record quota. One bad record refuses the file and the zone is untouched.
The SOA and apex NS set are the platform’s — the zone is served by our nameservers, and the apex NS is what the parent delegates to. The DNSSEC records (RRSIG, DNSKEY, DS, NSEC, NSEC3, NSEC3PARAM, CDS, CDNSKEY) are generated by our signer from keys your file cannot know. Records placed outside the zone are skipped too.Every one comes back in skipped with a reason. A file exported from a signed zone carries all of them, so refusing the file over them would make export/import unusable.
The file is capped at 1 MiB. On success the SOA serial advances once for the whole import.

Health-checked failover

Attach a check to an A or AAAA record and the nameservers stop answering with targets that are down.
Lower priority wins. Values sharing a priority form one tier and are answered together, so 0 and 1 is a failover pair while 0 and 0 is active/active. The nameservers answer with the healthy targets at the most-preferred tier, fall through to the next tier when those go unhealthy, and — if nothing is healthy anywhere — answer with every configured value rather than nothing.
GET on the health check returns a served array: the values the nameservers are answering with right now. The record’s own values stay whatever you configured. The difference between the two is the failover, so it is reported rather than left for you to infer.
A UDP probe cannot tell a healthy target from a silent one. Only http, https and tcp are offered.
The probe dials an address, not a name, so there is no name to validate against. It measures whether the target is serving, not who it is.
The prober runs in the control plane and could never reach an address inside a tenant VPC, so a check on an RFC 1918 or unique-local address is rejected rather than accepted and left permanently unhealthy.
A target no probe has reached a verdict on is initial, and initial is answered. A freshly created record resolves immediately instead of being dark until the first probe lands.
Every health transition re-signs the whole zone. healthy_threshold and unhealthy_threshold (both default 3) decide how many consecutive results it takes to move, and timeout_sec must stay below interval_sec or a slow target’s probes overlap each other.

Geo routing

A record can carry named answer variants, each scoped to a set of client prefixes, so a query is answered with the values closest to whoever asked.
The record’s own values stay the default — what a query matching no prefix is answered with, which is most of the internet. So answers describes overrides, never the fallback; the fallback cannot be deleted, and default is a reserved label. PUT replaces the whole configuration, and GET reports the default alongside the variants rather than leaving it implicit.
Only A, AAAA and CNAME records can carry variants — those are what “send this client somewhere closer” means. A variant only changes rdata, so nothing structural stops MX or SRV, but each brings its own question and none is what this is for.

How a client is matched

Selection is longest-prefix match, so overlapping prefixes of different lengths are meaningful rather than a conflict — a /8 and a /16 inside it do what you would expect. The same prefix on two variants is rejected, because that would make the answer depend on row order and let two nameservers disagree about one query. Matching uses the EDNS Client Subnet prefix when the resolver sends one, and the query’s source address otherwise. A rule more specific than what the resolver actually asserted is skipped, not matched: beyond the ECS source netmask the address is zeros the resolver never claimed, and matching them would route a whole /8 to whichever /24 happens to sit at its base. Write prefixes as the network address185.0.0.0/8, not 185.1.2.3/8.
Prefixes cannot be longer than /24 for IPv4 or /56 for IPv6. That is the ECS privacy convention, and it is also what keeps resolver caches correct: the scope length echoed back is the record’s longest configured prefix, so an unbounded prefix would force per-host caching on every resolver that talks to us.

Why the limits are what they are

Each variant is signed separately, so a record with N variants is N+1 RRsets to mint on every re-sign of the zone — and a health flip or a record edit re-signs the whole zone.
The prefix list is scanned per query, on an unauthenticated UDP packet. It is deliberately a short linear scan over a list sorted longest-first at load, rather than anything that grows with what was configured.
Nothing routes to it, and it would cost a signature on every re-sign to never be answered. Refusing it is more useful than carrying it silently.
A variant is still a CNAME RRset, and RFC 1034 §3.6.2 allows a CNAME RRset exactly one value. The constraint is on the answer, not on the row that configured it.
Geo answers stay DNSSEC-valid. Each variant is signed at write time and a response carries exactly one variant’s RRset together with that variant’s signature. Filtering one signed RRset per query — the obvious implementation — would answer with data the shipped signature does not cover, which is SERVFAIL for every validating resolver. Because a variant changes only rdata and never which names or types exist, the NSEC denial chain is identical whichever variant is served.

Private zones

A zone is public or private, fixed at creation.
A private zone answers only inside the VPCs associated with it, and needs at least one at creation. Attach or detach more later through the vpc-associations endpoints.
The two mismatches are rejected rather than quietly corrected: a private zone with no vpc_ids, and a public zone that carries them, both answer 400.

DNSSEC

DNSSEC is on for every zone created through this API — there is no switch, and no unsigned mode. The signer bootstraps the zone and the dnssec block appears on it, carrying the key tags, the algorithm (13, ECDSA P-256 SHA-256), and the DS records. To complete the chain of trust, paste the DS record at your parent registrar:
1

Read the DS records off the zone

dnssec.ds_records holds them. Each also carries rdata — the zone-file form of the same record, for registrars that want a single string.
2

Publish it at the registrar

This is the one DNSSEC step that cannot be done from here: only the parent zone’s operator can publish a DS record, and for a public suffix that is your registrar.
3

Validate

Once the DS is live, resolvers can validate the chain from the root down to your records.
Until the DS record is published at the registrar, your zone is signed but not validated — resolvers have no way to reach your keys from the root. Signing without a DS buys nothing.

Limits and behaviour

1 MiB
Larger files are refused.
quota-enforced
Checked on both single-record creation and import, against your organization’s quota.
default 3600
Minimum 0.
cascades
Deletes every record it owns, in a single cascading delete.

Troubleshooting

Check ownership.verified. A zone with no zone above it on the platform does not answer until ownership is proved — creating it is not claiming it. See proving ownership for which of the two methods applies to you.
That pattern — works for non-validating resolvers, fails for validating ones — is the signature of a CNAME sharing a name with another record. The API refuses new ones, but a zone imported or migrated in unusual ways is worth checking. List the records at that name and make sure the CNAME is alone.
Almost always a missing or stale DS record at the registrar. Compare what the registrar publishes against dnssec.ds_records on the zone — a DS left over from a previous provider breaks validation exactly as thoroughly as none at all.
GET the health check and read served against the targets’ status. A target still in initial has no verdict yet and is answered on purpose. Remember thresholds default to three consecutive results, so a flapping target takes three intervals to move in either direction.
Most resolvers do not send EDNS Client Subnet, so selection falls back to the resolver’s own source address — a client using a distant public resolver is matched where that resolver sits, not where the client is. Check GET on the geo routing: default is the answer the bulk of the internet receives, and it is the one to get right.Also confirm the prefix is written as a network address: 185.1.2.3/8 is not the same rule as 185.0.0.0/8.
Working as intended — import merges and never deletes. An RRset absent from the file is left alone. Delete records explicitly through the record endpoints.

Next

Certificates

A zone on our DNS gets its ACME challenge CNAME created automatically.

API reference

Every zone and record operation, with schemas.