Deploy dual-region DTM
Region-aware answers on private IPs are DTM’s flagship trick, and dual-region is also its highest-silent-failure setup: a missing peering route or a closed cluster port leaves the second node joining forever, and a missing identity grant makes Azure discovery quietly do nothing. This tutorial gates on the prerequisites first, proves each layer before building the next, and ends with a fallback you have actually watched happen.
1. Gate on the prerequisites before spending money
Section titled “1. Gate on the prerequisites before spending money”One network fact must be true before you deploy, and one decision must be made in the wizard.
- Non-overlapping address spaces between the two regions’ VNets, and no subnet NSG, UDR, or hub firewall that blocks traffic between them. This is the number-one cause of a second node stuck joining.
- Peering: decide before you deploy. The dual-region plan creates the
bidirectional peering for you (the “Create a VNet peering between the two
VNets” checkbox, on by default). If your VNets are already connected, by an
existing peering or through a hub, clear that checkbox
(
createVnetPeering = false) so the deployment does not try to create a second peering.
You do not need to pre-create NSG rules. The plan creates both NSGs, allowing
7946 TCP and UDP between the two node IPs and 53 from your DNS client
CIDR, per the port table.
Verify cross-region routability with any existing VM pair across the
peering (ICMP, or any port that is already listening). Do not test 7946 yet:
no DTM node exists until step 2, so that port will not answer. Ten minutes
here saves an afternoon of
stuck-joining troubleshooting.
2. Deploy the dual-region plan
Section titled “2. Deploy the dual-region plan”Deploy the dual-region plan (Marketplace, or its Bicep dialect) with one
node per region and one shared Key Vault in the primary region. The
bootstrap is behavioral and worth knowing: the primary node generates the
cluster’s secrets (the initial administrator password and the cluster
encryption key) and publishes them to the vault; the secondary reads the
same vault and retries until they appear, so expect the second node to reach
ready a few minutes after the first. Retrieve the admin password with the
deployment’s recoverAdminAccessCommand output
(retrieveAdminPasswordCommand in the Bicep dialect);
first login covers it.
3. Verify cluster formation, then prove replication
Section titled “3. Verify cluster formation, then prove replication”Readiness first, on both nodes (the secondary sits at 503 until its first
full copy of cluster state arrives):
curl -sk https://10.0.250.4:8443/readyz # northeurope node: 200curl -sk https://10.1.250.4:8443/readyz # westeurope node: 200Then membership:
curl -sk https://10.0.250.4:8443/api/v1/nodes \ -H "Authorization: Bearer $DTM_TOKEN"# two members, both "status": "alive", each with its regionIf only one member appears, now is the moment to test the gossip port
directly, from a VM in one region toward the other node’s IP:
nc -vz 10.1.250.4 7946. It answers only once the node is up, which is why
this check belongs here and not in step 1.
Now prove replication the way that actually matters, write on one node and resolve from the other:
# create zone example.internal. and record app -> 10.0.1.10 on the NE node# (UI, API, or Terraform; see the quickstart), then:dig @10.1.250.4 app.example.internal +short# 10.0.1.10 (written in northeurope, answered from westeurope)4. Teach DTM your regions
Section titled “4. Teach DTM your regions”DTM maps a client to a region by the subnet its query arrives from. Two ways to build that map; pick one:
- Path A, Azure discovery: the deployment ships with
azure.region_discovery_enabledon; add a discovery scope and run it (POST /api/v1/discovery/scopes, thenPOST /api/v1/discovery/run, both admin; add?dry_run=truefirst to preview the diff without writing). Note the periodic loop only detects drift after this; you apply changes from the UI or with another run, unless you setazure.region_discovery_auto_apply: true. The prerequisite is the Reader grant at each scope, and a missing grant fails silently: discovery runs and maps nothing. The antidote is the identity probe surface, the Cluster permissions card on the Lifecycle page (orGET /api/v1/lifecycle/identity), which shows a per-backend read-only probe refreshed every 60 seconds; check it before blaming the mapper. - Path B, manual mappings: explicit and grant-free:
curl -sk -X POST https://10.0.250.4:8443/api/v1/subnet-mappings \ -H "Authorization: Bearer $DTM_TOKEN" -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{"subnet": "10.0.0.0/16", "region_id": "northeurope", "source": "manual"}'# repeat for 10.1.0.0/16 -> westeuropeRegions and subnet mapping explains precedence when both sources coexist.
Verify with the right endpoints: GET /api/v1/subnet-mappings lists the
mappings (subnet to region); GET /api/v1/regions returns the deduplicated
region IDs only, so use it as the roll-up check, not the detail view.
5. Health-check the record, then make it region-aware
Section titled “5. Health-check the record, then make it region-aware”First, attach a health check to the record’s values (a TCP probe on the
app port with a 5 second interval, per
health checks; the default interval is 15
seconds, and the explicit 5 keeps step 7’s drill timing tight). This is not
optional garnish: without probes, values never go unhealthy and region
fallback can never trigger.
Then make the record dual-region: add the westeurope backend as a second
value, tag each value with its region (a blank region auto-detects from the
subnet map), and set the policy to region (local first, with an optional
region_preference fallback order) or geo-failover (region filtering plus
round-robin within the selected region).
Load-balancing methods covers the choice, and
its two standing prerequisites: subnet mappings in place, and every node
able to probe backends in both regions (peering is not transitive; if
probes cannot cross, health goes dark from one side).
Verify: GET /api/v1/health/status on each node shows both backends
healthy from that node’s own vantage point.
6. Prove local-first
Section titled “6. Prove local-first”From a client in each region, against that region’s node:
# northeurope clientdig @10.0.250.4 app.example.internal +short # 10.0.1.10
# westeurope clientdig @10.1.250.4 app.example.internal +short # 10.1.1.10Same name, different region, different (local) answer: that is the feature.
7. Drill the cross-region fallback
Section titled “7. Drill the cross-region fallback”Stop the westeurope backend and keep querying from the westeurope
client. The timing is the standard probe math: with the 5 second interval
you set in step 5 and the template’s fixed failure threshold of 3, the
local node marks its backend
unhealthy after about 15 seconds and answers flip to 10.0.1.10, plus the
record TTL as the client-cache floor. Restore the backend; two consecutive
successful probes bring it back.
Two semantics to observe while you watch, both by design:
- Health is per node, never replicated. Each node answers from its own probe view, so run the drill’s digs against the client’s local node; the northeurope node’s view of the westeurope backend is a different (and independently correct) story.
- If every value is unhealthy, DTM fails open: it treats all values as candidates again (so answers never go empty), and your record’s region policy still picks from them. An answer that might reach a recovering backend beats an empty answer that strands every client.
Verify: you saw the flip, the timing matched the math, and recovery returned the local answer.
8. Cut clients over, local node first
Section titled “8. Cut clients over, local node first”Point each region’s VNet DNS at its local node first with the remote node second, so normal resolution stays regional and a node loss fails over to the other region’s node:
az network vnet update -g rg-ne -n vnet-ne --dns-servers 10.0.250.4 10.1.250.4az network vnet update -g rg-we -n vnet-we --dns-servers 10.1.250.4 10.0.250.4(Clients pick the change up on restart or lease renewal.) Then extend your backups and observability to cover both nodes; a dual-region cluster with single-region monitoring is half-finished.
Where next
Section titled “Where next”- Wire observability end to end: dashboards and alerts across both nodes.
- Production readiness pass: the go-live gate, now with two regions in scope.
- Adding nodes (scaling out): a second node per region is the next resilience step.
- Regions and subnet mapping: the mapping model in full.
Need a hand? Email [email protected].
Last validated: 2026-07-26