Back to Articles

DNS Zone Files and JSON: Building Bidirectional Parsers with dns-zonefile

[ View on GitHub ]

DNS Zone Files and JSON: Building Bidirectional Parsers with dns-zonefile

Hook

DNS zone files haven't changed since RFC1035 was published in 1987, yet most modern infrastructure tools speak JSON. The gap between these formats is where dns-zonefile operates—and where automation becomes possible.

Context

Managing DNS infrastructure programmatically has always been awkward. Zone files are human-readable text files with a specific format that DNS servers understand, but they're painful to generate or modify with code. You can't easily version control incremental changes, extract specific records for validation, or integrate DNS configuration into modern CI/CD pipelines without parsing that archaic syntax.

Most developers resort to string templating or brittle regex patterns when they need to generate zone files programmatically. Parsing existing zone files is even worse—handling comments, whitespace variations, TTL inheritance, and the quirks of different record types creates a maintenance nightmare. This is where elgs/dns-zonefile comes in: it's a focused library that transforms DNS zone files into JSON objects and back again, making DNS configuration just another data structure you can manipulate with standard JavaScript tools.

Technical Insight

The library exposes two primary functions that mirror each other. The generate() function takes a JSON object and produces RFC1035-compliant zone file text, while parse() does the inverse. This bidirectional design means you can round-trip zone files through your application without data loss.

Here's a practical example of generating a zone file:

const zonefile = require('dns-zonefile');

const zoneData = {
  "$origin": "example.com.",
  "$ttl": 3600,
  "soa": {
    "mname": "ns1.example.com.",
    "rname": "admin.example.com.",
    "serial": "{time}",
    "refresh": 3600,
    "retry": 600,
    "expire": 604800,
    "minimum": 86400
  },
  "ns": [
    { "host": "ns1.example.com." },
    { "host": "ns2.example.com." }
  ],
  "a": [
    { "name": "@", "ip": "192.0.2.1" },
    { "name": "www", "ip": "192.0.2.2" },
    { "name": "mail", "ip": "192.0.2.3", "ttl": 7200 }
  ],
  "mx": [
    { "preference": 10, "host": "mail.example.com." }
  ]
};

const output = zonefile.generate(zoneData);
console.log(output);

This generates a properly formatted zone file with all the syntactic details handled for you—the trailing dots on FQDNs, spacing alignment, and record ordering. The {time} placeholder is particularly clever: it gets replaced with a Unix timestamp, giving you an automatically incrementing serial number for SOA records without manual tracking.

The parsing direction works just as smoothly. Feed it an existing zone file and you get back a structured JSON object:

const zoneText = `
$ORIGIN example.com.
$TTL 3600
@   IN  SOA ns1.example.com. admin.example.com. (
            2024010101 ; serial
            3600       ; refresh
            600        ; retry
            604800     ; expire
            86400 )    ; minimum

    IN  NS  ns1.example.com.
    IN  NS  ns2.example.com.
    IN  A   192.0.2.1
www IN  A   192.0.2.2
`;

const parsed = zonefile.parse(zoneText);
console.log(JSON.stringify(parsed, null, 2));

The architecture is straightforward: the parser uses string manipulation and regex patterns to tokenize zone file syntax into JavaScript objects, while the generator uses template functions to convert objects back into formatted text. It handles TTL inheritance (where records without explicit TTLs use the $TTL directive), class fields (defaulting to IN), and the various syntactic shortcuts DNS administrators use.

What makes this library particularly useful is its support for record-specific fields. CAA records get their flags, tag, and value properties. SRV records properly parse priority, weight, port, and target. DS records include key tag, algorithm, digest type, and digest fields. This attention to detail means you're not dealing with generic key-value pairs—you're working with properly typed data structures that map to actual DNS semantics.

The cross-platform design is another architectural win. The library works in Node.js, Deno, and browser environments without modification. There's also a CLI tool included, so you can use it for one-off conversions in shell scripts:

# Convert JSON to zone file
node cli.js generate input.json > output.zone

# Parse zone file to JSON
node cli.js parse input.zone > output.json

This makes dns-zonefile useful not just as a library dependency but as a standalone tool in your DNS management workflow. You can commit JSON representations of your zones to Git, review changes in pull requests with familiar diff tools, then generate the actual zone files during deployment.

Gotcha

The library's simplicity is both its strength and limitation. It doesn't support all DNS record types—notably missing are DNSSEC records like RRSIG, NSEC, and DNSKEY (though DS records are supported). If you're managing a DNSSEC-signed zone with inline signing, you'll need to handle those records separately or use zone manipulation tools from BIND itself.

Advanced zone file features like $INCLUDE directives and $GENERATE statements aren't supported either. These directives let you compose zones from multiple files or generate repetitive records programmatically—features that larger DNS installations rely on. The library also doesn't appear to validate your data before generating zone files. If you pass invalid IP addresses, malformed hostnames, or out-of-range numeric values, you'll get syntactically correct but semantically broken zone files. There's no mention in the documentation of validation, error recovery, or how the parser handles malformed input. For production use, you'd want to add your own validation layer on top.

Verdict

Use if: You need to programmatically generate or manipulate standard DNS zone files from JavaScript/TypeScript code, want to version control DNS configurations as JSON rather than raw zone files, or are building automation around DNS management that needs to parse existing zones. It's particularly well-suited for API integrations where you're consuming or producing zone data, or for CI/CD pipelines that need to validate and deploy DNS changes. Skip if: You're working with DNSSEC-signed zones that require comprehensive record type support, need advanced zone file features like $INCLUDE or $GENERATE, or require extensive validation and error handling for untrusted input. Also skip if your DNS infrastructure is complex enough to benefit from a full infrastructure-as-code tool like DNSControl, which provides provider abstractions and change management on top of basic zone file manipulation.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/elgs-dns-zonefile.svg)](https://starlog.is/api/badge-click/developer-tools/elgs-dns-zonefile)