Skip to content

Concepts

The DNS module models DNS data the same way it's modeled on the wire, with one twist around naming so that international domain names work cleanly with Drupal's URL and storage layers. This page walks through the key concepts site admins need to know to use the module effectively.

Zones and records

A zone is the unit of authority for a domain — for example example.com. In this module, a zone is a content entity of type dns_zone with a name, an owner, and a set of attached records.

A record is a content entity of type dns_record, scoped to a zone, with a type (A, AAAA, …), a prefix (the subdomain part), a TTL, and type-specific data (an IP address, a target hostname, etc.).

Every record belongs to exactly one zone via a required entity reference. Zones contain zero or more records. Deleting a zone is blocked while records exist — the delete form refuses with a count of remaining records and a link back to the zone. Remove the records first, then the zone. (Cascade deletion via an explicit "delete zone and all records" option may land in a later release.)

Zone names: name vs label

DNS zone names can contain non-ASCII characters (internationalized domain names — IDN, e.g. münchen.de). On the wire, DNS itself only carries ASCII, so IDN names are encoded with Punycode (xn--mnchen-3ya.de). The module stores both forms and surfaces them cleanly:

  • name is the canonical lowercase Punycode form. It's the entity's primary key and what shows up in URLs (/dns/xn--mnchen-3ya.de), cache tags, and any place the on-wire form matters.
  • label is the Unicode display form. It's what every page title, link text, and Views column shows by default.

For pure-ASCII zones (example.com), name and label are equal and the distinction doesn't show up in the UI.

When you create a zone, the module accepts either form as input — type münchen.de or paste xn--mnchen-3ya.de, both produce the same canonical pair. The wire form is read-only after first save: the zone name is its identity, so renaming is conceptually delete-and-recreate.

Records and the rdata model

Most record types share a small set of fields and differ only in one or two type-specific values. The module exploits this:

  • A handful of "shared" base fields are real entity fields, queryable and indexable:
  • zone — entity reference to the parent zone.
  • prefix — the subdomain part (e.g. www).
  • record_type — A, AAAA, CNAME, MX, …
  • ttl — time-to-live in seconds.
  • ip_address — the IP for A/AAAA records (using the field_ipaddress module's binary-storage field type).
  • target — the target hostname for CNAME, MX, NS, SRV, PTR, DNAME records. Stored in BIND zone-file form (see below).
  • Type-specific data that doesn't fit a shared field — MX/SRV priority, SRV weight/port, TXT content, CAA flags, etc. — lives in a long-tail rdata JSON field.

Whether a key earns promotion to a real shared field follows a deliberate threshold: it has to be shared by at least two record types and be operationally meaningful to query across types. ip_address and target clear that bar; priority and weight don't.

For site admins this matters because:

  • Standard Views columns / sorts / filters work for the shared fields.
  • For rdata-stored values (e.g. MX priority), a custom Views field handler is provided that reads a single key out of the JSON and renders it as a column. See Customizing the records view.

Record types as plugins

Each record type (A, AAAA, CNAME, NS, MX, TXT, CAA, SRV, PTR, HTTPS, SVCB in core; DNAME, SSHFP, TLSA, SMIMEA, OPENPGPKEY, DS, DNSKEY in the optional dns_extras submodule) is a plugin. The plugin owns:

  • which shared base fields it claims (A and AAAA claim ip_address; CNAME claims target);
  • type-specific validation (e.g. an A record requires an IPv4 address, not just any IP; a CNAME target must be a valid hostname);
  • duplicate-detection rules for that type;
  • how to render the record's value in list contexts.

For site admins this is mostly transparent: in the record form, choose the type from the dropdown and the form re-renders with only the fields that type uses. For developers, see Writing a record type.

Target storage in BIND zone-file form

Targets (the target field on CNAME, MX, NS, PTR, SRV, HTTPS, SVCB in core — and DNAME in the dns_extras submodule) follow RFC 1035 §5.1 BIND zone-file conventions and are stored in that exact form:

  • A name without a trailing dot — www, mail.example.com — is relative to the zone's origin. www in zone example.com resolves to www.example.com..
  • A name ending in .www.example.com. — is an absolute fully-qualified name. The trailing dot is preserved in storage.
  • The literal @ denotes the zone apex itself.

Storage preserves the user's choice rather than canonicalizing to FQDN, which keeps zone-file export a direct write of the field with no per-record context. The record form shows a live preview of the resolved name so you can see exactly what www or @ will mean inside your zone.

Cross-type rules

Some DNS rules apply across record types and are enforced at the entity level rather than within any single plugin. The current set:

  • CNAME exclusivity (RFC 2181 §10.1) — a name with a CNAME cannot have any other record. Adding a CNAME at a name where other records already exist is rejected, and adding any non-CNAME record at a name with an existing CNAME is rejected.

Ownership and access

Zones have a uid (owner) with separate administer / create / view-any / view-own / edit-own / delete-own permissions. Records inherit their access from the parent zone — if you can update the zone, you can manage its records. There are no per-record permissions. See Permissions.

Per-zone delegation is shipped: each zone has a Collaborators tab on its canonical page where the owner (or an admin) can grant a specific capability set to another Drupal user. Capabilities are fine-grained (view records, edit records, etc. — see the permissions page for the full vocabulary). Owner implies all capabilities automatically; admins bypass the system. Provider integrations layer on top via the dns_zone access-policy scope when they ship.

URL structure

Frontend (entity canonicals):

Path What
/dns/{zone} View a zone
/dns/{zone}/edit, /delete Edit / delete the zone
/dns/zones/add Add a zone
/dns/{zone}/records/{record_id} View a record
/dns/{zone}/records/{record_id}/edit Edit a record
/dns/{zone}/records/{record_id}/delete Delete a record
/dns/{zone}/records/add Add a record (zone pre-bound)
/dns/records/add Add a record (no zone pre-bound)

Admin overview:

Path What
/admin/content/dns DNS landing
/admin/content/dns/zones Zone collection
/admin/content/dns/records Records view (Views-backed)
/admin/config/content/dns Module-wide settings