Getting started
How DNS migration works
See how DNS migration works inside DNSMigrator: canonical records, capability-aware translation, live change plans, authoritative checks, and scoped rollback.
5 min read
On this page
How does a DNS migration work?#
| Stage | Input | Output | Writes? |
|---|---|---|---|
| Read | Provider API or BIND text | Canonical zone | No |
| Normalize | Canonical records | Stable names, values, record sets and order | No |
| Translate | Canonical zone plus destination capabilities | Per-record status, notes and destination record | No |
| Plan | Desired destination records plus live destination | Create, update, delete and unchanged sets | No |
| Apply | Confirmed target and mode plus a fresh destination read | Successful operations and a snapshot | Destination only |
| Verify | Planned records plus destination authoritative nameservers | Pass, fail or informational results | No |
| Cut over | Verified destination plus registrar state | New delegation and, when needed, a DNSSEC transition | Registrar; source only if TTL lowering is selected |
The web migration flow runs these stages through background jobs. The local CLI exposes the same parser, normalizer, translator and diff engine for credential-free checks, but it never applies provider changes.
Canonical zone model#
A provider may call the same concept an RRset, record group, entry or answer. DNSMigrator represents it as a record set with these core fields:
{
"name": "www.example.com.",
"type": "A",
"ttl": 300,
"values": ["192.0.2.10", "192.0.2.11"]
}Provider behavior that cannot fit in ordinary RDATA is metadata. The model can retain proxy state, aliases to provider resources, routing policy, and comments. Provider adapters map the fields they support into this model on read and back into provider operations on write.
Normalization makes comparisons stable:
- Zone and owner names become fully qualified names with a trailing dot.
- Type-specific values are canonicalized, deduplicated and sorted.
- Rows with the same owner, type and routing set identifier merge into one record set.
- When merged rows disagree on TTL, the lower TTL is retained.
- Records are sorted by name and type so comparisons and exports are reproducible.
Normalization does not claim that every provider can store every field. Capability checks happen during translation or provider planning.
Capability-driven translation#
Each catalog entry describes the destination’s accepted record types, TTL range, TXT limit, apex alias behavior, routing policies, proxy support, DNSSEC support and other API constraints. translateZone evaluates each normalized source record against that catalog entry.
| Status | Meaning |
|---|---|
| Exact | The destination representation needs no change or note. |
| Translated | A safe, explicit conversion was made, such as clamping a TTL or storing obsolete SPF data as TXT. |
| Warning | A destination record can be produced, but behavior may change and requires review. |
| Unsupported | No safe destination record was produced. The reason is attached to the row. |
| Skipped | The provider owns the record, such as SOA, DNSKEY or apex NS in the migration translator. |
Translation handles more than type names. It converts aliases to a destination-native alias or CNAME, can use resolved addresses supplied by the caller, and removes source-only proxy metadata when the destination has no proxy. In a one-time migration, routing sets are flattened into ordinary answers; failover keeps the non-secondary answers while other policies combine their values. Provider-only redirect and appliance functions are blocked when there is no equivalent rather than being disguised as DNS records.
A warning is intentionally not “close enough.” For example, replacing an apex alias with current static addresses produces a usable A record but loses automatic address tracking. Read Translation rules and Previews and record statuses for the individual cases.
Change planning#
The translated target says what DNSMigrator wants. A change plan says how the destination differs right now. Records are keyed by owner, type and routing identifier, then divided into four groups:
- Create: desired record set is absent.
- Update: the key exists, but TTL, values or relevant metadata differs.
- Delete: the record exists only at the destination and exact-copy mode allows deletion.
- Unchanged: the destination already has the desired representation.
Keep extra records uses merge mode and does not populate the delete set. Make an exact copy uses mirror mode. SOA, DNSKEY and apex NS records are excluded from ordinary migration diffs because providers manage them. Managed-zone planning handles declared nameservers separately and applies ignore rules, catalog capability checks, and NO_PURGE before producing a plan.
Provider writes are not transactional. An adapter can accept some operations and reject others, so a partial apply records the successful subset for rollback and reports the failures in Activity.
Preview and live re-planning#
A migration preview reads the source, stores a source snapshot, translates it, and reads the existing destination when one exists. A managed-zone preview performs the same comparison for each configured provider and includes provider-specific warnings.
Apply ensures the destination zone exists, reads it again, stores the pre-apply snapshot, and recalculates the plan. If the fresh operations differ from the preview, DNSMigrator records the revised counts in Activity and applies the fresh plan. A managed-zone push first checks that the saved draft version still matches the preview, then its worker re-reads every provider and applies newly calculated provider plans.
See Apply and verify and Preview and push for each workflow’s confirmation gates.
Authoritative verification#
Verification bypasses recursive-cache ambiguity by querying nameservers reported for the destination zone. For literal records, the normalized observed value set must exactly equal the planned set. Missing and unexpected values are reported separately.
Aliases need a different check. An ALIAS, apex CNAME or provider-resource alias is queried as A or AAAA. An empty answer fails. When a source authoritative answer is available, equal address sets pass; different non-empty sets are informational because CDNs and load balancers can legitimately return different addresses. Initial failures are retried before the migration returns to an applied state with mismatches visible.
Verification proves what the destination authoritative servers answer. It does not change the public delegation. That happens later in Nameserver cutover.
Rollback scope#
DNSMigrator stores both the destination snapshot and the subset of operations that actually succeeded. Rollback builds an inverse plan from that subset:
- a created record is deleted only if it still equals the value DNSMigrator wrote;
- an updated record is restored only if it still equals the post-migration value;
- a deleted record is recreated only if its key is still absent.
A record changed out of band after apply is skipped and reported. This is why rollback is narrower than restoring the whole snapshot. Read Rollback.
BIND input and output#
The parser understands $ORIGIN, $TTL, owner inheritance, parentheses, comments, and duration-style TTLs. It returns parsed records and line-specific errors so a partial file can still be inspected. DNSSEC signing records that should not move between providers, including RRSIG and NSEC families, are ignored on import. $INCLUDE, $GENERATE, and other unsupported directives produce errors; materialize their records in the pasted file instead.
Serialization emits normalized BIND text. Provider-resource aliases with no zone-file representation are emitted as comments, and unsupported translated records are omitted from downloadable migration files with the report as the explanation. See BIND zone files.