Skip to content

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.

Create a conditional forwarder in DTM for corp.example pointing at your current DNS servers, and prove transparent pass-through:

Terminal window
dig @10.0.250.4 anything.corp.example +short
# same answer your existing DNS gives

Honest 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.

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):

Terminal window
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:

Terminal window
curl -sk https://10.0.250.4:8443/api/v1/healthchecks \
-H "Authorization: Bearer $DTM_TOKEN"
Terminal window
dig @10.0.250.4 app.corp.example +short # health-gated answer from DTM
dig @10.0.250.4 sibling.corp.example +short # still forwarded, unchanged

That pair of digs is the whole value proposition in two lines.

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.

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.

Terminal window
terraform import dtm_override_record.app <override-id>

Note target_mode changes force replacement; the Terraform page covers the resource.

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.

Terminal window
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:

  1. Dry-run the import, review, then import.
  2. Immediately add the lb_policy and health_check_template to the app record in the now-authoritative zone (PUT the record), so the gating exists on the zone path before anything else changes.
  3. Verify parity with the same dig pair from step 4.
  4. Delete the override.
  5. 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.)

Need a hand? Email [email protected].

Last validated: 2026-07-26