Skip to content

Cluster and runtime config with Terraform

These singleton resources configure runtime behaviour, and their scope differs: dtm_transport_config and dtm_query_log_config are cluster-wide, so you declare each once and it replicates to every node. The observability (OTLP), rate-limit, cache, and replication-timing singletons are per node: each configures only the node the provider endpoint targets, so apply them against every node’s endpoint for cluster-consistent behaviour. All require the admin role. Applying one never restarts DTM; dtm_replication_config values persist on apply but only take effect at the endpoint node’s next restart (noted below).

dtm_otlp_config turns on DTM’s OTLP/HTTP metrics export to a collector you manage. See observability.

resource "dtm_otlp_config" "this" {
enabled = true
endpoint = "https://otel-collector.internal:4318"
interval_seconds = 30
headers = {
"x-api-key" = var.otlp_api_key # sensitive
}
}
Attribute Description
enabled Default false.
endpoint Collector URL. Required when enabled = true.
headers Export headers (values are secrets, stored encrypted, redacted on read).
interval_seconds Export interval (0 = exporter default).
insecure Default false. Push metrics over plaintext HTTP with no TLS at all (the OTLP-standard “insecure”). Not a skip-cert-verify switch: a self-signed HTTPS collector still needs a trusted certificate. An explicit false clears the flag on the server.

dtm_rate_limit_config sets per-IP API rate limits (token bucket) for all four request classes: auth, write, read, and upload (the restore/compact disk-staging endpoints). A value of 0 uses the built-in default for that class; for upload that is upload_rps of 1 request per 600 seconds and upload_burst of 1. Fields you leave out keep the value the node already holds: the provider reads the current record first and sends only what your configuration sets, so an apply resets nothing it omits.

resource "dtm_rate_limit_config" "this" {
auth_rps = 5
auth_burst = 5
write_rps = 60
write_burst = 20
read_rps = 10
read_burst = 30
}

dtm_cache_config controls the cache for forwarded answers.

resource "dtm_cache_config" "this" {
enabled = true
max_size = 10000
ttl_seconds = 0 # 0 = use each record's own TTL
}

max_size must be greater than 0 when enabled = true.

dtm_delete_guardrail_config caps how many records one bulk delete may remove, the same guardrail the bulk-delete API enforces, and sets the default deletion lock for new zones. It is per node: neither setting replicates, so declare one instance per node endpoint to cover a cluster.

resource "dtm_delete_guardrail_config" "this" {
bulk_delete_cap = 250
}
Attribute Description
bulk_delete_cap Maximum records one bulk delete may remove. Unset uses the built-in default of 100 (on a node where it was never set). The minimum is 1: 0 and negative values are rejected at plan time. Write 100 to pin the default explicitly.
default_zone_deletion_lock Deletion-lock default for zones created through this node. Applies at creation only, never retroactively; an explicit deletion_lock on the create (or dtm_zone.deletion_lock) wins. Default false.

Singleton: import with ID delete-guardrails. Removing the resource resets the cap to the default rather than removing the guardrail, and leaves default_zone_deletion_lock as it is.

dtm_transport_config manages the encrypted inbound DNS listeners, the same setting as the UI’s Transport Protocols card and PUT /api/v1/transport-config. Applies live and replicates cluster-wide; see DNS over TLS and HTTPS for the security caveats before enabling either.

resource "dtm_transport_config" "this" {
dot_enabled = true
dot_listen_addr = ":853"
doh_enabled = false
}

Singleton: import with ID transport-config.

dtm_query_log_config turns per-query logging on and off at runtime, cluster-wide, without a restart. sample_rate keeps 1-in-N queries, worth setting on high-QPS nodes.

resource "dtm_query_log_config" "this" {
enabled = true
sample_rate = 10 # keep 1 in 10 queries
}

Singleton: import with ID query-log. The observability.query_log_* file keys are only the boot seed; once this resource has applied, the persisted value wins over the YAML at the next boot.

The companion singleton dtm_region_discovery_config lives with the other region resources; see regions, discovery and subnets.

dtm_replication_config tunes how quickly cluster state spreads between nodes and how often nodes exchange a full sync. Intervals are duration strings ("30s", "200ms"). Defaults suit every supported deployment: set these only on Hayami support’s advice. The one exception documented elsewhere is keeping the previous 30-second sync interval after an upgrade (push_pull_interval = "30s"); see the release notes.

resource "dtm_replication_config" "this" {
push_pull_interval = "30s"
gossip_interval = "200ms"
gossip_nodes = 3
probe_interval = "1s"
probe_timeout = "500ms"
}

Local users and the Entra SSO configuration are also manageable as code (both admin), with the same secrets-never-in-state discipline as everything else: the credential fields are write-only arguments (Terraform 1.11 or newer), sent to the API on apply and never persisted to state or plan.

dtm_user manages a local user and their role:

resource "dtm_user" "alice" {
username = "alice"
role = "editor" # admin | editor | readonly
enabled = true
password_wo = var.alice_password # sensitive variable
password_wo_version = "1"
}
  • Rotating the password means changing password_wo and bumping password_wo_version together; a write-only value is invisible to the diff, so changing it alone is a no-op.
  • username forces replacement (there is no rename). Import by username (terraform import dtm_user.alice alice), then add the password fields in config to manage the credential.
  • The server-side guards still apply: you cannot disable or demote the last remaining admin, and a user cannot disable their own account.
  • Entra-provisioned users (source = "entra") are not created here; manage their access through the app registration.

dtm_sso_config manages the Entra ID SSO configuration. From v1.0.5 the configuration is cluster-wide once every node runs v1.0.5 (or once the cluster already holds a cluster-wide configuration): declare one instance, apply it through any node’s endpoint, and the save reaches every node. From then on, a destroy turns SSO off on every node.

resource "dtm_sso_config" "entra" {
enabled = true
tenant_id = "00000000-0000-0000-0000-000000000000"
client_id = "11111111-1111-1111-1111-111111111111"
redirect_uri = "https://dtm.example.internal/api/v1/auth/entra/callback"
client_secret_wo = var.entra_client_secret # sensitive variable
client_secret_wo_version = "1"
}

The client secret follows the same write-only pattern: rotate by changing the value and bumping its version together. Role mapping is app-role based (the app registration’s Admin role maps to DTM admin), so there is no claims-mapping argument here. Leave out allow_meta_tenant: it is deprecated and does nothing (meta-tenants are refused since v1.0.4), setting it shows a deprecation warning, and true fails at plan. (Changed in v1.0.5.)

Two data sources expose live cluster state:

# All cluster members
data "dtm_nodes" "all" {}
# One-shot cluster summary
data "dtm_cluster_info" "this" {}
output "alive_nodes" {
value = data.dtm_cluster_info.this.alive_count
}

dtm_nodes returns a nodes list, each with id, name, addr, region_id, status, and version. status is always alive: a member is listed while it is part of the cluster, and one that fails or leaves drops out of the list, so a lost node shows as a smaller list (and a lower alive_count), not as a different status. dtm_cluster_info returns version, node_count, alive_count, node_id, and region_id.