Skip to content

Promote a pilot to production

The pilot worked: four zones, health checks, the failover demo that convinced everyone. Promotion is not “make the pilot bigger”, it is standing up a production cluster born with its own secrets and moving the DNS data across, deliberately leaving behind the pilot’s users, tokens, and, above all, its cluster encryption key. This tutorial is that path.

Terminal window
curl -sk https://<pilot-node>:8443/api/v1/zones/export \
-H "Authorization: Bearer $DTM_TOKEN" -H "X-DTM-CSRF: 1" -o pilot-zones.json

The JSON bundle carries zones, records, LB policies, health-check templates, and region tags; system SOA/NS records re-synthesize on import. It contains no secrets: probe credentials (OAuth2 client secrets, mTLS key material, auth headers) are redacted before serialization. Treat the file as sensitive anyway, because it maps your internal namespace, but it is topology-sensitive, not credential-bearing.

There is a second way to move data between clusters: an offline cross-cluster restore of a full snapshot (snapshots are bound to their origin cluster, so restoring one elsewhere requires an explicit accept-cross-cluster step). It restores the entire local data store, which is precisely the problem: it drags along pilot users, service accounts, and audit history, and the secrets inside it were protected by the pilot’s cluster encryption key, so they do not usably carry anyway and cluster credentials must be re-issued regardless.

For promotion, the bundle wins: DNS data only, clean identity slate, and production’s key is never entangled with the pilot’s. Keep the cross-cluster restore for disaster scenarios, as a deliberate act.

3. Deploy production with its own birth certificate

Section titled “3. Deploy production with its own birth certificate”

Deploy the dual-region production cluster (the Marketplace dual-region plan, or the Terraform path; the dual-region tutorial walks it end to end, prerequisites gate included). The fresh deployment generates its own first-boot secrets, the initial administrator password and the cluster encryption key, into its own Key Vault.

Retrieve the initial admin password with the deployment’s recoverAdminAccessCommand output (retrieveAdminPasswordCommand in the Bicep dialect), change it, and run the production readiness pass now, before the data arrives: named accounts and break-glass admin, Entra SSO with the private-window interlock, scoped NSGs, scheduled backups. Hardening an empty cluster is strictly easier than hardening a serving one.

5. Import the bundle, then re-enter probe credentials

Section titled “5. Import the bundle, then re-enter probe credentials”

Dry-run first, commit second (limits: 20 MB, 50 zones, 50,000 records per zone):

Terminal window
curl -sk -X POST "https://<prod-node>:8443/api/v1/zones/import?dry_run=true" \
-H "Authorization: Bearer $DTM_TOKEN" -H "X-DTM-CSRF: 1" \
-H "Content-Type: application/json" --data-binary @pilot-zones.json
# review, then re-send without dry_run

Verify zones and records arrived (API counts plus a few digs), then do the step the redaction makes mandatory: re-enter every authenticated probe’s credentials (OAuth2 secrets, mTLS material, auth headers) on the production cluster. They did not survive the round trip by design, and without them authenticated probes fail quietly in production.

The bundle moves DNS data; a six-week pilot accumulated more than that. Recreate deliberately, each with its owning page:

Assign the Azure Policy pack in its default DoNotEnforce mode, so the production estate starts life with the audit trail on and the rollout tracker ready for step 8’s waves.

Move client VNets from the pilot node’s IP to the production cluster’s stable IPs, wave by wave, verifying each with dig and the dashboard (local-node-first ordering per the dual-region tutorial).

Let the pilot and production run in parallel until the pilot’s own metrics say it is done: watch the pilot’s query rate (rate(dtm_dns_queries_total[5m]), or the dashboard’s QPS panel) fall to and stay at zero (the raw counter is monotonic and never falls). A pilot still answering queries is a client you forgot.

  • Revoke pilot service accounts (POST /api/v1/service-accounts/{id}/revoke); disable or delete pilot local users. Bearer login tokens need nothing: they are 24-hour self-expiring JWTs.
  • Delete the pilot’s Azure resources (VM, disk, NIC, Key Vault per your retention policy).
  • Mark old pilot snapshots as historical. They remain bound to the pilot cluster, so restoring one into production would require the explicit cross-cluster step, which is exactly the friction you want in front of that mistake.

Production is now serving on its own secrets, its own hardening, and its own evidence trail, and nothing of the pilot survives but the DNS data that earned the promotion.

Need a hand? Email [email protected].

Last validated: 2026-07-26