Skip to content

Zero to serving DNS

This tutorial takes you from the Azure Marketplace to a test VNet resolving through DTM, in the order that avoids the classic day-one mistake: pointing VNet DNS at a server you have not proven yet. You will deploy a node, create one record with automatic failover, watch that failover actually happen, and only then touch your VNet’s DNS settings.

In the Azure Marketplace, deploy DTM’s single-region plan into a dedicated /27 subnet in your VNet (for this scenario, 10.0.250.0/27). Take the wizard’s defaults except: set the DNS client CIDR to cover the subnets your clients and backends live in (here 10.0.0.0/16), and note the Admin CIDR (the adminAddressPrefix parameter) for later hardening. The deployment creates one node VM with a Premium SSD data disk holding the node’s local data store, a Key Vault for the cluster’s secrets, an NSG scoped to your CIDRs, and a stable private IP. Deploy covers every parameter and permission; if the deploy fails at role assignment, that page’s permissions section is the fix.

Verify, and capture the node’s IP for everything that follows:

Terminal window
az vm list-ip-addresses -g rg-dtm-poc -o table
# VirtualMachine PrivateIPAddresses
# ---------------- ------------------
# dtm-node-1 10.0.250.4

Before logging in, before creating anything, confirm the node is serving:

Terminal window
curl -sk https://10.0.250.4:8443/readyz
# 200: ready

A single node deployed without seeds goes straight from starting to ready, so a 503 here means the deployment is still finishing (or failed); see Verify the cluster before continuing.

Retrieve the initial administrator password from the deployment’s Key Vault (it is generated on the node at first boot and never appears in the template or its outputs):

Terminal window
az keyvault secret show \
--vault-name kv-dtm-poc \
--name dtm-initial-admin-password \
--query value -o tsv

Browse to https://10.0.250.4:8080 and sign in as admin. Your browser will warn about the certificate: this is the node’s first-boot web UI certificate, self-signed with two-year validity and the node’s IP as its subject alternative name. For a POC that is acceptable; before production, replace it with one your browsers trust, per TLS certificates.

Verify: the dashboard loads and shows one node, alive.

Do two things before creating any DNS data: change the admin password (the Key Vault copy is a bootstrap secret, not a password manager), and create a named admin account for yourself so the audit log records a person, not a shared login. Both are on the Users page; see users and access for the role model.

Verify: sign in with the named account in a private browser window before signing out of the bootstrap session. A login that works in a private window is the proof; one you assume is not.

5. Create the zone and one failover-ready record

Section titled “5. Create the zone and one failover-ready record”

Create zone example.internal. (trailing dot), then one A record named app with two values, 10.0.1.10 and 10.0.1.11, and a TTL of 30, and set its Load balancing method to round-robin while you are in the form (with no policy set, DTM returns all healthy values in every answer; round-robin is what makes step 6’s rotation visible). One record with two values, not two records: load-balancing policy and health checks attach per record and apply across its values. The quickstart shows the same step over the API and Terraform if you prefer those paths.

Verify: the zone shows active in the UI (a new zone is created active).

6. Prove resolution with dig, not with the VNet

Section titled “6. Prove resolution with dig, not with the VNet”

From a client VM, query the node directly:

Terminal window
dig @10.0.250.4 app.example.internal +short
# 10.0.1.10 (repeat the query; both IPs appear across answers)

DTM answers with one healthy value per query under round-robin, so repeated queries rotate across both backends.

7. Attach a health check and switch to failover

Section titled “7. Attach a health check and switch to failover”

Edit the app record: enable a TCP health check on port 443 with a 5 second interval, and change the load-balancing method to failover. Under failover, answers contain only the first healthy value in order, so dig now returns 10.0.1.10 every time while it is healthy. See health checks and load-balancing methods for what each knob does.

Verify that both backends pass their probes:

Terminal window
curl -sk https://10.0.250.4:8443/api/v1/health/status \
-H "Authorization: Bearer $DTM_TOKEN"
# both checks: "status": "healthy"

(Capture $DTM_TOKEN as shown in the quickstart, or just watch the Health Checks page in the UI.)

8. Drill the failure before your users find it

Section titled “8. Drill the failure before your users find it”

Stop the app listener on the primary backend, 10.0.1.10 (stop the service or its VM; under failover, stopping the standby would show nothing). Start a stopwatch and keep repeating the dig from step 6.

The math is predictable: checks created from a record template use a fixed failure threshold of 3, so with a 5 second interval the node marks the backend unhealthy after about 15 seconds, and every new answer flips to 10.0.1.11. Clients holding the cached answer age out within the record’s 30 second TTL, for a worst case of about 45 seconds end to end:

Failover timeline for the worked example: the backend fails at t equals zero, three failed probes over 15 seconds mark it unhealthy, new queries get healthy-only answers from that moment, and clients still holding the cached answer age out within the 30 second TTL, for a worst case of 45 seconds. Failover timeline for the worked example: the backend fails at t equals zero, three failed probes over 15 seconds mark it unhealthy, new queries get healthy-only answers from that moment, and clients still holding the cached answer age out within the 30 second TTL, for a worst case of 45 seconds.

Restore the backend and keep watching: after two consecutive successful probes (about 10 seconds), answers return to 10.0.1.10.

Verify: you saw the flip and the recovery with your own stopwatch. That number, not the documentation, is what you quote in your runbook.

Only now, with resolution and failover proven, point a test VNet at DTM:

Terminal window
az network vnet update -g rg-test -n vnet-test --dns-servers 10.0.250.4

Azure clients pick up DNS server changes on restart or DHCP lease renewal, so reboot the test VM (or wait out its lease) before judging the result.

Verify both halves of resolution from the test VM, with no @ this time:

Terminal window
dig app.example.internal +short # answered by DTM: 10.0.1.10
dig www.microsoft.com +short # recursion via the default forwarder

Internet names keep resolving because the single-region deployment configures a default forwarder to Azure’s platform resolver (168.63.129.16) at first boot; see forwarders for how that pipeline works.

You now have the whole day-one arc: deployed, verified, one health-gated name, a failover you have personally witnessed, and a test VNet resolving through DTM. The checklist you just walked:

Done Step
[x] Node deployed and readyz returns 200
[x] Bootstrap admin retired for a named account
[x] app.example.internal created as one record, two values
[x] Resolution proven with dig against the node
[x] Failover drilled and timed
[x] Test VNet cut over, internal and internet names verified

Need a hand? Email [email protected].

Last validated: 2026-07-26