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.
Prerequisites
Section titled “Prerequisites”- 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
Step 1: Read the current zone state
Section titled “Step 1: Read the current zone state”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.
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:
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.shAPI_KEY=YOUR_API_KEY DOMAIN=example.com ./dns-desired-state-sync-example.sh desired-state.jsonAPPLY=1 API_KEY=YOUR_API_KEY DOMAIN=example.com ./dns-desired-state-sync-example.sh desired-state.jsonStep 3: Create missing records
Section titled “Step 3: Create missing records”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.
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"}'Step 4: Update changed records by RRID
Section titled “Step 4: Update changed records by RRID”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.
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"}'Step 5: Remove obsolete records
Section titled “Step 5: Remove obsolete records”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.
curl --request DELETE \ --url 'https://api.regfish.com/dns/rr/103' \ --header 'x-api-key: YOUR_API_KEY'Practical notes for production workflows
Section titled “Practical notes for production workflows”- compare records by both
nameandtype - 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
Result
Section titled “Result”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.