Skip to content

Zone management

Import transforms

Transform imported DNS records with name, type, target, and TTL rules. Trace derived records, strip labels, and build IPv4 or IPv6 reverse zones.

4 min read

On this page

Import transforms derive A and CNAME records from another managed zone, optionally remapping IPv4 ranges and owner suffixes; REV derives reverse-zone names for PTR records. The key point is that a transform is saved as a rule, then expanded from current saved sibling zones during preview and push rather than permanently copying generated records into the Records table.

Configure the settings form#

Both the source and destination must already be managed in the same workspace. Open the destination under Zones, then open Settings → Import transform.

Choose the source

Enter its exact managed-zone name in Copy A records from zone, such as internal.example.com. Despite this label, the planner imports both A and CNAME records; it skips every other type.

Choose the TTL

Leave TTL blank to inherit each source record's TTL, or enter a whole number of seconds to apply one TTL to every generated A and CNAME set.

Enter the transform table

Add one range per line under Transform table. Each line has four columns separated by ~: low address, high address, replacement bases, and fixed replacement addresses.

Save and preview

Select Apply to draft, then Save changes. Select Preview changes and inspect the generated operations for each provider. The generated sets appear in Changes, not as permanent rows under Records.

The form edits one transform. Loading dnsconfig.js can preserve several transforms, but applying the settings form replaces that list with the single rule shown there. Clear both the source and table and select Apply to draft to remove transforms.

Read a transform-table row#

The text format is:

text
low IPv4 ~ high IPv4 ~ new base list ~ new IP list

A row must contain all four columns, even when one is blank. Low and high are inclusive IPv4 addresses and low cannot be greater than high. The two output modes are mutually exclusive:

  • New base preserves the source address's offset from low. A comma-separated list creates one output per base.
  • New IPs replaces every source address in the range with the same comma-separated fixed list.

The first matching row wins. An address outside every row remains unchanged.

text
1.2.3.10 ~ 1.2.3.20 ~ 123.123.123.100 ~
2.4.6.20 ~ 2.4.6.20 ~ ~ 9.9.9.9
5.5.5.0 ~ 5.5.5.255 ~ 6.6.6.0,7.7.7.0 ~

This table produces:

SourceResultWhy
1.2.3.13123.123.123.103Source offset is 3 from .10; that offset is added to the replacement base .100.
2.4.6.209.9.9.9The fixed replacement list is returned.
5.5.5.76.6.6.7, 7.7.7.7Offset 7 is applied to both bases.
8.8.8.88.8.8.8No row matches.

IPv6 is not accepted in a transform table. Invalid addresses, malformed rows, or a row containing both output modes prevent Apply to draft from succeeding.

Follow an imported record#

Assume foo.com contains:

text
one    60  IN  A      1.2.3.10
three  60  IN  A      1.2.3.13
www    60  IN  CNAME  one.foo.com.
@      60  IN  MX     10 mx.foo.com.

and bar.com contains this rule:

dnsconfig.js
D("bar.com", REG_NONE, DnsProvider(DSP),
  IMPORT_TRANSFORM(
    "1.2.3.10 ~ 1.2.3.20 ~ 123.123.123.100 ~",
    "foo.com",
    300,
  ),
)

The preview includes:

text
one.foo.com.bar.com.    300  IN  A      123.123.123.100
three.foo.com.bar.com.  300  IN  A      123.123.123.103
www.foo.com.bar.com.    300  IN  CNAME  one.foo.com.bar.com.

The source's full owner name, without its final dot, is appended in front of the destination zone. A records pass through the table. CNAME targets do not pass through the IP table; the destination domain is appended to their target. MX is skipped because only A and CNAME are imported.

A record already declared in the destination wins over a generated record with the same owner and type. Source records marked import_transform_skip through dnsconfig.js are also excluded. Generated duplicates are merged into canonical record sets.

Use IMPORT_TRANSFORM_STRIP#

IMPORT_TRANSFORM_STRIP accepts the same table, source domain, and optional TTL, followed by a suffix to remove from each source owner before the destination is appended:

dnsconfig.js
D("com.extra", REG_NONE, DnsProvider(DSP),
  IMPORT_TRANSFORM_STRIP(
    "1.2.3.10 ~ 1.2.3.20 ~ 123.123.123.100 ~",
    "foo.com",
    300,
    "com",
  ),
)

For one.foo.com., the importer removes .com, then appends .com.extra., producing one.foo.com.extra.. If the destination begins with the stripped suffix plus a dot, CNAME targets append only the destination remainder, so one.foo.com. also becomes one.foo.com.extra. rather than one.foo.com.com.extra..

The suffix match is exact and case-normalized with the zone. If a source owner does not end with .<suffix>, that transform expansion is skipped and the core records an error string; as with other planner warnings, the release managed-zone Changes payload does not display it. Import transform settings have no suffix field, so use Config as code → Load from dnsconfig.js for this form. See dnsconfig.js import and export.

Re-evaluate transforms at push time#

Preview loads the saved destination and other managed zones in the workspace, expands transforms, and compares the result with each provider. Push repeats that work against current saved sibling zones and fresh provider state. The release does not compare the new operation set with the preview, so a source-zone save made after review can change what the confirmed push applies. Run a new preview after changing either zone.

Ignore rules and provider capability checks run after expansion. A generated value can therefore be preserved or rejected just like a direct draft value. Review Ignore rules, Record types, and each provider section in Changes.

Use IP in dnsconfig.js#

IP("a.b.c.d") converts a valid IPv4 address to its unsigned 32-bit number. The importer converts numeric A arguments and numeric transform-table fields back to dotted addresses. This makes offset expressions possible without string parsing:

dnsconfig.js
var PRIVATE = IP("10.0.0.0");
var PUBLIC = IP("203.0.113.0");

D("example.com", REG_NONE, DnsProvider(DSP),
  A("next", IP("192.0.2.10") + 1),
  IMPORT_TRANSFORM([
    { low: PRIVATE, high: PRIVATE + 255, newBase: [PUBLIC] },
  ], "internal.example.com"),
)

next becomes 192.0.2.11. The table maps 10.0.0.7 to 203.0.113.7. IP accepts IPv4 only; an invalid or partial address stops the browser importer.

Build reverse zones with REV#

REV(cidr) returns the reverse-zone name for IPv4 or IPv6 and is available through Config as code. It does not query an RIR or create delegation records.

dnsconfig.js
D(REV("192.0.2.0/24"), REG_NONE, DnsProvider(DSP),
  PTR("192.0.2.42", "host.example.com."),
)

D(REV("2001:db8:302::/48"), REG_NONE, DnsProvider(DSP),
  PTR("2001:db8:302::2", "v6.example.com."),
)

The first zone is 2.0.192.in-addr.arpa; the full IPv4 owner is reduced to 42. The IPv6 zone is 2.0.3.0.8.b.d.0.1.0.0.2.ip6.arpa, and the address becomes the remaining reversed nibbles inside it. A full address outside the chosen reverse zone is rejected.

The default classless IPv4 form follows RFC 2317:

text
REV("192.0.2.128/25") → 128/25.2.0.192.in-addr.arpa

Set compatibility once before declaring reverse zones to use the RFC 4183-style hyphen form and mask host bits:

dnsconfig.js
REVCOMPAT("rfc4183")
D(REV("192.0.2.130/25"), REG_NONE, DnsProvider(DSP),
  PTR("192.0.2.130", "host.example.com."),
)

That zone is 128-25.2.0.192.in-addr.arpa. In the default mode, a CIDR containing host bits, such as 192.0.2.130/25, is rejected. Prefixes shorter than eight bits are rejected in RFC 4183 mode. Validate exported zone text with the zone file validator, and compare browser-importer compatibility under DNSControl parity.