Cloudflare.DNS reference
AccountDnsSettings
Section titled “AccountDnsSettings”Source:
src/Cloudflare/DNS/AccountSettings.ts
The DNS settings of a Cloudflare account
(/accounts/{account_id}/dns_settings) — the account-wide
enforceDnsOnly override and the default DNS settings applied to
every new zone (zoneDefaults).
The settings object is a per-account singleton — it always exists
with Cloudflare defaults, so this resource never creates or deletes
anything physical. Reconcile patches only the fields you declare
(and only when the observed value differs); destroy restores the
managed fields to the values they had before Alchemy first touched
the account (captured as initialSettings).
Some fields are plan-gated: zoneDefaults.nsTtl and custom SOA
values require the custom nameserver TTL / custom SOA entitlements,
foundationDns is a paid add-on, and internalDns is Enterprise
Internal DNS only.
AccountDnsSettings: Account-wide overrides
Section titled “AccountDnsSettings: Account-wide overrides”yield* Cloudflare.DNS.AccountDnsSettings("DnsSettings", { enforceDnsOnly: true,});AccountDnsSettings: Zone defaults
Section titled “AccountDnsSettings: Zone defaults”Flatten CNAMEs in every new zone
yield* Cloudflare.DNS.AccountDnsSettings("DnsSettings", { zoneDefaults: { flattenAllCnames: true },});Default new zones to multi-provider DNS
yield* Cloudflare.DNS.AccountDnsSettings("DnsSettings", { zoneDefaults: { multiProvider: true },});Dnssec
Section titled “Dnssec”Source:
src/Cloudflare/DNS/Dnssec.ts
DNSSEC configuration for a Cloudflare zone
(/zones/{zone_id}/dnssec).
DNSSEC is a per-zone singleton — it always exists in either an enabled or disabled state, so this resource never creates or deletes anything physical. Reconcile patches the configuration toward the desired state; destroy restores the state the zone had before Alchemy first managed it (enabled stays enabled, previously-disabled zones are deactivated again).
Activation is eventually consistent: after enabling, Cloudflare
reports pending until the ds attribute (the DS record) is
submitted at the domain’s registrar. The reconciler polls with
bounded retries for the zone to leave the disabled state but does
not wait for full active — that depends on the registrar.
Safety: when there is no prior state and DNSSEC is already enabled
on the zone, read reports it as Unowned and the engine refuses
to take it over unless --adopt (or adopt(true)) is set.
Dnssec: Enabling DNSSEC
Section titled “Dnssec: Enabling DNSSEC”Sign the zone
const dnssec = yield* Cloudflare.DNS.Dnssec("ZoneDnssec", { zoneId: zone.zoneId,});// Paste `dnssec.ds` at your registrar to complete activation.Multi-signer DNSSEC
yield* Cloudflare.DNS.Dnssec("ZoneDnssec", { zoneId: zone.zoneId, dnssecMultiSigner: true,});Dnssec: Disabling DNSSEC
Section titled “Dnssec: Disabling DNSSEC”yield* Cloudflare.DNS.Dnssec("ZoneDnssec", { zoneId: zone.zoneId, status: "disabled",});Firewall
Section titled “Firewall”Source:
src/Cloudflare/DNS/Firewall.ts
A Cloudflare DNS Firewall cluster.
DNS Firewall sits in front of your authoritative DNS infrastructure,
caching responses on Cloudflare’s anycast network and shielding the
upstream nameservers from attack traffic. Creating a cluster assigns a
set of Cloudflare anycast IPs (dnsFirewallIps) that you point NS glue
records at; queries hitting those IPs are answered from cache or
forwarded to your upstreamIps.
DNS Firewall is a paid add-on (typically Enterprise / contract). On
accounts without the entitlement, creation fails with the typed
DnsFirewallNotEntitled error (Cloudflare error code 10101).
All settings are mutable in place; only name (the cold-state recovery
identity) triggers a replacement.
Firewall: Creating a Cluster
Section titled “Firewall: Creating a Cluster”Basic cluster
const cluster = yield* Cloudflare.DNS.Firewall("dns-shield", { upstreamIps: ["192.0.2.1", "192.0.2.2"],});// Point NS glue records at the assigned anycast IPs:const ips = cluster.dnsFirewallIps;Tuned caching and attack mitigation
const cluster = yield* Cloudflare.DNS.Firewall("dns-shield", { upstreamIps: ["192.0.2.1"], minimumCacheTtl: 120, maximumCacheTtl: 3600, negativeCacheTtl: 300, ratelimit: 600, retries: 2, attackMitigation: { enabled: true, onlyWhenUpstreamUnhealthy: true, },});Firewall: Reverse DNS
Section titled “Firewall: Reverse DNS”const cluster = yield* Cloudflare.DNS.Firewall("dns-shield", { upstreamIps: ["192.0.2.1"], reverseDns: { "203.0.113.1": "ns1.example.com", },});ReadDns
Section titled “ReadDns”Source:
src/Cloudflare/DNS/ReadDns.ts
Binding that lets a Worker read Cloudflare DNS records at runtime.
Creates a least-privilege AccountApiToken with only the DNS Read
permission, scoped to the single zone passed to bind, and binds its value
into the Worker so runtime code can authenticate.
ReadDns: Reading DNS records at runtime
Section titled “ReadDns: Reading DNS records at runtime”Bind the client in the Worker’s Init phase and provide ReadDnsBinding.
The zone is fixed by ReadDnsBinding(zone) — the provisioned token only grants
access to that zone, so calls take no zoneId. Pass the Zone
resource directly (it’s an Effect), or yield* Zone for a resolved value.
import * as Cloudflare from "alchemy/Cloudflare";import * as Effect from "effect/Effect";import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
const Zone = Cloudflare.Zone.Zone("MyZone", { name: "example.com" });
export class ReadDnserWorker extends Cloudflare.Worker<ReadDnserWorker>()( "ReadDnserWorker", { main: import.meta.url }, Effect.gen(function* () { // Init phase — bind the read client scoped to the zone. const dns = yield* Cloudflare.DNS.ReadDns(Zone);
return { fetch: Effect.gen(function* () { const { result } = yield* dns.listDnsRecords({ type: "A" }); const record = yield* dns.getDnsRecord(result[0].id); return yield* HttpServerResponse.json({ id: record.id }); }), }; }).pipe(Effect.provide(Cloudflare.DNS.ReadDnsBinding)),) {}ReadWriteDns
Section titled “ReadWriteDns”Source:
src/Cloudflare/DNS/ReadWriteDns.ts
Binding that lets a Worker perform the full Cloudflare DNS record CRUD surface at runtime.
Creates a least-privilege AccountApiToken with both the DNS Read
and DNS Write permissions, scoped to the single zone passed to bind, and
binds its value into the Worker so runtime code can authenticate.
ReadWriteDns: Managing DNS records at runtime
Section titled “ReadWriteDns: Managing DNS records at runtime”Bind the client in the Worker’s Init phase and provide
ReadWriteDnsBinding. The zone is fixed by ReadWriteDnsBinding(zone) — the
provisioned token only grants access to that zone, so calls take no
zoneId. Pass the Zone resource directly (it’s an Effect), or
yield* Zone for a resolved value.
import * as Cloudflare from "alchemy/Cloudflare";import * as Effect from "effect/Effect";import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
const Zone = Cloudflare.Zone.Zone("MyZone", { name: "example.com" });
export class Worker extends Cloudflare.Worker<Worker>()( "Worker", { main: import.meta.url }, Effect.gen(function* () { // Init phase — bind the full CRUD client scoped to the zone. const dns = yield* Cloudflare.DNS.ReadWriteDns(Zone);
return { fetch: Effect.gen(function* () { const { result } = yield* dns.createDnsRecord({ type: "A", name: "app.example.com", content: "192.0.2.1", ttl: 1, }); const record = yield* dns.getDnsRecord(result.id); yield* dns.deleteDnsRecord(result.id); return yield* HttpServerResponse.json({ id: record.id }); }), }; }).pipe(Effect.provide(Cloudflare.DNS.ReadWriteDnsBinding)),) {}Record
Section titled “Record”Source:
src/Cloudflare/DNS/Record.ts
A single DNS record on a Cloudflare-managed zone.
Safety: when there is no prior state, read scans the zone for an
existing (name, type) match. DNS records carry no ownership markers
we can inspect, so an existing match is reported as Unowned and the
engine refuses to take it over unless --adopt (or adopt(true)) is
set. This protects hand-edited records (especially the apex A/AAAA
and email DKIM/SPF records that the dashboard often manages) from
being clobbered.
Several records may legitimately share (name, type) — MX fallbacks,
multiple TXT records, round-robin A records. When the scan finds more
than one candidate, the declared content (and priority) must match
exactly one record; a record with no exact match is treated as missing
(a new sibling record is created), and a still-ambiguous match fails
with an error listing the candidates. To adopt one record out of such
a set, declare its current content/priority verbatim first, then
change them in a follow-up deploy.
Record: Proxied CNAME pointing at a tunnel
Section titled “Record: Proxied CNAME pointing at a tunnel”yield* Cloudflare.DNS.Record("AdminCname", { zoneId: zone.zoneId, name: "cluster-admin.example.com", type: "CNAME", content: `${tunnel.tunnelId}.cfargotunnel.com`, proxied: true, comment: "research admin UI",});Record: Plain A record
Section titled “Record: Plain A record”yield* Cloudflare.DNS.Record("ApiA", { zoneId: zone.zoneId, name: "api.example.com", type: "A", content: "203.0.113.42", ttl: 300,});Record: Structured service binding records
Section titled “Record: Structured service binding records”SVCB record
yield* Cloudflare.DNS.Record("McpSvcb", { zoneId: zone.zoneId, name: "_mcp._agents.example.com", type: "SVCB", content: { priority: 1, target: "mcp.example.com.", value: 'mandatory="alpn,port" alpn="h2,h3" port="443"', },});HTTPS record
yield* Cloudflare.DNS.Record("WebsiteHttps", { zoneId: zone.zoneId, name: "example.com", type: "HTTPS", content: { priority: 1, target: ".", value: 'alpn="h2,h3"', },});Source:
src/Cloudflare/DNS/View.ts
An Internal DNS view (/accounts/{account_id}/dns_settings/views) —
a named set of internal zones that DNS queries can be resolved
against, for split-horizon / internal DNS setups.
Requires the Enterprise Internal DNS entitlement on the account
(creation fails with InternalDnsNotAvailable otherwise). Both
name and zones are mutable in place.
View: Creating a View
Section titled “View: Creating a View”View over internal zones
const view = yield* Cloudflare.DNS.View("Internal", { zones: [internalZone.zoneId],});View with an explicit name
const view = yield* Cloudflare.DNS.View("Internal", { name: "datacenter-east", zones: [zoneA.zoneId, zoneB.zoneId],});WriteDns
Section titled “WriteDns”Source:
src/Cloudflare/DNS/WriteDns.ts
Binding that lets a Worker create, update, and delete Cloudflare DNS records at runtime.
Creates a least-privilege AccountApiToken with only the DNS Write
permission, scoped to the single zone passed to bind, and binds its value
into the Worker so runtime code can authenticate.
WriteDns: Mutating DNS records at runtime
Section titled “WriteDns: Mutating DNS records at runtime”Create, update, and delete records from inside a Worker
Bind the client in the Worker’s Init phase and provide WriteDnsBinding.
The zone is fixed by WriteDnsBinding(zone) — the provisioned token only grants
access to that zone, so calls take no zoneId. Pass the Zone
resource directly (it’s an Effect), or yield* Zone for a resolved value.
import * as Cloudflare from "alchemy/Cloudflare";import * as Effect from "effect/Effect";import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
const Zone = Cloudflare.Zone.Zone("MyZone", { name: "example.com" });
export class WriteDnsrWorker extends Cloudflare.Worker<WriteDnsrWorker>()( "WriteDnsrWorker", { main: import.meta.url }, Effect.gen(function* () { // Init phase — bind the write client scoped to the zone. const dns = yield* Cloudflare.DNS.WriteDns(Zone);
return { fetch: Effect.gen(function* () { const { result } = yield* dns.createDnsRecord({ type: "A", name: "app.example.com", content: "192.0.2.1", ttl: 1, proxied: true, }); yield* dns.updateDnsRecord(result.id, { type: "A", name: "app.example.com", content: "192.0.2.2", ttl: 1, }); yield* dns.deleteDnsRecord(result.id); return yield* HttpServerResponse.json({ id: result.id }); }), }; }).pipe(Effect.provide(Cloudflare.DNS.WriteDnsBinding)),) {}Apply a batch of changes atomically
yield* dns.batchDnsRecords({ posts: [{ type: "A", name: "a.example.com", content: "192.0.2.1", ttl: 1 }], deletes: [{ id: oldRecordId }],});ZoneDnsSettings
Section titled “ZoneDnsSettings”Source:
src/Cloudflare/DNS/ZoneSettings.ts
The DNS settings of a Cloudflare zone
(/zones/{zone_id}/dns_settings) — nameserver assignment, NS TTL,
SOA components, CNAME flattening, multi-provider mode, and zone mode.
The settings object is a per-zone singleton — it always exists with
Cloudflare defaults, so this resource never creates or deletes
anything physical. Reconcile patches only the fields you declare
(and only when the observed value differs); destroy restores the
managed fields to the values they had before Alchemy first touched
the zone (captured as initialSettings).
Some fields are plan-gated: foundationDns is a paid add-on,
nameservers.type: "custom.*" requires account custom nameservers,
internalDns and secondaryOverrides are Enterprise features.
ZoneDnsSettings: Basic settings
Section titled “ZoneDnsSettings: Basic settings”Lower the NS record TTL
yield* Cloudflare.DNS.ZoneDnsSettings("DnsSettings", { zoneId: zone.zoneId, nsTtl: 3600,});Flatten every CNAME in the zone
yield* Cloudflare.DNS.ZoneDnsSettings("DnsSettings", { zoneId: zone.zoneId, flattenAllCnames: true,});ZoneDnsSettings: SOA tuning
Section titled “ZoneDnsSettings: SOA tuning”yield* Cloudflare.DNS.ZoneDnsSettings("DnsSettings", { zoneId: zone.zoneId, soa: { minTtl: 300 },});ZoneDnsSettings: Multi-provider DNS
Section titled “ZoneDnsSettings: Multi-provider DNS”yield* Cloudflare.DNS.ZoneDnsSettings("DnsSettings", { zoneId: zone.zoneId, multiProvider: true,});ZoneTransferAcl
Section titled “ZoneTransferAcl”Source:
src/Cloudflare/DNS/ZoneTransferAcl.ts
A Secondary DNS zone-transfer ACL
(/accounts/{account_id}/secondary_dns/acls) — an account-wide
IPv4/IPv6 range that may receive NOTIFYs for secondary zones and from
which Cloudflare accepts AXFR/IXFR requests for outgoing transfers.
Requires the Secondary DNS (zone transfer) entitlement on the
account. Both name and ipRange are mutable in place.
ZoneTransferAcl: Creating an ACL
Section titled “ZoneTransferAcl: Creating an ACL”Allow a primary nameserver range
const acl = yield* Cloudflare.DNS.ZoneTransferAcl("PrimaryNs", { ipRange: "192.0.2.48/28",});ACL with an explicit name
const acl = yield* Cloudflare.DNS.ZoneTransferAcl("PrimaryNs", { name: "primary-nameservers", ipRange: "2001:db8::/64",});ZoneTransferIncoming
Section titled “ZoneTransferIncoming”Source:
src/Cloudflare/DNS/ZoneTransferIncoming.ts
The incoming zone-transfer configuration of a secondary zone
(/zones/{zone_id}/secondary_dns/incoming) — links the zone to the
peers Cloudflare transfers it in from and
sets the auto-refresh interval.
Requires the Secondary DNS (zone transfer) entitlement, and the zone
must be created with type: "secondary". The configuration is a
per-zone singleton: zoneId is the identity (replacement on change),
everything else is mutable in place.
ZoneTransferIncoming: Configuring incoming transfers
Section titled “ZoneTransferIncoming: Configuring incoming transfers”const peer = yield* Cloudflare.DNS.ZoneTransferPeer("Primary", { ip: "192.0.2.53", port: 53,});yield* Cloudflare.DNS.ZoneTransferIncoming("Incoming", { zoneId: zone.zoneId, name: "example.com.", peers: [peer.peerId], autoRefreshSeconds: 86400,});ZoneTransferOutgoing
Section titled “ZoneTransferOutgoing”Source:
src/Cloudflare/DNS/ZoneTransferOutgoing.ts
The outgoing zone-transfer configuration of a primary zone
(/zones/{zone_id}/secondary_dns/outgoing) — links the zone to the
peers Cloudflare NOTIFYs and serves
AXFR/IXFR to, and toggles transfers on or off via the dedicated
enable/disable endpoints.
Requires the Secondary DNS (zone transfer) entitlement on the zone.
The configuration is a per-zone singleton: zoneId is the identity
(replacement on change), everything else is mutable in place.
ZoneTransferOutgoing: Configuring outgoing transfers
Section titled “ZoneTransferOutgoing: Configuring outgoing transfers”Serve a primary zone to an external secondary
const peer = yield* Cloudflare.DNS.ZoneTransferPeer("Secondary", { ip: "192.0.2.53", port: 53,});yield* Cloudflare.DNS.ZoneTransferOutgoing("Outgoing", { zoneId: zone.zoneId, name: "example.com.", peers: [peer.peerId],});Configure transfers but keep them disabled
yield* Cloudflare.DNS.ZoneTransferOutgoing("Outgoing", { zoneId: zone.zoneId, name: "example.com.", peers: [peer.peerId], enabled: false,});ZoneTransferPeer
Section titled “ZoneTransferPeer”Source:
src/Cloudflare/DNS/ZoneTransferPeer.ts
A Secondary DNS zone-transfer peer
(/accounts/{account_id}/secondary_dns/peers) — an external
nameserver Cloudflare exchanges zone transfers with. Link peers to a
zone via ZoneTransferIncoming (secondary zones) or
ZoneTransferOutgoing (primary zones).
Requires the Secondary DNS (zone transfer) entitlement on the
account. Cloudflare’s create API only accepts a name; the provider
follows up with an update when ip, port, tsigId, or
ixfrEnable are declared, all of which remain mutable in place.
ZoneTransferPeer: Creating a Peer
Section titled “ZoneTransferPeer: Creating a Peer”Primary nameserver to transfer from
const peer = yield* Cloudflare.DNS.ZoneTransferPeer("Primary", { ip: "192.0.2.53", port: 53,});Peer with TSIG authentication
const tsig = yield* Cloudflare.DNS.ZoneTransferTsig("TransferKey", { algo: "hmac-sha512.", secret: Redacted.make(process.env.TSIG_SECRET!),});const peer = yield* Cloudflare.DNS.ZoneTransferPeer("Primary", { ip: "192.0.2.53", tsigId: tsig.tsigId, ixfrEnable: true,});ZoneTransferTsig
Section titled “ZoneTransferTsig”Source:
src/Cloudflare/DNS/ZoneTransferTsig.ts
A Secondary DNS TSIG key
(/accounts/{account_id}/secondary_dns/tsigs) — shared-secret
authentication for zone transfers between Cloudflare and external
nameservers. Reference it from a ZoneTransferPeer via
tsigId.
Requires the Secondary DNS (zone transfer) entitlement on the account. All fields are mutable in place; the secret is redacted and never persisted in attributes.
ZoneTransferTsig: Creating a TSIG
Section titled “ZoneTransferTsig: Creating a TSIG”const tsig = yield* Cloudflare.DNS.ZoneTransferTsig("TransferKey", { algo: "hmac-sha512.", secret: Redacted.make(process.env.TSIG_SECRET!),});ZoneTransferTsig: Using with a Peer
Section titled “ZoneTransferTsig: Using with a Peer”const peer = yield* Cloudflare.DNS.ZoneTransferPeer("Primary", { ip: "192.0.2.53", port: 53, tsigId: tsig.tsigId,});