Adopt DTM one name at a time
You do not have to migrate a zone to get DTM’s failover. An override record intercepts exactly one name while everything else in the domain keeps resolving from your existing DNS server, and removing it is one call. This tutorial takes one production hostname through the whole ramp: forward, override, drill, codify, expand, and eventually graduate to a real zone migration without silently losing the health gating along the way.
1. Forward the domain first
Section titled “1. Forward the domain first”Create a conditional forwarder in DTM for
corp.example pointing at your current DNS servers, and prove transparent
pass-through:
dig @10.0.250.4 anything.corp.example +short# same answer your existing DNS givesHonest framing: the override you are about to create does not require
this forwarder (overrides intercept before conditional forwarding). You do
it first because it proves pass-through and gives every sibling name a
home before you intercept anything; skip it and the failure mode is broken
sibling resolution (falling to the default forwarder, or REFUSED), not a
broken override.
2. Put one pilot client on DTM
Section titled “2. Put one pilot client on DTM”Point one test client (or a test resolver) at 10.0.250.4 so you can watch
both behaviors side by side: forwarded siblings and, in a moment, the
overridden name.
3. Create the override, health check included
Section titled “3. Create the override, health check included”One call creates the override and its health checks (the template is inline; there is no separate entity to create first):
curl -sk -X POST https://10.0.250.4:8443/api/v1/overrides \ -H "Authorization: Bearer $DTM_TOKEN" -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{ "fqdn": "app.corp.example", "type": "A", "target_mode": "static", "ttl": 30, "values": [{"value": "10.0.1.10"}, {"value": "10.0.1.11"}], "lb_policy": {"method": "failover"}, "health_check_template": { "protocol": "tcp", "port": 443, "interval_seconds": 5 } }'With IP backends this is static mode, the common case. The alternative, linked mode, targets hostnames instead (it rejects IP literals, has a 5 second interval floor, and each node probes the resolved IPs independently); choose it when the app sits behind a name that changes. The override records page owns the full model.
The template auto-creates one check per value, with ids ovr-<id>-0,
ovr-<id>-1 and so on (zero-indexed, one per value); their name is the same
string prefixed auto:. Watch them:
curl -sk https://10.0.250.4:8443/api/v1/healthchecks \ -H "Authorization: Bearer $DTM_TOKEN"4. Verify both behaviors
Section titled “4. Verify both behaviors”dig @10.0.250.4 app.corp.example +short # health-gated answer from DTMdig @10.0.250.4 sibling.corp.example +short # still forwarded, unchangedThat pair of digs is the whole value proposition in two lines.
5. Drill the failover
Section titled “5. Drill the failover”Stop the listener on 10.0.1.10 and repeat the first dig. The timing is
fixed by the template math: template-created checks use failure threshold
3 and success threshold 2, so at the 5 second interval the answer flips
in about 15 seconds and recovers about 10 seconds after you restore
the backend. (Tune an auto-created check afterwards via
PUT /api/v1/healthchecks/{id} if you need different behavior.) One
semantic worth witnessing: if every backend is down, DTM fails open
rather than going dark: the health gate stops filtering and the answer comes
from the full value set under your policy (with failover, the primary’s
address); an address (A/AAAA) query for a claimed name is never forwarded
(other query types, like TXT or MX, still follow the forwarder). A query
for the other address family answers empty (NODATA), never NXDOMAIN.
6. Codify it in Terraform, by import
Section titled “6. Codify it in Terraform, by import”If DNS is code for your team, adopt the live override with terraform import, never by re-declaring it: overrides are one-per-FQDN (a second
create is a 409), and delete-then-apply opens a real resolution gap on a
production name.
terraform import dtm_override_record.app <override-id>Note target_mode changes force replacement; the
Terraform page covers the resource.
7. Expand, one reversible name at a time
Section titled “7. Expand, one reversible name at a time”Repeat for the next name that deserves failover, keeping an inventory of active overrides. The rollback story stays constant at every stage: deleting an override takes effect immediately, and that name simply forwards again.
curl -sk -X DELETE https://10.0.250.4:8443/api/v1/overrides/<id> \ -H "Authorization: Bearer $DTM_TOKEN" -H "X-DTM-CSRF: 1"8. Graduate to a real zone, without losing the gating
Section titled “8. Graduate to a real zone, without losing the gating”Eventually the override inventory says this zone belongs on DTM. The
graduation has a trap: the moment you import the zone and DTM becomes
authoritative, the zone path wins and your override is silently
shadowed, and imported zone files carry plain records with no
lb_policy or health_check_template (file imports are lossy on those). Do
it in this order:
- Dry-run the import, review, then import.
- Immediately add the
lb_policyandhealth_check_templateto theapprecord in the now-authoritative zone (PUTthe record), so the gating exists on the zone path before anything else changes. - Verify parity with the same dig pair from step 4.
- Delete the override.
- Remove the conditional forwarder last.
(One guardrail you may meet: an override equal to a zone apex is rejected outright, so apex names error early rather than going quietly dormant like child names.)
Where next
Section titled “Where next”- Migrate from Azure Private DNS: the full-zone journey the graduation step opens into.
- Override records: the mechanism in full, including linked mode.
- Health checks: the probe math behind the drill.
Need a hand? Email [email protected].
Last validated: 2026-07-26