Release notes
Each Hayami DTM release on the Azure Marketplace, newest first. Before you upgrade, read the breaking changes and upgrade notes of every version between the one you run and the one you are moving to, then upgrade one node at a time as Rolling upgrades describes.
v1.0.6 (2026-10-22)
Section titled “v1.0.6 (2026-10-22)”This release includes security improvements; we recommend all customers upgrade.
Breaking changes
Section titled “Breaking changes”-
The DNS query log ships through the Logs Ingestion API, and the Data Collector settings are removed. Microsoft ended support for the HTTP Data Collector API on 2026-09-14, and it does not work with Azure Monitor Private Link Scope. The query log now goes to a data collection rule, authenticated by the node’s managed identity, with no shared key. Set
azure.logs_ingestion_endpoint,azure.logs_ingestion_dcr_immutable_idandazure.logs_ingestion_stream(defaultCustom-DTMLogs) in the configuration file, inPUT /api/v1/config/azure-monitor, in Terraform’sdtm_azure_monitor_configor on the Settings page, and give each node’s managed identity Monitoring Metrics Publisher on the rule. TheDTMLogs_CLtable and its columns are unchanged, so the shipped workbook, the KQL alert pack and your own queries keep working. What is removed:azure.log_analytics_workspace_idandazure.log_analytics_workspace_key, their API fields, and thedtm_azure_monitor_configattributeslog_analytics_workspace_id,log_analytics_workspace_key_setandlog_analytics_workspace_key_wo(with its version);- the Terraform
monitoringmodule’sworkspace_keyoutput.
A configuration file that still carries them loads, and start-up logs an error that names the replacement; nothing ships through them and DNS keeps serving. A
PUTthat still sends them answers 400, naming the replacement. See the upgrade notes for the one-time table migration and the key rotation.
- The Terraform and Bicep kits can create the query log’s Logs Ingestion
setup, off by default, so a deployment that does not opt in plans exactly
as before. In the Terraform
monitoringmodule, setlogs_ingestion_enabled = trueand pass the nodes’ identities innode_principals: it creates theDTMLogs_CLtable, a data collection endpoint, a data collection rule and one role assignment per identity, scoped to the rule, and outputs the three valuesdtm_azure_monitor_configtakes. In the single-region and dual-region Bicep plans, setenableLogsIngestionwithenableObservability. The Marketplace (ARM) templates do not create it: the Azure Monitor page of the docs shows how to create the setup by hand for a Marketplace deployment, including a no-shell restart and what a node does when the rule cannot be reached. - A failed stable-IP swap that leaves the stable IP unserved now raises an
alert. The new gauge
dtm_lifecycle_swap_stable_ip_unrestoredcounts the stable IPs whose latest swap run failed without the address back on a running VM, and the new critical alertDTMSwapStableIPUnrestored(also in the Azure Monitor alerting samples) fires on it. A failed run reportsstable_ip_outcome(not_restoredornot_confirmed) inGET /api/v1/lifecycle/swap. The gauge reports what the swap recorded, not a live check of the address: after you fix the address by hand, retry the swap to clear it. - New metrics for the query log:
dtm_observability_querylog_retimed_rows_totalcounts rows whose own time Azure replaced because the node’s clock is more than two days behind or a day ahead (a sign the clock is wrong), anddtm_observability_log_api{api}says what the node ships through (logs_ingestionornone).
Changed
Section titled “Changed”- Built with Go 1.26.10, which distrusts eight compromised eMudhra (emSign) intermediate certificates. Earlier releases accepted a public certificate chain that passes through one of them. See the upgrade notes for nodes that are still on 1.0.5.
- The Azure Monitor metrics target is validated.
PUT /api/v1/config/azure-monitoranswers 400, naming the field, for amonitor_regionthat is not lower-case letters and digits or amonitor_resource_idthat is not an Azure resource ID. A node logs an error at start-up for the same values in the configuration file, and the exporter no longer follows a redirect. A valid region and resource ID are unchanged. - The query log is sent in batches, where the old path made one request
per DNS query (see query-log shipping). A throttled or briefly
unavailable endpoint is retried. The
TimeGeneratedof a row is now the query’s own time, not the ingestion time.dtm_observability_push_total{exporter="azure_log"}now counts one outcome per batch, where it counted one per row. - The Azure Monitor endpoint must be an Azure Monitor host. The node sends
its managed identity token to it, so an IP address or a host outside
.monitor.azure.comis refused. - Script health checks can be changed or deleted only by an admin. A
non-admin
PUTorDELETEon aprotocol: scriptcheck answers 403 and leaves the check as it was. Editors keep full control of every other check. A background process that a script check leaves behind is now stopped with it. See HAY-2026-003. - Security fix: admin-only API endpoints now refuse every account below admin. Earlier versions could, in rare cases, let a readonly or editor account through. Upgrade to 1.0.6 (HAY-2026-002).
GET /api/v1/config/backupis admin only. It names the storage container that receives snapshots, the local directory and whether snapshots are encrypted. A readonly user or a service account that scripted this call now gets 403.GET /api/v1/lifecycle/swapandGET /api/v1/lifecycle/swap/{run_id}are admin only, and the dtm-ui Lifecycle page shows a non-admin an empty swap panel.GET /api/v1/lifecycle/oraclestays readable by every role, but the text of itslast_erroris shown to admins only; other roles readhidden (admin only).- A wildcard record in a Microsoft
privatelinkzone is refused, such as*inprivatelink.blob.core.windows.net, on the API, every zone importer and dynamic updates, with a message that says to create one record per Private Endpoint. DTM never answers from such a record, leaves it out of zone transfers and writes it as a comment in a BIND export. One already stored, for example after an upgrade, is named in a warning at every start-up and listed byGET /api/v1/diagnoseunderstorage.api_refused_recordswith the ruleprivatelink_wildcard. Nothing removes it for you. A wildcard in any other zone is unaffected. See HAY-2026-004. - The BIND zone-file export writes a record it could not have loaded as a
;comment, with the reason, instead of as a record. That is a record whose name is not a valid DNS name, whose type is not supported, or whose value (or CAA tag) fails the check the API applies. A stored value that fails its type’s rule, such as an A record that is not an IPv4 address, is now a comment too; a BIND server would not have loaded that line either. Valid records export as before. - Health-check targets are screened for
cloud metadata and platform addresses (and, when a target is saved, loopback
addresses) written in IPv6 forms that embed an IPv4 address, as they
already were when written as IPv4. The NAT64 local-use prefix
64:ff9b:1::/48is refused outright. Azure virtual networks do not provide these paths by default. - Ignore any update-appliance setting; it has no effect in 1.0.6. Nothing in your deployment needs to change for that.
- A forwarded answer served from the cache counts its TTL down. A client that asked near the end of an entry’s life used to be handed the original TTL, and could keep the answer for up to about twice what the upstream intended. Each record’s TTL is now reduced by the seconds since the entry was stored, to a floor of 1 (a TTL the upstream sent as 0 stays 0), and a hit never extends an entry’s life.
GET /api/v1/config/query-logreads back the sample rate that runs. On a node whose configuration file leaves the rate unset it answered 0 while the node logged every query; it now answers 1.- A node that joins with an empty store, such as the replacement in a lifecycle swap, now takes every user. Before, it could refuse the edited admin, and then have no default admin either, so nobody could sign in to it as admin.
- A delete that reaches a node only through the background sync now stops being served there. A conditional forwarder, stub zone, TSIG key, subnet mapping or discovery scope deleted while a node missed the message used to keep being used on that node until a restart: queries still went to the deleted upstream, the deleted TSIG key was still accepted, and the deleted mapping or scope still applied. A deleted health check is also removed from the node’s checker at once.
- The self-signed bootstrap certificate (API on 8443, DNS over TLS and
DNS over HTTPS) renews over the same key. Each renewal in its last 30 days
used to generate a new key too, so anything that pinned the node’s public key
stopped trusting it about every 23 months. The certificate is still a new
one, so a client that trusts the certificate itself (
SSL_CERT_FILE, a forwarder’sca_pem) should fetch it again after a renewal. - The Lifecycle page’s upgrade plan names a subnet join permission it does
not carry. When the old node’s identity held the role above the subnet, on
its virtual network, resource group or subscription, the plan deliberately
does not copy it, and the next swap from the new node was refused with
nothing to say why. The plan now prints a
notice:line naming each inherited role and its scope before the deploy; grant the new node the same before it drives a swap. The plan still never creates a grant. - An aborted swap that leaves the stable IP unserved puts that outage first
in its error (
step SWAPPING failed: STABLE IP <ip> NOT RESTORED: ...; the swap had been aborted: ...), and the Audit Log records it in onelifecycle.swap.recoveryrow instead of a secondlifecycle.swap.failedrow. dtm-server restore --localcan no longer stop the node starting or corrupt a running one:- With
--accept-cross-clusteror--insecure-backupit now opens a working copy of the snapshot with the key the node starts with, and refuses one that would stop the node starting (“Failed to install secrets-at-rest key”), naming--source-key-file. Run it with the service’s environment file loaded, as the backup and restore page shows. A restore made through the API is not yet checked this way; see known limitations. - It refuses while dtm-server holds the database (“is locked by another process”), before it reads the key or the snapshot. Stop dtm-server first.
- It refuses, and changes nothing, when a restore or compaction is already
staged (
dtm.db.restore-pendingordtm.db.restore-triggerexists). Before, a staged restore was silently replaced, and a snapshot that then failed validation left a trigger with no snapshot, so the next start failed with “restore trigger set but sidecar missing”.POST /api/v1/restorestill replaces what is staged. To abandon a staged restore, remove both files.
- With
- A restore whose final swap fails no longer leaves the node without a
database. If applying a staged restore failed (a data directory the service
cannot write, a full disk), the node started with no database and the next
start applied the snapshot anyway. A failed swap now
puts the previous
dtm.dbback and keeps the staged snapshot and trigger, so you can fix the cause and start again; the error says which state the store is in, and if the previous file cannot be put back it namesdtm.db.pre-restoreso you can move it back by hand. A live file that cannot be moved aside at all stops the swap too. This coversdtm-server restore --localand the swap on server start. - A cluster deployed again from scratch can restore its old snapshots. A
snapshot is sealed to the replication key of the cluster that took it, and
every template generates a new key, so after losing a whole deployment its
snapshots could not be restored:
restore --localrefused them, and--accept-cross-clusterapplied them and the node then failed to start. The newdtm-server restore --local --source-key-file <path>takes the old cluster’s key from a file, verifies the snapshot with it, re-encrypts the snapshot’s secrets for this cluster and verifies the result before staging it. A refusal, or a crash before that point, leaves the live store as it was. The key file must be a regular file owned by the user running the command and readable by its owner only, and the key never goes on the command line. Keep the replication key with your backups but away from them: it is the only thing that opens them. - The backup, restore and disaster-recovery runbooks match the code.
Following them used to fail or silently do nothing:
- if you set up the cron backup from these docs before 1.0.6, check that it
is producing backups: its script pointed
--cacertat a file no template writes, so it never took one. It now names the node’s API certificate,/var/lib/dtm/api-tls.crt; - the whole-cluster restore now restores on the node with no seeds, readmits
each wiped node before starting the next, and checks convergence with the
member count and the
dtm_storage_zonesanddtm_storage_recordsgauges; - the disaster-recovery restore no longer crash-loops node 1: it says where
snapshots are, that they are applied with
dtm-server restore --local, and that a snapshot restores only under the replication key it was taken with; - shell access in the disaster-recovery runbook is SSH through a temporary network security group rule that is removed afterwards;
- re-sending a zone to trigger a sync no longer overwrites the zone’s TTL on every node;
- write calls send
X-DTM-CSRF: 1, without which the API answers 403.
- if you set up the cron backup from these docs before 1.0.6, check that it
is producing backups: its script pointed
- The upgrade, known-limitations, troubleshooting and failure-mode pages
match the code. The main corrections that change what you do:
- replacing a node that is already dead is refused unless
retire_node_idorretire_skipis sent; see rolling upgrades; - never change
replication.encrypt_keyone node at a time, because it splits the cluster; there is no live rotation of that key. Rotating it does not invalidate API tokens; removing the node that minted them does; - if you move
dtm.dbaside on a node, remove and readmit the node before it rejoins; see disaster recovery; - when you check that all nodes are alive, count the members against the
number you expect, because
GET /api/v1/nodeslists only current members; - two alert examples that could not fire now do, and the drain alert no longer fires at the start of every drain;
- which secrets a snapshot seals: TSIG, Entra, OAuth2 and OTLP secrets are sealed, health-check bearer headers and mTLS keys are not.
- replacing a node that is already dead is refused unless
Upgrade notes
Section titled “Upgrade notes”-
Nodes still on 1.0.5: install the Ubuntu
ca-certificatesupdate, then restart. The Ubuntuca-certificatesupdate20260816ubuntu0.24.04.1removes the root Ubuntu blocked for this incident, “emSign ECC Root CA - G3” (SHA-256 fingerprint86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B), so a target of your own whose certificate chain ends at that root will stop verifying. The same update also drops four roots in Mozilla’s routine 2.90 refresh (“Atos TrustedRoot 2011”, “Entrust Root Certification Authority”, “SecureSign Root CA12” and “ePKI Root Certification Authority”), so a target chaining to one of those stops verifying too: check your HTTPS health-check targets and forwarders first. A running process only reads the roots at start, so afteraptinstalls the update, restart dtm-server and dtm-ui, one node at a time (a rolling restart). Check the result with (ca-certificatesshould be at or above20260816ubuntu0.24.04.1, and the count should be 0):Terminal window apt-cache policy ca-certificatesopenssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt \| openssl pkcs7 -print_certs -noout \| grep -c '^subject=.*CN = emSign ECC Root CA - G3$' # expect 0Tested against Ubuntu’s own packages, the count reads 1 with
ca-certificates20240203 and 0 with 20260816ubuntu0.24.04.1. (ls /etc/ssl/certs | grep -c emSign_ECC_Root_CA_-_G3is a shorter check that reads the same.)On 1.0.6 the binary itself refuses chains through the eight intermediates. Apply the
ca-certificatesupdate as well, as normal OS patching.Corrected 2026-10-24: an earlier version of these notes named different roots and a different check. The root, the list and the check above are taken from the Ubuntu package and tested against it.
-
A workspace that already has a
DTMLogs_CLtable needs a one-time migration before the first apply with Logs Ingestion on. That is a table created by the old Data Collector path, and the Terraform module’s own archive table (total_retention_in_days > 0). Runaz monitor log-analytics workspace table migrate. With the Terraform module, first runterraform state rmon the module’s retention-only table resource, or the apply resets the archive retention ofDTMLogs_CLto the workspace’s analytics retention, and import the migrated table afterwards. The steps, in order, are indeploy/terraform/modules/monitoring/README.md; Bicep users run only thetable migratecommand (deploy/bicep/README.md). A deployment that never turns Logs Ingestion on is unchanged. -
Once on, the custom table is protected from destroy. Turning Logs Ingestion off, removing the module or a full
terraform destroythen fails the plan withInstance cannot be destroyed, instead of deletingDTMLogs_CLand its rows. The module’s README gives theterraform state rmstep that keeps the table. -
Rotate the Log Analytics workspace’s shared keys after upgrading. A node record stored by an older version keeps the old key in the node’s database until the next
PUT /api/v1/config/azure-monitor, and a backup taken before then carries it.
v1.0.5 (2026-10-20)
Section titled “v1.0.5 (2026-10-20)”This release includes security improvements; we recommend all customers upgrade.
- Managed script health checks. DTM can now store a script health check’s
script and run it on every node, where before a script check ran a file you
placed on one node, and only on that node. A script version runs only if one
of your keys signed exactly its body: each node checks the signature against
its own
script_signing_keysbefore every run, and never runs a script whose hash is listed in itsscript_revoked_hashes. DTM never holds the private key. Create a key withdtm-server script-key generateand sign a script withdtm-server script-sign. A check pins one version, so publishing a newer version never changes what a check runs. A check that cannot run is refused by name, its backend stays in service, and the newDTMScriptCheckNotRunningalert fires. Script checks still run only on nodes whereallow_script_health_checksis on. In Terraform, the newdtm_scriptresource takes a script and a signature made outside Terraform, anddtm_healthchecktakes ascript_configblock that names a managed script. - New storage gauges:
dtm_storage_zonesanddtm_storage_records(the live zones and records on the node),dtm_storage_tombstones(the delete markers held, by type) anddtm_storage_tombstone_oldest_timestamp_seconds(iftime()minus this passes 30 days, old delete markers are no longer being purged). - A Marketplace node can recover on its own when Microsoft starts signing
its instance attestation with a new issuing certificate, as long as its
network allows outbound HTTP to
www.microsoft.com. A warning in the journal and the counterdtm_entitlement_issuer_fallback_totalshow when this happened, which is the cue to upgrade before you deploy a node with no outbound internet. Without outbound internet the node still refuses to start, as before, and the journal names the certificate it needed, the address it tried and why it failed. - Private Endpoints that report their public name are published. An
endpoint with no private DNS zone group reports its resource’s public name,
such as
myacct.blob.core.windows.net, and DTM now maps it to the matchingprivatelinkzone and record. Endpoints that have a private DNS zone group are read too, from the zone group. When one resource has two endpoints, the record carries both endpoints’ addresses. See Private Endpoints. - A Private Endpoint’s public name resolves to its private address through
DTM. When a forwarded answer’s CNAME chain enters a
privatelinkzone that DTM hosts, the rest of the chain is answered from DTM’s own record, as Azure DNS does for a linked private zone. If DTM hosts the zone but not the name, the public answer goes out unchanged and is counted in the newdtm_dns_privatelink_splice_totalmetric. Setazure.privatelink_splice_enabled: falseto turn this off. - HTTPS health checks can verify a backend by its hostname. The new
tls_server_namefield onhttp_configandhealth_check_template(API, Terraform and the console) sets the name the probe sends as SNI and checks the certificate against, while the probe still connects to the value’s address. A backend whose certificate names its hostname, not its private IP, can now pass. See the upgrade notes for theHostheader. - The templates can grant the stable-IP swap its subnet permission.
grantSubnetJoin(off by default) andsubnetJoinRoleDefinitionId(default Network Contributor) are in every ARM template and Bicep plan, and in a new Upgrades step of the Marketplace wizards. With the grant on, each node’s managed identity gets the role on each cluster subnet outside the deployment resource group, scoped to that subnet alone. - New metric
dtm_cluster_capability_open{capability}: 1 while a behaviour that waits for every node to support it is switched on, 0 while it waits.
Changed
Section titled “Changed”- Zone, record and override changes reach every node within seconds. Before, on larger clusters some nodes could lag until the next background sync. Sign-ins and session refreshes now reach every node within seconds too, so a refresh served by a node other than the one you signed in on no longer signs you out, unless that node could not be reached when you signed in.
- The background sync between nodes runs every 15 seconds by default,
instead of 30. Every other change, such as users, TSIG keys,
health checks and forwarders, now almost always reaches every node within
40 seconds on a cluster of up to 12 nodes. This doubles the sync traffic
between nodes. To keep the old interval, set
push_pull_intervalto30swithPUT /api/v1/config/replication, orreplication.push_pull_interval_secondsto30in the configuration file. - HTTP and HTTPS health checks open a new connection for every probe. A backend that stops accepting new connections, for example behind a new network security group deny rule, a drained listener or a route change, is now marked unhealthy. Before, probes reused one kept-alive connection, which such a change does not close.
- The Entra SSO configuration is cluster-wide. One save, from the Settings
page, the API or Terraform’s
dtm_sso_config, now configures every node. Before, it reached only the node that served it, so rotating the client secret broke sign-in on the other nodes. See the upgrade notes. - Sign in with Microsoft completes on any node of a cluster once every node runs 1.0.5. It used to fail with “invalid or expired state” unless the sign-in started and finished on the same node. A sign-in started on another node’s address is first sent to the redirect URI’s host.
- The Entra client ID must be a UUID, as the tenant ID already had to be. A saved configuration keeps working until it is saved again.
- Changing a forwarder, a stub zone, the default forwarder or the forwarder allowlist takes effect at once. The cache of forwarded answers is now cleared on such a change, where before clients could keep getting the old answers until each cached entry expired (up to an hour).
- Forwarded answers carry only records owned by the queried name, or by a name its CNAMEs reach, from the first answer on. A negative answer through a CNAME now keeps the target zone’s SOA record, so downstream resolvers can cache it, and the upstream’s extended error codes and DNSSEC OK flag reach the client.
- An ALIAS record takes only addresses on its target’s own CNAME chain, at most 8 CNAMEs. A loop or a longer chain answers NODATA.
- An ALIAS record keeps its last good addresses for up to 5 minutes when a refresh times out or gets SERVFAIL, instead of answering NODATA until the next refresh. Health checks keep running on those addresses.
- A region written as an Azure display name matches its location: “West
Europe” now matches
westeuropeeverywhere DTM compares regions, andGET /api/v1/regionslists each region once. The Terraform provider warns at plan time on a region that is not in the canonical form. See the upgrade notes for thedtm_node_infolabel. - The Cluster permissions card checks the subnet permission the stable-IP
swap needs on every subnet a swap from this node can target, including
the subnet of a node that is down but not removed.
GET /api/v1/lifecycle/identityis now admin only. - Tag-based VM registration now covers management-group discovery scopes,
and pauses, rather than removes, the addresses behind a disabled discovery
scope, as region discovery already did. A failed Azure Resource Graph query
now fails the cycle and removes nothing, and
POST /api/v1/tag-registration/runanswers502instead of200with zero counts. - On a partial region discovery cycle, the preview proposes exactly the removals Apply makes.
- A health check refuses two spellings of the same header, such as
Hostandhost. - Template, wizard and Settings page text says replication traffic on the replication port (7946), and the replication encryption key. The Settings page’s Replication Timing fields read Broadcast interval, Broadcast fan-out and Dead-peer send window. Setting names, API fields and resource names are unchanged.
- dtm-server no longer stops while its database grows. A storage metrics sample taken while the database file was growing could stop the process, most often during a restart or a node’s first sync.
dtm_storage_bbolt_pages_in_usereports the pages actually in use. It used to count the whole database file, which grows ahead of the data, so it read high by up to about 2x. See the upgrade notes for the alert this affects.- Removing a node and then stopping it now completes the removal. Before,
the cluster kept counting the removed node: it stayed in the
longest-unreachable-member gauge and its alert, until you repeated the
removal with
?force=true. dtm_dns_dot_listener_upno longer reads 0 on a healthy node after the DNS over TLS listen address is changed.- Audit log entries written in the same instant now list in the order they happened. New entry IDs end in a zero-padded sequence number; existing entries keep their IDs.
- The four-node, two-region Marketplace wizard’s subnet tooltips now name the right nodes: nodes 1 and 2 in the primary region, nodes 3 and 4 in the secondary.
- An A query for a name that holds a CNAME in a DTM zone now gets the target’s address, not just the CNAME. A target inside DTM is answered from DTM’s own data, with its own health and region selection; one outside DTM is resolved through the forwarders when the client asks for recursion. A loop, or a chain of more than 8 CNAMEs, answers SERVFAIL.
- Narrowing
adminAddressPrefixno longer cuts node-to-node 8443, so a node with a corrupt database can confirm a healthy peer and reseed. Every template that has the management deny rule now allows 8443 from the cluster’s own subnets, from the same sources as its replication rule. See the upgrade notes for existing deployments. - A stable-IP swap that fails after the old VM is deallocated puts the stable IP back on the old VM and restarts it, whatever the error, or gives the manual recovery steps when it cannot. A swap is also checked for the subnet permission it needs before it changes anything, and Azure’s “another operation in progress” is retried twice before the swap gives up.
- The Lifecycle page’s deployment plan for a replacement node now keeps the old node’s region, VM size, zone, data disk and network security group sources, reads the replication sources from the old node, deploys into the old node’s subnet, and runs when pasted into zsh.
- One unreadable subscription no longer stops Private Endpoint cleanup in every subscription. A record is now removed when every subscription its addresses came from was read in full.
- A manual region discovery Apply takes effect at once on the node that applied it, without a restart.
- An OAuth2 health check accepts a token response whose
expires_inis a string. - The shipped Grafana dashboard’s per-node and region-discovery panels show data again on any Prometheus.
- An upper-case Entra tenant ID no longer fails sign-in, and Terraform’s
dtm_sso_configrefusesallow_meta_tenant = trueat plan time instead of at apply. - A gallery copy of the Marketplace image keeps its purchase plan on the Terraform and Bicep image paths, so the VM is created and starts. See the upgrade notes.
- A default forwarder configuration saved before the first release is no longer rewritten on every node every 15 seconds.
- On a Terraform deployment, first boot no longer waits indefinitely for a stalled metadata service: the wait is bounded to about three and a half minutes.
- Managed values on records maintained by Private Endpoint automation or tag registration cannot be changed through the records API or JSON import, and editing a managed record keeps them.
Upgrade notes
Section titled “Upgrade notes”- The freelist-bloat alert can fire after you upgrade. Because pages in
use are now counted correctly,
dtm_storage_bbolt_free_page_ratioreads higher for the same database, soDTMBboltFreelistBloat(or the Azure Monitorbbolt-freelist-bloatrule) can fire on a node where it did not before. When it does, more than half of the database really is free pages: compact the node. - While some nodes still run 1.0.4, a user who signs in within 5 minutes of their sessions being ended can be asked to sign in once more at the first refresh. Finish the upgrade and remove any node you have retired. See Known limitations.
- Rolling a node back to 1.0.4 can ask some users to sign in again, at the first refresh of a session that node learned of only after the rollback.
- The 15-second sync interval applies to each node as you upgrade it. A node on 1.0.4 keeps 30 seconds until it is upgraded.
- Before you roll a node back to 1.0.4 with Private Endpoint automation,
turn the automation off on that node, and do not call
POST /api/v1/discovery/private-endpoints/runon it afterwards. An older node removes every record that newer nodes published from public names, and the removal reaches every node. - Public-name and zone-group Private Endpoint records are published once
every node runs 1.0.5. Until then they are held and counted as
heldin the run response. Ifheldstays above zero after the upgrade, a node that is down was never removed: remove it. - When you move to DTM, keep the Azure
privatelinkzones that a private DNS zone group populates. Do not delete or unlink them. - If your VNet is in a resource group other than DTM’s, give each node’s
managed identity Network Contributor, or a role with only
Microsoft.Network/virtualNetworks/subnets/join/action, on the cluster subnet before the first stable-IP swap, or turn ongrantSubnetJoin. Without it, a swap is refused before it changes anything. - A
Hostheader now also sets an HTTPS check’s TLS name whentls_server_nameis not set. A check that sends aHostheader to a backend whose certificate names only its IP now fails verification: give the backend a certificate for that hostname, or remove the header if the backend does not need it. - The
regionlabel ofdtm_node_infouses the canonical form, for examplewesteuropefor a node configured as “West Europe”. Update a dashboard or alert that filters on the old spelling. - On the Terraform or Bicep image path, if your own image carries no
purchase plan, set
image_id_purchase_plan = false(orimageResourceIdPurchasePlan = false) before your next apply. Otherwise the new default adds a plan and the VMs are planned for replacement. - The first Entra SSO save after every node is upgraded replaces every node’s configuration, including any per-node redirect URI. The Settings page says when the nodes’ configurations differ. Until that save, each node keeps its own.
- Tag registration pauses for a disabled scope once every node runs 1.0.5. An older node that runs a cycle during the upgrade still removes those addresses.
- Clusters deployed before 1.0.5 keep their network security groups: an
upgrade adds no 8443 rule. Until you add one, keep
adminAddressPrefixwide enough to cover the node subnets, or add the 8443 rules by hand (see known limitations). Do not redeploy the original plan for this: it replaces the rule list and drops rules added by hand. - A re-applied alerting sample shows an in-place update to the cluster members rule, whose description now says replication traffic.
v1.0.4 (2026-10-14)
Section titled “v1.0.4 (2026-10-14)”This release includes security improvements; we recommend all customers upgrade.
Version 1.0.3 was not published to the Azure Marketplace. 1.0.4 is the first Marketplace release after 1.0.2, and it includes every change made in 1.0.3, listed below.
Breaking changes
Section titled “Breaking changes”- The cluster-traffic source parameter is now a list of extra subnets. The
single-region ARM template’s
gossipSourceAddressPrefixis removed and replaced bygossipSourceAddressPrefixes, a list of CIDRs that is added to the node subnet and never replaces it. In the Terraform single-region sample,gossip_source_address_prefixbecomes the listgossip_source_address_prefixes. In the Bicep node module,gossipSourceAddressPrefixesis renamedgossipSubnetPrefixes, alongside the newgossipPeerPrefixesandgossipExtraPrefixes. Update your parameter files before you redeploy. - ARM templates:
adminUsernamehas no default. A deployment from the Azure CLI or the API must pass it. The Azure portal wizards and the add-node command on the Lifecycle page already do. Bicep and Terraform keep their defaults. - Microsoft Entra ID sign-in needs your directory (tenant) ID. The values
common,organizationsandconsumersare refused, and so isallow_meta_tenantset totrue. A node whose configuration still names one starts with Entra ID sign-in off and logs a warning; local sign-in is unaffected. Settenant_idto your directory ID and removeallow_meta_tenant.GET /api/v1/auth/entra/confignow reportsactive, andinactive_reasonwhen sign-in is off. - A CNAME at the zone apex is refused; use an ALIAS record. The API refuses a new apex CNAME and a rename that moves a CNAME onto the apex, and the importers skip one and name it in the zone’s errors. A CNAME already at the apex keeps serving and can still be edited in place.
- Managed identity health probes need the admin role, and some audiences are
refused. Creating or changing a check, record or override that uses a
managed identity probe now needs the admin role. By default a probe can no
longer request a token for Key Vault, Managed HSM, Resource Manager,
Microsoft Graph, Azure AD Graph or Storage. Your own application’s audience
is unaffected, and
managed_identity_probe_audiencesin the server configuration file sets an exact list instead. - Health checks refuse 168.63.129.16 and link-local addresses
(
169.254.0.0/16andfe80::/10), both when a check is saved and when it probes. DNS forwarding to 168.63.129.16 is unaffected. - Moving a stored health-probe credential needs it entered again. A user
without the admin role who changes where a credential is sent must send the
credential again. That means a check’s protocol, target or OAuth2 token URL,
a record or override template’s protocol, port, token URL or a new value, or
turning on a record’s
auto_populate. Admins are not affected. A dynamic DNS update can no longer add a new value to a record whose template carries a credential or uses a managed identity; re-adding a value the record already holds, such as a DHCP renewal, still works.
- The self-signed certificate for the API, DNS over TLS and DNS over HTTPS
lasts two years and renews itself in its last 30 days, without a restart.
A certificate you uploaded is never replaced. A new metric,
dtm_tls_certificate_expires_in_seconds, and a new alert,DTMTLSCertificateExpiringSoon, warn before a served certificate expires. - A new alert,
DTMReplicationQueueBacklog, fires when a node’s replication queue stays at 5000 messages or more for 15 minutes while it has peers. With this and the certificate alert, the Azure Monitor alerting samples carry 32 rules per node VM. - Limits on concurrent inbound cluster connections: 4 per source address
and 32 in total by default. Set them with
replication.gossip_max_streams_per_sourceandreplication.gossip_max_streams.
Changed
Section titled “Changed”- Every deployment template’s network security group denies the cluster port (7946, TCP and UDP) from the rest of the virtual network. The allow rules name the cluster’s subnets, so a node you add later in the same subnet joins with no rule change. A node in another subnet or region needs that subnet added to the existing nodes’ allow rules. The add-node wizard has a new optional Other cluster subnets field: when you add a node to a dual-region cluster, enter the other region’s subnet there.
- Deleting a node’s VM deletes its data disk on clusters deployed from the Azure portal or the ARM templates. In a cluster of two or more nodes, the data lives on the other nodes. On a single node, deleting the VM deletes its zones and records, so keep backups if you may delete or rebuild it.
/readyzno longer reports a node not ready because its replication queue is deep. It still names the depth, and a node that has fallen behind its peers is still reported not ready.api_rate_limit.trusted_proxiesdefaults to loopback (127.0.0.1/32and::1/128) when the key is absent, so each console browser has its own rate limits.[]trusts nobody, and a list replaces the default; if a load balancer also fronts the API, list it and keep loopback.- Audit entries and sessions record the browser’s address for console
actions, not
127.0.0.1. A direct API caller’s entry keeps its ownhost:port, soip_addressholds either a bare IP orhost:port. - Replacing a failed node with a Lifecycle swap asks which cluster identity
to retire. Such a swap is refused when it is requested, with
retire_candidates; send it again withretire_node_id, or withretire_skipto leave the identity in place. The swap history gains a Cluster identity column. - Session refresh is faster once every node runs 1.0.4. Existing sessions keep working.
- The Terraform provider’s resource and attribute descriptions read as plain documentation.
- A Marketplace node could fail to start after Azure changed the certificate that signs its instance attestation. Fixed; no action needed. The attestation must still chain to the same pinned Microsoft root.
- The web console waits up to 30 seconds for a session refresh, so a slow refresh no longer sends the operator to the sign-in page as easily.
- A script check whose command fails to start reports its own error at once, instead of “runner did not answer in time”.
- Terraform: a
dtm_userupdate no longer showssourceas “(known after apply)”. - The dual-region ARM and Bicep templates, deployed from the Azure CLI, could fail at the VNet peering step when one region’s subnet already existed. The Azure portal wizards create no peerings and were not affected.
- After a large zone was deleted, a node could take much longer than its peers to become ready after a restart.
- A node that missed a node removal now still applies it, even after the node that performed the removal has itself been removed.
Upgrade notes
Section titled “Upgrade notes”- Managed identity checks: on each upgraded node, a check whose audience
is now refused turns unhealthy. Point it at your application’s own audience,
or list the audiences you allow in
managed_identity_probe_audiences. - Health checks aimed at 168.63.129.16 or a link-local address fail on each upgraded node and cannot be saved again. Point them at the backend you meant to monitor.
- Rolling a node back to 1.0.2 after every node runs 1.0.4 can sign out users whose session renewed since then.
- Terraform configurations that still hold the
***placeholder for a probe secret need the real secret applied before a non-admin token can change that check’s destination. - Existing clusters keep their network security groups until you redeploy or edit them.
- Nodes deployed from the 1.0.2 or earlier templates keep their data disk
when the VM is deleted, including when a Lifecycle swap deletes the
retired node’s VM. After such a swap, delete the retired node’s data disk
once you have verified the upgrade. It is named like that node’s VM with
-datain place of-vm(for exampledtm-ha-n1-databesidedtm-ha-n1-vm), and it holds a copy of the cluster’s data and the retired node’s key material. Nodes deployed from the 1.0.4 templates delete it with the VM. - If a host or forwarder pins the self-signed API certificate, fetch it again after it renews. Existing nodes renew their one-year certificate in its last 30 days, once they run 1.0.4.
- To get the two new alerts, redeploy the Azure Monitor alerting sample or reload the Prometheus rules.
v1.0.2 (2026-10-10)
Section titled “v1.0.2 (2026-10-10)”Breaking changes
Section titled “Breaking changes”- Azure Monitor counters are now sent as the increase in each interval, not as a running total. Read them with Sum (Total). A chart or alert that reads a counter with Max now sees per-interval values, and must switch to Sum (Total).
- The alerting samples no longer take a
data_volume_bytesparameter. Their disk rules now compare the free-space ratio. Remove the parameter from your parameter set before you redeploy an existing copy.
- Manage the certificate DTM serves on the API, DNS over TLS and DNS over
HTTPS from Terraform, with the provider’s new
dtm_tls_certificateresource. Its private key is a write-only argument (Terraform 1.11 or later), so it never reaches Terraform state. Destroying the resource removes it from state only: the cluster keeps serving the certificate. - The Terraform provider can trust a private certificate authority. Set
ca_cert_pemto your CA’s PEM, and the provider verifies the API against it instead of the system trust store, even ifinsecure_skip_verifyis also set. - The web console’s self-signed certificate (port 8080) renews itself in the last 30 days before it expires, on deployments from the Azure portal, ARM and Bicep. A certificate you supplied is never replaced.
- On Terraform deployments, the console certificate follows its Key Vault secret. Add a new version of the secret, and every node applies it within about an hour, with no SSH. The default self-signed console certificate renews itself in Key Vault before it expires.
- The Azure Monitor alerting samples now cover every alert rule of the
Prometheus rule set: 30 rules per node VM, up from 16. Two new optional
parameters set the DNS latency thresholds,
dns_latency_p99_threshold_secondsanddns_latency_p95_threshold_seconds.
Changed
Section titled “Changed”- The ARM templates declare each node’s data disk on the VM itself, as Microsoft’s Marketplace guidance requires. A node’s VM and its data disk are always in the same zone.
- A node whose first-boot secrets are not available yet retries about once a minute until it succeeds, and picks up a secret created later with no action on the node. It never stopped after an hour, as earlier documentation said.
- Built with Go 1.26.9 and updated Go networking libraries.
- A DNS listener that went down while the node was starting could be missed
by the readiness check (
/readyz), which then reported the node ready with that transport unavailable.
Upgrade notes
Section titled “Upgrade notes”- Upgrade every node to 1.0.2 before you deploy the 1.0.2 alerting samples. A node on an earlier version can make the stale-lock rule fire continuously.
- Terraform: apply one node at a time. 1.0.2 changes how the module
encodes each node’s custom data, so
terraform planshows every node’s VM replaced. Apply each node with-targetand wait for it to rejoin before the next, as the rolling-upgrade guide describes. A plainterraform applywould replace every node at once. - ARM: do not redeploy a 1.0.2 template over a 1.0.1 deployment. Azure
refuses it (
PropertyChangeNotAllowedon the data disk) and changes nothing. Upgrade existing nodes with the Lifecycle swap.
v1.0.1 (2026-10-08)
Section titled “v1.0.1 (2026-10-08)”The first release of Hayami DTM on the Azure Marketplace.
- Health-checked DNS for private Azure virtual networks. DTM answers authoritatively for your zones on the nodes’ private IP addresses, and removes a backend from its answers when that backend fails its TCP or HTTP(S) health check.
- Record types A, AAAA, CNAME, MX, TXT, NS, PTR, SRV and CAA, plus ALIAS records that point a name at other hostnames and return their healthy addresses.
- Load balancing per record or per zone: round-robin, failover, region-aware failover, latency-based, client affinity, and weighted, which splits traffic between addresses by percentage for canary or blue-green rollouts.
- Region-aware answers: clients are given backends in their own region first, and fail over to another region only when every local backend is unhealthy.
- Clusters of several nodes, in one region or two. The cluster is leaderless: a change made on any node reaches the others, with no central server.
- Conditional forwarding for chosen domains, stub zones, and dynamic DNS updates (RFC 2136) authenticated with TSIG.
- Optional DNS over TLS and DNS over HTTPS.
- A web console and a REST API for zones, records, health checks and settings, with optional Microsoft Entra ID sign-in.
- Metrics for Prometheus, metrics to Azure Monitor, query logs to Log Analytics, and alerting samples for Azure Monitor in Terraform, Bicep and ARM.
- Secure defaults. The administrator password is generated on the node at first boot and kept in a Key Vault the deployment creates; that vault is private by default, with public network access off and the nodes reaching it through a private endpoint. SSH to the nodes is denied from inside the virtual network by default, and accepts keys only.