Configuration

Basics

This document picks up where Getting Started and Records leave off, discussing details and less common scenarios.

YamlProvider

octodns.provider.yaml lays out the options for configuring the most commonly used source of record data.

Dynamic Zone Config

In many cases octoDNS’s dynamic zone configuration is the best option for configuring octoDNS to manage your zones. In its simplest form that would look something like:

---
providers:
  config:
    class: octodns.provider.yaml.YamlProvider
    directory: ./config
    default_ttl: 3600
    enforce_order: True
  ns1:
    class: octodns_ns1.Ns1Provider
    api_key: env/NS1_API_KEY
  route53:
    class: octodns_route53.Route53Provider
    access_key_id: env/AWS_ACCESS_KEY_ID
    secret_access_key: env/AWS_SECRET_ACCESS_KEY

zones:
  '*':
    sources:
      - config
    targets:
      - ns1
      - route53

This configuration will query both ns1 and route53 for the list of zones they are managing and dynamically add them to the list being managed using the sources and targets corresponding to the ‘*’ section. See Dynamic Zone Config for details.

Static Zone Config

In cases where fine grained control is desired and the configuration of individual zones varies zones can be an explicit list with each configured zone listed along with its specific setup. As exemplified below alias zones can be useful when two zones are exact copies of each other, with the same configuration and records. YAML anchors are also helpful to avoid duplication where zones share config, but not records.:

---
manager:
  include_meta: True
  max_workers: 2

providers:
  config:
    class: octodns.provider.yaml.YamlProvider
    directory: ./config
    default_ttl: 3600
    enforce_order: True
  ns1:
    class: octodns_ns1.Ns1Provider
    api_key: env/NS1_API_KEY
  route53:
    class: octodns_route53.Route53Provider
    access_key_id: env/AWS_ACCESS_KEY_ID
    secret_access_key: env/AWS_SECRET_ACCESS_KEY

zones:
  example.com.: &dual_target
    sources:
      - config
    targets:
      - ns1
      - route53

  # these have the same setup as example.com., but will have their own files
  # in the configuration directory for records.
  third.tv.: *dual_target
  fourth.tv.: *dual_target

  example.net.:
    # example.net. is an exact copy of example.com., there will not be an
    # example.net.yaml file in the config directory as `alias` includes
    # duplicating the records of the aliased zone along with its config.
    alias: example.com.

  other.com.:
    lenient: True
    sources:
      - config
    targets:
      - ns1

General Configuration Concepts

class is a special key that tells octoDNS what python class should be loaded. Any other keys will be passed as configuration values to that provider. In general any sensitive or frequently rotated values should come from environmental variables. When octoDNS sees a value that starts with env/ it will look for that value in the process’s environment and pass the result along.

Further information can be found in the docstring of each source and provider class.

The include_meta key in the manager section of the config controls the creation of a TXT record at the root of a zone that is managed by octoDNS. If set to True, octoDNS will create a TXT record for the root of the zone with the value provider=<target-provider>. If not specified, the default value for include_meta is False.

The max_workers key in the manager section of the config enables threading to parallelize the planning portion of the sync.

lenient

lenient mostly focuses on the details of Record``s and standards compliance.  When set to ``true octoDNS will allow non-compliant configurations & values where possible. For example CNAME values that don’t end with a ., label length restrictions, and invalid geo codes on dynamic records. When in lenient mode octoDNS will log validation problems at WARNING and try and continue with the configuration or source data as it exists. See Lenience for more information on the concept and how it can be configured. For more targeted control — selectively disabling specific checks or adding custom validation rules — see Validators below.

Validators

octoDNS ships with a suite of built-in validators that check records for correctness (valid TTLs, well-formed values, healthcheck protocol names, etc.) before any changes are applied. The validator system supports: enabling validator sets, adding custom validators, disabling individual validators, and registering validators programmatically from third-party code.

Validator sets and manager.enabled

Validators belong to named sets. manager.enabled controls which sets are active for a run (default: ['legacy']):

manager:
  enabled:
    - legacy

Omitting manager.enabled is equivalent to enabled: [legacy] and preserves the original octoDNS behaviour. The legacy set will remain the default until a future release when strict and best-practice take over as the defaults.

To migrate to stricter validation, replace legacy with strict and add best-practice:

manager:
  enabled:
    - strict
    - best-practice

The strict set contains stricter validators that supersede their legacy counterparts — use one or the other, not both, to avoid redundant checks.

The best-practice set contains validators that enforce DNS best-practice recommendations (rather than RFC requirements) and is independent of strict/legacy. The recommended configuration is to enable both strict and best-practice together.

A validator can belong to multiple sets; it becomes active when any of its sets is listed in manager.enabled.

Setting enabled: [] activates only validators whose sets is None (see Attaching validators programmatically below).

Built-in validator ids

Validators with ids ending in -rfc enforce requirements derived directly from an RFC. Ids prefixed with _ (e.g. _values-type) are internal bridge validators with sets=None — always active and cannot be disabled.

Validators active in both legacy and strict:

id

description

name-rfc

Record name format (RFC 1035/2181)

ttl-rfc

TTL range (positive integer)

healthcheck

octoDNS healthcheck config fields

cname-root-rfc

CNAME must not be at zone root

alias-root

ALIAS must not be at zone root

ip-value-rfc

A / AAAA value format

target-value-rfc

CNAME/ALIAS/DNAME/PTR target format

targets-value-rfc

NS targets format

loc-value-rfc

LOC rdata format (RFC 1876)

chunked-value-rfc

TXT/SPF chunk encoding

svcb-value-rfc

SVCB rdata format (RFC 9460)

https-value-rfc

HTTPS rdata format (RFC 9460)

``openpgpkey-value-rfc``| OPENPGPKEY rdata format (RFC 7929)

dynamic

Dynamic routing config (pools and rules)

urlfwd-value

URLFWD rdata format

Validators active in legacy only (will be superseded in strict):

id

description

geo

Geo routing config

srv-name

SRV name format

uri-name

URI name format

caa-value

CAA rdata format

mx-value

MX rdata format

sshfp-value

SSHFP algorithm/fingerprint format

srv-value

SRV rdata format

uri-value

URI rdata format

naptr-value

NAPTR rdata format

ds-value

DS rdata format

tlsa-value

TLSA rdata format

Validators active in strict only (stricter replacements):

id

description

srv-name-rfc

SRV name strict per RFC 2782 + RFC 6335 §5.1; replaces srv-name

srv-value-rfc

SRV rdata strict per RFC 2782 (range, null target); replaces srv-value

uri-name-rfc

URI name strict per RFC 7553 + RFC 6335 §5.1; replaces uri-name

mx-value-rfc

MX preference in [0, 65535]; null MX rules per RFC 7505; replaces mx-value

caa-value-rfc

CAA flags restricted to 0/128 (RFC 8659 §4.1); tag must match [a-zA-Z0-9]+; replaces caa-value

tlsa-value-rfc

TLSA fields uint8 [0, 255]; certificate_association _data must be hex; SHA-256/SHA-512 length enforced (RFC 6698); replaces tlsa-value

ds-value-rfc

DS key_tag uint16, algorithm/digest_type uint8; digest must be hex; SHA-1/SHA-256/SHA-384 length enforced (RFC 4034/4509/6605); replaces ds-value

naptr-value-rfc

NAPTR order/preference uint16; flags S/A/U/P only; replacement must be FQDN or “.” (RFC 3403); replaces naptr-value

sshfp-value-rfc

SSHFP algorithm/fingerprint_type uint8 [0, 255]; fingerprint must be hex; SHA-1/SHA-256 length enforced (RFC 4255/6594); replaces sshfp-value

uri-value-rfc

URI priority/weight uint16 [0, 65535] (RFC 7553 §4); replaces uri-value

To opt into all strict validators at once:

manager:
  enabled:
    - strict

Validators active in best-practice only:

id

description

target-value-best-practice

CNAME/ALIAS/DNAME target must end with "." (absolute name prevents resolver search-domain append)

targets-value-best-practice

NS/PTR targets must each end with "."

mx-value-best-practice

MX exchange must end with "."

srv-value-best-practice

SRV target must end with "."

svcb-value-best-practice

SVCB targetname must end with "."

https-value-best-practice

HTTPS targetname must end with "."

naptr-value-best-practice

NAPTR replacement must end with "."

The recommended configuration is to enable both sets:

manager:
  enabled:
    - strict
    - best-practice

In a future release strict and best-practice will become the default sets.

Adding validators via config

Custom validators are declared in a top-level validators: section (parallel to providers: and processors:):

validators:
  my-ttl-floor:
    class: mymodule.MinTtlValidator
    min_ttl: 300
    types:
      - MX

The class key specifies the dotted import path of a RecordValidator or ValueValidator subclass. The optional types key restricts the validator to those record types; omitting it registers the validator for all types ('*'). All other keys are passed as keyword arguments to __init__ after the mandatory id (config key) argument — including sets if set-based activation is desired.

Config-declared validators follow the same activation rules as built-in validators: a validator with sets=None (the default) is always active; one with an explicit sets value is activated when any of its sets appears in manager.enabled. manager.validators can still be used to activate a validator for additional record types beyond those listed under types.

Disabling built-in validators

Individual built-in validators can be turned off under manager.disable_validators:

manager:
  disable_validators:
    '*':
      - healthcheck
    MX:
      - mx-value

'*' removes the validator from every record type; a type string removes it only for that type. Bridge validators (_-prefixed ids) cannot be disabled and will raise a config error if listed here.

Attaching validators programmatically

Third-party modules (providers, processors, plugins) can register validators at import time without any config entry. There are two paths depending on whether the validator belongs to a Record subclass / value class or stands on its own.

For a custom Record subclass (or a custom value class), declare a VALIDATORS class attribute. Record.register_type walks the new class’s MRO collecting every VALIDATORS list it finds, plus _value_type.VALIDATORS if defined, and registers each one against the new type:

from octodns.record import Record, ValuesMixin
from octodns.record.validator import RecordValidator, ValueValidator

class FooValueValidator(ValueValidator):
    def validate(self, value_cls, data, _type): ...

class FooValue(str):
    VALIDATORS = [FooValueValidator('foo-value')]
    ...

class NoPublicFooValidator(RecordValidator):
    def validate(self, record_cls, name, fqdn, data): ...

class FooRecord(ValuesMixin, Record):
    _type = 'FOO'
    _value_type = FooValue
    VALIDATORS = [NoPublicFooValidator('no-public-foo')]

Record.register_type(FooRecord)

To attach a validator to an already-registered record type, call Record.register_validator directly:

from octodns.record import Record
from octodns.record.validator import RecordValidator

class NoPublicMxValidator(RecordValidator):
    def validate(self, record_cls, name, fqdn, data): ...

Record.register_validator(NoPublicMxValidator('no-public-mx'), types=['MX'])

types=None (the default) registers for all record types.

Set membership and activation. A validator’s sets attribute controls when it becomes active. The default is sets=None, which means the validator is always activated regardless of manager.enabled — the right choice for most third-party validators that should always run. To opt a validator into set-based filtering, pass an explicit sets at construction time:

Record.register_validator(
    StrictMxValidator('strict-mx', sets={'rfc'}), types=['MX']
)

That validator is then only active when manager.enabled includes 'rfc'. As long as the module is imported before Manager initialises, the validator will be in the available registry and activated appropriately.

strict_supports

strict_supports is a Provider level parameter that comes into play when a provider has been asked to create a record that it is unable to support. The simplest case of this would be record type, e.g. SSHFP not being supported by AzureProvider. If such a record is passed to an AzureProvider as a target the provider will take action based on the strict_supports. When true it will throw an exception saying that it’s unable to create the record, when set to false it will log at WARNING with information about what it’s unable to do and how it is attempting to work around it. Other examples of things that cannot be supported would be dynamic records on a provider that only supports simple or the lack of support for specific geos in a provider, e.g. Route53Provider does not support NA-CA-*.

It is worth noting that these errors will happen during the plan phase of things so that problems will be visible without having to make changes.

As of octoDNS 1.x strict_supports is on by default. You have the choice to set strict_supports=false on a per provider basis to request that things warn and continue in a best-effort fashion.

Configuring strict_supports

The strict_supports parameter is available on all providers and can be configured in YAML as follows:

providers:
  someprovider:
    class: whatever.TheProvider
    ...
    strict_supports: true

Automatic PTR generation

octoDNS supports automatically generating PTR records from the A/AAAA records it manages. For more information see the Automatic PTR Generation documentation.

JSON Schema for zone YAML files

octoDNS publishes a JSON Schema (Draft 2020-12) describing its zone YAML file format. It is generated from the currently registered record types on every docs build, so the schema matches the code in that release.

The schema is intended for editors and CI linters. octoDNS’s own validation is unchanged — it continues to handle error reporting with source context.

Available versions

Read the Docs serves a copy of the schema under each version’s _static directory:

Note

The legacy filename octodns.schema.json is still published alongside octodns-zone.schema.json with identical content. Existing modelines or yaml.schemas configs pointing at the old name keep working.

Opting in with a modeline

The yaml-language-server (used by the redhat.vscode-yaml extension and other editors) honors a modeline at the top of a YAML file:

# yaml-language-server: $schema=https://octodns.readthedocs.io/en/stable/_static/octodns-zone.schema.json
---
www:
  type: A
  ttl: 300
  value: 1.2.3.4

Editor configuration

In VS Code (or any editor that uses yaml-language-server) you can instead associate the schema with a file pattern via yaml.schemas:

"yaml.schemas": {
  "https://octodns.readthedocs.io/en/stable/_static/octodns-zone.schema.json": [
    "zones/*.yaml"
  ]
}

Generating locally

The octodns-schema CLI prints or writes the same schema:

octodns-schema --output octodns-zone.schema.json

JSON Schema for the main config file

octoDNS also publishes a JSON Schema describing the main config.yaml format: providers, processors, validators, secret_handlers, and zones. Built-in core classes (YamlProvider, OwnershipProcessor, etc.) get full property schemas; third-party plugin classes are accepted with any kwargs as long as class is present.

Available versions

Opting in with a modeline

# yaml-language-server: $schema=https://octodns.readthedocs.io/en/stable/_static/octodns-config.schema.json
---
providers:
  config:
    class: octodns.provider.yaml.YamlProvider
    directory: ./zones

Editor configuration

"yaml.schemas": {
  "https://octodns.readthedocs.io/en/stable/_static/octodns-config.schema.json": [
    "config.yaml"
  ]
}

Generating locally

octodns-schema --kind config --output octodns-config.schema.json