Skip to content

Apply DNS desired state to a zone

This recipe enforces a defined DNS desired state against an existing zone. You first read the current state, compare it with your target definition, and then perform focused create, update, and delete operations.

There is also a small Bash example script for this workflow: dns-desired-state-sync-example.sh. The script reads the desired state from a JSON file and prints an example of the expected file format when started without arguments.

  • an API key with access to the DNS endpoints
  • an existing zone managed through regfish DNS
  • an internal desired-state definition, for example from Git, Terraform, or your own service config
  • a clear rule that defines which records may be managed automatically and which may not

Every sync starts with a full snapshot of the zone. What matters here is that you capture not only name, type, and data, but also the id so updates and deletes remain unambiguous.

Terminal-Fenster
curl --request GET \
--url 'https://api.regfish.com/dns/example.com/rr' \
--header 'x-api-key: YOUR_API_KEY'

You will typically receive a list of records including their IDs:

{
"success": true,
"response": [
{
"id": 101,
"type": "A",
"name": "api.example.com.",
"data": "203.0.113.10"
},
{
"id": 102,
"type": "CNAME",
"name": "www.example.com.",
"data": "example.com."
}
]
}

Step 2: Define the desired state internally

Section titled “Step 2: Define the desired state internally”

Prepare a target set for the synchronization job. In many environments it comes from a file or deployment system. The important part is that you compare normalized values, for example with FQDNs consistently stored with a trailing dot.

[
{
"type": "A",
"name": "api.example.com.",
"data": "203.0.113.20",
"ttl": 300,
"annotation": "managed-by=dns-desired-state-sync-example"
},
{
"type": "CNAME",
"name": "www.example.com.",
"data": "example.com.",
"ttl": 300,
"annotation": "managed-by=dns-desired-state-sync-example"
},
{
"type": "TXT",
"name": "_service.example.com.",
"data": "managed-by=regfish-api",
"ttl": 300,
"annotation": "managed-by=dns-desired-state-sync-example"
}
]

The script runs in dry-run mode by default and only applies changes when started with APPLY=1:

Terminal-Fenster
curl -fsSLO 'https://www.regfish.de/downloads/docs/dns-desired-state-sync-example.sh'
chmod +x dns-desired-state-sync-example.sh
./dns-desired-state-sync-example.sh
API_KEY=YOUR_API_KEY DOMAIN=example.com ./dns-desired-state-sync-example.sh desired-state.json
APPLY=1 API_KEY=YOUR_API_KEY DOMAIN=example.com ./dns-desired-state-sync-example.sh desired-state.json

Records that exist in the desired state but not in the current state are created from scratch. This is the simplest part of the reconciliation.

Terminal-Fenster
curl --request POST \
--url 'https://api.regfish.com/dns/rr' \
--header 'content-type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '
{
"type": "TXT",
"name": "_service.example.com.",
"data": "managed-by=regfish-api",
"ttl": 300,
"annotation": "managed-by=dns-desired-state-sync-example"
}
'

If a record already exists but data, ttl, or another relevant field has changed, update it by rrid. That avoids ambiguity when similar records exist.

Terminal-Fenster
curl --request PATCH \
--url 'https://api.regfish.com/dns/rr/101' \
--header 'content-type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '
{
"type": "A",
"name": "api",
"data": "203.0.113.20",
"ttl": 300,
"annotation": "synced-by-desired-state-job"
}
'

Anything that still exists in the zone but no longer appears in the desired state is drift and can be removed. This is also the step where you should be strict about only deleting records that your workflow actually owns.

Terminal-Fenster
curl --request DELETE \
--url 'https://api.regfish.com/dns/rr/103' \
--header 'x-api-key: YOUR_API_KEY'
  • compare records by both name and type
  • normalize FQDNs and trailing dots before comparing
  • separate manually maintained records from automated ones through conventions or annotations
  • run a dry-run first and log planned changes before applying them
  • treat deletes more strictly than creates and updates, for example through allowlists

This turns individual DNS endpoints into an idempotent sync workflow. That is what you need to reduce drift and derive repeatable changes from deployments or configuration state.